From 892a0f93c902842a9ac028cf15fda55a8f2042ce Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Tue, 4 Aug 2026 18:21:25 +0800 Subject: [PATCH 001/167] Document workflow activity vNext baseline --- apps/aevatar-console-web/AGENTS.md | 31 + .../workflow-activity-vnext/README.md | 195 + ...aevatar-workflow-activity-vnext.excalidraw | 56083 ++++++++++++++++ .../aevatar-workflow-activity-vnext.gen.py | 1212 + .../prototype-activity.png | Bin 0 -> 143323 bytes .../prototype-create-blank.png | Bin 0 -> 49457 bytes .../prototype-create-describe.png | Bin 0 -> 81550 bytes .../prototype-create-import.png | Bin 0 -> 67719 bytes .../prototype-create-mobile.png | Bin 0 -> 27974 bytes .../prototype-create-template.png | Bin 0 -> 71513 bytes .../prototype-editor-canvas.png | Bin 0 -> 75131 bytes .../prototype-editor-mobile-node.png | Bin 0 -> 45181 bytes .../prototype-editor-node.png | Bin 0 -> 103470 bytes .../prototype-editor-yaml.png | Bin 0 -> 106924 bytes .../prototype-mobile.png | Bin 0 -> 68896 bytes .../prototype-node-library.png | Bin 0 -> 69052 bytes .../prototype-settings-account.png | Bin 0 -> 61391 bytes .../prototype-settings-advanced.png | Bin 0 -> 50785 bytes .../prototype-settings-llm.png | Bin 0 -> 49304 bytes .../prototype-settings-mobile.png | Bin 0 -> 36258 bytes .../prototype-settings-save.png | Bin 0 -> 62526 bytes .../prototype-settings-tablet.png | Bin 0 -> 39954 bytes .../prototype-workflows.png | Bin 0 -> 103216 bytes .../workflow-activity-vnext/prototype.html | 1824 + ...26-08-04-workflow-activity-vnext-design.md | 915 + 25 files changed, 60260 insertions(+) create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.gen.py create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-activity.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-blank.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-describe.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-import.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-mobile.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-template.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-canvas.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-mobile-node.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-node.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-yaml.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-mobile.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-node-library.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-account.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-advanced.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-llm.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-mobile.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-save.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-tablet.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-workflows.png create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype.html create mode 100644 apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md diff --git a/apps/aevatar-console-web/AGENTS.md b/apps/aevatar-console-web/AGENTS.md index c4085866da..4397e59fbb 100644 --- a/apps/aevatar-console-web/AGENTS.md +++ b/apps/aevatar-console-web/AGENTS.md @@ -133,6 +133,37 @@ pnpm --dir apps/aevatar-console-web build - Do not add or preserve hidden `/teams/:scopeId...` compatibility routes. Parse paths by resource name, not by fragile segment indexes. +## Workflow Activity vNext Baseline + +- Before changing any route, page, component, hook, query, adapter, model, + style, locale, or test for + `/scopes/:scopeId/workflow-activity-vnext`, read both + `docs/design-baselines/workflow-activity-vnext/README.md` and + `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md` + completely. +- Treat + `docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw` + as the primary visual, information-architecture, and interaction reference. + Treat the design specification as the normative route, product identity, + API, state, and backend-compatibility contract. +- The PNG and HTML prototypes are reference artifacts, not runtime data + sources. Never copy their hard-coded records, `localStorage` persistence, + timers, simulated receipts, or successful-looking defaults into production + code. +- Production remote state must come from real API responses or real user + actions acknowledged by those APIs. When an API is pending, empty, + unavailable, delayed, or failed, render that exact state; do not insert mock + or fixture fallback data. +- Keep mock and fixture data in clearly named test-only files. Production + routes, components, hooks, queries, and API adapters must not import them. +- Include the design-baseline declaration from + `docs/design-baselines/workflow-activity-vnext/README.md` in every vNext + implementation task and pull request so the Excalidraw hash, contract + document, and real-API-only data-source rule are reviewable. +- Keep this feature frontend-only and isolated to its new route namespace. + Do not change backend code or alter existing Workflow, Run, Settings, Studio, + Team, member, redirect, or menu behavior to implement it. + ## UI and Interaction - For page, component, console, playground, or visual-polish work, follow diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md new file mode 100644 index 0000000000..fb300d8c64 --- /dev/null +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md @@ -0,0 +1,195 @@ +# Workflow + Activity + Settings vNext + +## Repository Implementation Baseline + +Status: **Normative for the Workflow Activity vNext frontend**. + +Any implementation or review of routes below +`/scopes/:scopeId/workflow-activity-vnext` must read this directory together +with +[`2026-08-04-workflow-activity-vnext-design.md`](../../superpowers/specs/2026-08-04-workflow-activity-vnext-design.md). + +Use the sources in this order: + +1. `aevatar-workflow-activity-vnext.excalidraw` is the primary visual, + information-architecture, and interaction reference. +2. The design specification is the normative product, route, identity, API, + state, and backend-compatibility contract. +3. The PNG files are viewport references for individual states. +4. `prototype.html` is an interaction demonstration only. +5. `aevatar-workflow-activity-vnext.gen.py` is the source generator for these + reference artifacts; it is not application runtime code. + +If the Excalidraw or prototype conflicts with a real API contract, follow the +design specification's backend-honest behavior. Do not change the backend and +do not fabricate data to close the gap. + +## Production Data Truth Rule + +The production frontend must never make sample, fixture, generated, cached, or +hard-coded data look as though it came from an API. + +- API-owned Workflows, Runs, Run details, settings, identities, receipts, + statuses, timestamps, revisions, usage, lineage, and availability must come + from real API responses or real user actions acknowledged by those APIs. +- While a request is pending or unavailable, render the specified loading, + empty, delayed, unavailable, or error state. Never substitute demonstration + rows or a successful-looking default response. +- An empty state is allowed only after a successful authoritative query returns + no records. A request failure is an error state, not an empty state. +- Browser storage must not act as an Activity, Workflow, account, or settings + backend. Existing session helpers may retain non-authoritative request + recovery facts only when the design specification explicitly permits it. +- Mock and fixture data are allowed only inside clearly named automated test + fixtures or isolated test harnesses. They must not be imported by production + route, component, hook, query, or API-adapter code. +- Versioned, bundled Workflow templates are frontend product content, not API + data. If used, label and model them explicitly as bundled templates; never + present them as a server-returned template catalogue. + +`prototype.html` intentionally contains hard-coded demonstration records, +`localStorage`, and timers so the standalone prototype can be exercised +without a server. Those mechanisms have no API or persistence authority and +must not be copied into the production implementation. + +## Required Development And Review Declaration + +Every implementation task and pull request for this vNext route should include +this declaration, updated only when the named baseline changes: + +```text +Design baseline: + apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/ +Primary design: + aevatar-workflow-activity-vnext.excalidraw +Design SHA-256: + 30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de +Contract specification: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-design.md +Production data source: + Real APIs and API-acknowledged user actions only; no mock fallback. +``` + +This declaration makes design drift and mock-data substitution explicit review +failures rather than undocumented implementation choices. + +## Imported Source + +这个目录是根据 2026-08-03 会议结论重新整理的独立版本。`/Users/xiezixin/Downloads/mocks/` 下的原始画板和生成器没有修改。 + +## 文件 + +- `aevatar-workflow-activity-vnext.excalidraw`:合并后的 Excalidraw,包含 Workflows、Activity 与 Settings,共 17 个 frame。 +- `aevatar-workflow-activity-vnext.gen.py`:画板生成器,内含画框边界、ID 唯一性和废弃术语检查。 +- `prototype.html`:可直接在浏览器打开的交互原型,不需要安装依赖或启动服务。 +- `prototype-workflows.png`:Workflows 桌面视图截图。 +- `prototype-activity.png`:Activity 桌面视图截图。 +- `prototype-mobile.png`:Activity 移动视图截图。 +- `prototype-create-describe.png`:通过描述生成的 Workflow 草稿。 +- `prototype-create-blank.png`:空白 Workflow 草稿。 +- `prototype-create-import.png`:从 YAML 导入的 Workflow 草稿。 +- `prototype-create-template.png`:从模板创建的 Workflow 草稿。 +- `prototype-editor-canvas.png`:真实 Aevatar Workflow Studio 结构的默认节点画布。 +- `prototype-editor-node.png`:点击节点后打开的 Node configuration 侧面板。 +- `prototype-node-library.png`:从左侧打开并可搜索的 Node library。 +- `prototype-editor-yaml.png`:Workflow YAML 侧面板。 +- `prototype-create-mobile.png`:移动端 Workflow 画布,保留 Run、Add node、Edit YAML、Save 和 Publish。 +- `prototype-editor-mobile-node.png`:移动端点击节点后的底部配置面板。 +- `prototype-settings-llm.png`:重构后的 AI defaults 桌面视图。 +- `prototype-settings-save.png`:产生修改后才出现的 sticky save bar 与保存确认状态。 +- `prototype-settings-account.png`:Account 身份、会话与 service access 桌面视图。 +- `prototype-settings-advanced.png`:独立的只读 Runtime 与 request values 视图。 +- `prototype-settings-tablet.png`:Settings 的 768px 布局。 +- `prototype-settings-mobile.png`:Settings 的 390px 移动布局。 + +## Workflow Studio 参照 + +本轮没有继续设计新的 Workflow 编辑器,而是以真实 Aevatar 项目现有的 Workflow Studio 为来源: + +- `apps/aevatar-console-web/src/pages/team-member-workflow-studio/index.tsx` +- `components/WorkflowStudioCanvas.tsx` +- `components/WorkflowStudioNodeLibrary.tsx` +- `components/WorkflowStudioNodeDetailPanel.tsx` +- `components/WorkflowStudioYamlPanel.tsx` +- `src/shared/graphs/GraphCanvas.tsx` + +原型因此统一采用“节点画布 + 连线 + 按需侧面板”的编辑模式。Describe、Start blank、Import YAML 和 Template 只负责产生不同的初始 Workflow 文档,不再各自发明编辑行为。 + +## 新语义 + +- 用户从 Workflows 直接创建 Workflow 草稿,没有其他资源的创建前置步骤。 +- `Run` 是唯一的常规执行入口。 +- 每次 Run 一开始就创建 Activity 记录,包括当前草稿修订的执行。 +- Activity 记录明确显示 `Current draft · revision N` 或 `Published · vN`。 +- Retry 和 Run again 会创建新记录并链接来源 Run;原记录不被覆盖。 +- Activity 菜单是唯一的 Run 历史入口;需要查看某个 Workflow 时,在 Activity 中应用 Workflow 筛选。 +- 全局导航包含 Workflows、Activity 和 Settings;Settings 是明确新增的配置区域,不改变 Workflow / Activity 的执行语义。 + +## Excalidraw 阅读顺序 + +导入 `aevatar-workflow-activity-vnext.excalidraw` 后,建议先缩放到全部内容,再按 frame 名称从 01 看到 17: + +1. `01 Workflows - catalogue`:日常入口、搜索、筛选、Run 和 Open。 +2. `02 New workflow - direct creation`:四种入口都直接创建 Workflow 草稿。 +3. `03 Describe - generated Workflow draft`:描述生成匹配的节点与连线;点击节点后从右侧配置。 +4. `04 Start blank - empty Workflow draft`:真实 Studio 的空画布;添加第一个节点之前不能 Publish 或 Run。 +5. `05 Import YAML - imported Workflow draft`:YAML 中的名称和节点类型进入同一套 Studio 画布。 +6. `06 Template - populated Workflow draft`:模板创建独立草稿,并以连接节点显示。 +7. `07 Run - unified execution dialog`:确认修订、输入、连接和外部影响;底部说明本次 Run 会保存在 Activity。 +8. `08 Running draft - Studio canvas and Run console`:仍在同一个节点画布中显示运行状态,底部打开 Run console;Activity 记录已经存在。 +9. `09 Activity - filtered by Workflow`:从 Activity 菜单进入,并用 Workflow 筛选历史。 +10. `10 Activity - all retained Runs`:全局、最新优先的所有 Run 记录。 +11. `11 Run detail - immutable record`:Run 的修订、来源、耗时、用量、步骤时间线与关联记录。 +12. `12 Failed Run - recovery creates a new record`:失败解释和恢复;Retry 预览明确展示新旧 Run 的关系。 +13. `13 Workflows and Activity - states`:空、加载、错误和移动端信息优先级。 +14. `14 Settings - AI defaults`:正常态只保留 Preferred service 与 Default model 两个决策。 +15. `15 Settings - save and recovery states`:dirty save bar、accepted observation、fallback、catalog unavailable 和保存失败恢复。 +16. `16 Settings - Account`:Profile、claims、Authentication、Sign out 与 Manage service access。 +17. `17 Settings - Advanced and responsive`:单份只读 effective request values 与完整移动端操作布局。 + +重点不是逐个查看控件,而是沿着这条主路径检查语义是否连贯: + +```text +Workflows + -> New workflow + -> direct draft creation + -> edit connected nodes in Workflow Studio + -> Run + -> Activity record created immediately + -> Run detail + -> Retry / Run again creates a linked record +``` + +## 原型操作路径 + +1. 打开 `prototype.html`,默认进入 Workflows。 +2. 点击 `New workflow`,分别检查四种方式:描述会生成匹配节点;空白会进入画布空状态;导入会先校验 YAML;模板会先要求选择模板。 +3. 在画布点击节点,右侧打开 Node configuration;`Add node` 从左侧打开 Node library;`Edit YAML` 从右侧打开 YAML 面板。 +4. 在列表或编辑器点击 `Run`,确认修订、输入、连接和外部影响。 +5. 勾选外部影响确认后点击 `Run`。记录会立即写入浏览器 `localStorage`,编辑器显示运行状态。 +6. 打开 Activity,点击任意一行查看详情。 +7. 对成功记录点击 `Run again`,或对失败记录点击 `Retry`,然后在确认框里创建新的关联记录。 +8. 点击 Settings,在 `AI defaults` 中切换 exact service 和 model。修改后页面底部才会出现 Discard / Save changes;再分别检查 `Account` 和只读的 `Advanced`。 + +原型数据会保留在当前浏览器中;重新打开页面后,新建的 Run 历史仍然存在。 + +四种创建方式共享同一个 Workflow Studio。画布节点、连线、节点库、节点配置、YAML 和 Run 入口保持一致;变化的只是各自创建出来的 Workflow 文档。 + +## Settings 参照 + +Settings 的功能来源仍然是当前 Aevatar Console,但信息架构已经重新设计: + +- `apps/aevatar-console-web/src/pages/settings/index.tsx` +- `apps/aevatar-console-web/src/pages/settings/accountContent.tsx` +- `apps/aevatar-console-web/src/pages/settings/shared.tsx` +- `apps/aevatar-console-web/src/pages/settings/userLlmSelection.ts` +- `apps/aevatar-console-web/src/pages/settings/userLlmSaveObservation.ts` + +重构后分成三个任务分区: + +- `AI defaults`:正常态只保留 Preferred service 与 Default model。适用范围已由页面说明表达,不再重复展示 provider inventory、route note 或保存成功状态。 +- `Account`:当前浏览器身份、User ID、roles、groups、会话到期时间、NyxID provider、scope、Sign in / Sign out 和 Manage service access;字段本身足够明确,不再重复配小节说明。 +- `Advanced`:只保留一份 effective request values。Runtime mode 与 base URL 不再同时出现在只读输入和 raw values 两处。 + +AI 默认值会保存在浏览器 `localStorage`。选择 exact connected service 时,模型列表会跟着服务切换。默认状态保持安静;只有产生修改后,sticky save bar 才出现。保存动作先进入 accepted / confirming saved values,再更新已保存 service。fallback、catalog unavailable 与 provider unavailable 只在异常状态出现。Account 的 Sign in、Sign out 与 service access 也有可见状态变化。 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw new file mode 100644 index 0000000000..e2e2970bdf --- /dev/null +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw @@ -0,0 +1,56083 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "t-00001-3326", + "type": "text", + "x": 160.0, + "y": 80.0, + "width": 1040.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 517879736, + "version": 1, + "versionNonce": 1140406283, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Aevatar Workflow + Activity + Settings vNext", + "originalText": "Aevatar Workflow + Activity + Settings vNext", + "fontSize": 32, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00002-6887", + "type": "text", + "x": 160.0, + "y": 132.0, + "width": 980.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 136598626, + "version": 1, + "versionNonce": 1723159338, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "One authoring resource. One Run action. One immutable Activity history.", + "originalText": "One authoring resource. One Run action. One immutable Activity history.", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00003-5943", + "type": "text", + "x": 160.0, + "y": 180.0, + "width": 1180.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1624035407, + "version": 1, + "versionNonce": 1408473053, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Primary path: Workflows -> direct draft -> Run -> Activity. Settings owns LLM defaults and browser identity.", + "originalText": "Primary path: Workflows -> direct draft -> Run -> Activity. Settings owns LLM defaults and browser identity.", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00004-7124", + "type": "frame", + "x": 160.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1523166750, + "version": 1, + "versionNonce": 1950145098, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "01 Workflows - catalogue" + }, + { + "id": "r-00006-5560", + "type": "rectangle", + "x": 160.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 394840533, + "version": 1, + "versionNonce": 152575559, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00007-3352", + "type": "rectangle", + "x": 160.0, + "y": 300.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 434787233, + "version": 1, + "versionNonce": 1859222484, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00008-7098", + "type": "text", + "x": 184.0, + "y": 324.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1253083477, + "version": 1, + "versionNonce": 1309538728, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00009-1297", + "type": "text", + "x": 184.0, + "y": 353.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1978300590, + "version": 1, + "versionNonce": 1205786937, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00010-2781", + "type": "rectangle", + "x": 172.0, + "y": 408.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1417836623, + "version": 1, + "versionNonce": 911048851, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00011-8651", + "type": "ellipse", + "x": 188.0, + "y": 420.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1142714299, + "version": 1, + "versionNonce": 496004083, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00012-1990", + "type": "text", + "x": 208.0, + "y": 416.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1632424781, + "version": 1, + "versionNonce": 262264550, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00013-2195", + "type": "ellipse", + "x": 188.0, + "y": 472.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2028662266, + "version": 1, + "versionNonce": 2030844556, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00014-1660", + "type": "text", + "x": 208.0, + "y": 468.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 929715049, + "version": 1, + "versionNonce": 848751520, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00015-9816", + "type": "ellipse", + "x": 188.0, + "y": 524.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1461285563, + "version": 1, + "versionNonce": 1425986971, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00016-9057", + "type": "text", + "x": 208.0, + "y": 520.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 350304066, + "version": 1, + "versionNonce": 1128414647, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00017-2005", + "type": "text", + "x": 184.0, + "y": 1146.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1683337894, + "version": 1, + "versionNonce": 1229776163, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00018-4250", + "type": "text", + "x": 184.0, + "y": 1172.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1935916886, + "version": 1, + "versionNonce": 156418574, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00019-5100", + "type": "rectangle", + "x": 370.0, + "y": 300.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1858501624, + "version": 1, + "versionNonce": 158400455, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00020-2160", + "type": "text", + "x": 400.0, + "y": 318.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 296691921, + "version": 1, + "versionNonce": 1547591908, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00021-9093", + "type": "text", + "x": 400.0, + "y": 349.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 168862334, + "version": 1, + "versionNonce": 870295758, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Create, edit, publish, and run automations.", + "originalText": "Create, edit, publish, and run automations.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00022-7329", + "type": "rectangle", + "x": 1426.0, + "y": 322.0, + "width": 144.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 914473273, + "version": 1, + "versionNonce": 1176119067, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00023-9539", + "type": "text", + "x": 1434.0, + "y": 331.0, + "width": 128.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1666935788, + "version": 1, + "versionNonce": 1115263221, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "New workflow", + "originalText": "New workflow", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00024-2248", + "type": "text", + "x": 400.0, + "y": 408.0, + "width": 47.879999999999995, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1095350280, + "version": 1, + "versionNonce": 632134050, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SEARCH", + "originalText": "SEARCH", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00025-8832", + "type": "rectangle", + "x": 400.0, + "y": 430.0, + "width": 370.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 893915208, + "version": 1, + "versionNonce": 1698291170, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00026-1189", + "type": "text", + "x": 412.0, + "y": 441.0, + "width": 346.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 509302994, + "version": 1, + "versionNonce": 153046552, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Search workflows", + "originalText": "Search workflows", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00027-8862", + "type": "rectangle", + "x": 796.0, + "y": 430.0, + "width": 73.19999999999999, + "height": 30.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 439540426, + "version": 1, + "versionNonce": 1530202018, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00028-3166", + "type": "text", + "x": 806.0, + "y": 437.0, + "width": 53.19999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1150199719, + "version": 1, + "versionNonce": 1848512635, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "All 5", + "originalText": "All 5", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00029-1910", + "type": "rectangle", + "x": 877.2, + "y": 430.0, + "width": 122.39999999999999, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 542608803, + "version": 1, + "versionNonce": 162002017, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00030-6870", + "type": "text", + "x": 887.2, + "y": 437.0, + "width": 102.39999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1022683653, + "version": 1, + "versionNonce": 802919697, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published 3", + "originalText": "Published 3", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00031-8180", + "type": "rectangle", + "x": 1007.6, + "y": 430.0, + "width": 97.8, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1922336479, + "version": 1, + "versionNonce": 1843337531, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00032-9060", + "type": "text", + "x": 1017.6, + "y": 437.0, + "width": 77.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1427342885, + "version": 1, + "versionNonce": 325237124, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Drafts 2", + "originalText": "Drafts 2", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00033-2798", + "type": "rectangle", + "x": 1113.4, + "y": 430.0, + "width": 106.0, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 153056693, + "version": 1, + "versionNonce": 1980469233, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00034-4906", + "type": "text", + "x": 1123.4, + "y": 437.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1160327554, + "version": 1, + "versionNonce": 224712208, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failing 1", + "originalText": "Failing 1", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00035-4670", + "type": "rectangle", + "x": 400.0, + "y": 494.0, + "width": 1140.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1400816048, + "version": 1, + "versionNonce": 384436219, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00036-4263", + "type": "text", + "x": 414.0, + "y": 507.0, + "width": 382.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1116377547, + "version": 1, + "versionNonce": 1134186838, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WORKFLOW", + "originalText": "WORKFLOW", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00037-2390", + "type": "text", + "x": 824.0, + "y": 507.0, + "width": 142.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 976958319, + "version": 1, + "versionNonce": 774781362, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "VERSION", + "originalText": "VERSION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00038-5174", + "type": "text", + "x": 994.0, + "y": 507.0, + "width": 192.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 709871509, + "version": 1, + "versionNonce": 1203098037, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "LAST RUN", + "originalText": "LAST RUN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00039-9382", + "type": "text", + "x": 1214.0, + "y": 507.0, + "width": 122.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 547366311, + "version": 1, + "versionNonce": 773412357, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "STATE", + "originalText": "STATE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00040-8651", + "type": "text", + "x": 1364.0, + "y": 507.0, + "width": 162.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 342502192, + "version": 1, + "versionNonce": 414243307, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ACTIONS", + "originalText": "ACTIONS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00041-4947", + "type": "rectangle", + "x": 400.0, + "y": 536.0, + "width": 1140.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1829966595, + "version": 1, + "versionNonce": 1639408864, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00042-9407", + "type": "text", + "x": 416.0, + "y": 551.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 531086355, + "version": 1, + "versionNonce": 728213774, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00043-9476", + "type": "text", + "x": 416.0, + "y": 579.0, + "width": 375.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 959723470, + "version": 1, + "versionNonce": 2100303493, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Group feedback and post a weekly summary", + "originalText": "Group feedback and post a weekly summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00044-1086", + "type": "text", + "x": 824.0, + "y": 564.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1794480028, + "version": 1, + "versionNonce": 1277864077, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v7", + "originalText": "v7", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00045-1593", + "type": "rectangle", + "x": 994.0, + "y": 562.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1027195755, + "version": 1, + "versionNonce": 521505030, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00046-3005", + "type": "text", + "x": 1004.0, + "y": 568.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1392785678, + "version": 1, + "versionNonce": 93968000, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00047-6901", + "type": "text", + "x": 1095.0, + "y": 567.0, + "width": 95.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2136670573, + "version": 1, + "versionNonce": 184513834, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "12 min ago", + "originalText": "12 min ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00048-9230", + "type": "rectangle", + "x": 1214.0, + "y": 562.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 938012587, + "version": 1, + "versionNonce": 1064478078, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00049-3164", + "type": "text", + "x": 1224.0, + "y": 568.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1491285223, + "version": 1, + "versionNonce": 1032348848, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00050-8049", + "type": "rectangle", + "x": 1362.0, + "y": 558.0, + "width": 72.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 2147267446, + "version": 1, + "versionNonce": 970649033, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00051-3932", + "type": "text", + "x": 1370.0, + "y": 567.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 254043338, + "version": 1, + "versionNonce": 1541489872, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00052-5029", + "type": "rectangle", + "x": 1444.0, + "y": 558.0, + "width": 76.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 245702973, + "version": 1, + "versionNonce": 1336067291, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00053-8965", + "type": "text", + "x": 1452.0, + "y": 567.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1489094443, + "version": 1, + "versionNonce": 1261217495, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open", + "originalText": "Open", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00054-2498", + "type": "rectangle", + "x": 400.0, + "y": 618.0, + "width": 1140.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 551689451, + "version": 1, + "versionNonce": 786771351, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00055-3366", + "type": "text", + "x": 416.0, + "y": 633.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 443971897, + "version": 1, + "versionNonce": 1527317609, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00056-3411", + "type": "text", + "x": 416.0, + "y": 661.0, + "width": 375.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1254559369, + "version": 1, + "versionNonce": 265118115, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Classify urgent conversations and prepare replies", + "originalText": "Classify urgent conversations and prepare replies", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00057-5110", + "type": "text", + "x": 824.0, + "y": 646.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 583736651, + "version": 1, + "versionNonce": 743125275, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "draft r12", + "originalText": "draft r12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00058-2865", + "type": "rectangle", + "x": 994.0, + "y": 644.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1577742005, + "version": 1, + "versionNonce": 1127063890, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00059-4708", + "type": "text", + "x": 1004.0, + "y": 650.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1289614278, + "version": 1, + "versionNonce": 641504127, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00060-4360", + "type": "text", + "x": 1095.0, + "y": 649.0, + "width": 95.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1725414202, + "version": 1, + "versionNonce": 353367026, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "4 min ago", + "originalText": "4 min ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00061-6433", + "type": "rectangle", + "x": 1214.0, + "y": 644.0, + "width": 68.0, + "height": 28.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1829439043, + "version": 1, + "versionNonce": 75887096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00062-2061", + "type": "text", + "x": 1224.0, + "y": 650.0, + "width": 48.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1657812163, + "version": 1, + "versionNonce": 529458988, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft", + "originalText": "Draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00063-8956", + "type": "rectangle", + "x": 1362.0, + "y": 640.0, + "width": 72.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1746409496, + "version": 1, + "versionNonce": 612545763, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00064-2124", + "type": "text", + "x": 1370.0, + "y": 649.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1496654549, + "version": 1, + "versionNonce": 220596962, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00065-6167", + "type": "rectangle", + "x": 1444.0, + "y": 640.0, + "width": 76.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1435326479, + "version": 1, + "versionNonce": 1348202668, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00066-9872", + "type": "text", + "x": 1452.0, + "y": 649.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1995297981, + "version": 1, + "versionNonce": 701113068, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open", + "originalText": "Open", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00067-9422", + "type": "rectangle", + "x": 400.0, + "y": 700.0, + "width": 1140.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2066972868, + "version": 1, + "versionNonce": 259401317, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00068-2590", + "type": "text", + "x": 416.0, + "y": 715.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1166141698, + "version": 1, + "versionNonce": 1455402352, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Invoice follow-up", + "originalText": "Invoice follow-up", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00069-4586", + "type": "text", + "x": 416.0, + "y": 743.0, + "width": 375.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 198744090, + "version": 1, + "versionNonce": 2102087459, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Prepare overdue invoice reminders for approval", + "originalText": "Prepare overdue invoice reminders for approval", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00070-7065", + "type": "text", + "x": 824.0, + "y": 728.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1354105043, + "version": 1, + "versionNonce": 1597624414, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v4", + "originalText": "v4", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00071-2288", + "type": "rectangle", + "x": 994.0, + "y": 726.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 127652592, + "version": 1, + "versionNonce": 639577668, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00072-1557", + "type": "text", + "x": 1004.0, + "y": 732.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 890573175, + "version": 1, + "versionNonce": 472078081, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00073-2727", + "type": "text", + "x": 1095.0, + "y": 731.0, + "width": 95.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2080967663, + "version": 1, + "versionNonce": 1760835508, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "42 min ago", + "originalText": "42 min ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00074-1406", + "type": "rectangle", + "x": 1214.0, + "y": 726.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1120520558, + "version": 1, + "versionNonce": 1243720374, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00075-2823", + "type": "text", + "x": 1224.0, + "y": 732.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 669922466, + "version": 1, + "versionNonce": 220568617, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00076-8974", + "type": "rectangle", + "x": 1362.0, + "y": 722.0, + "width": 72.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1481854150, + "version": 1, + "versionNonce": 488509408, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00077-7932", + "type": "text", + "x": 1370.0, + "y": 731.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2017699140, + "version": 1, + "versionNonce": 1511799295, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00078-4618", + "type": "rectangle", + "x": 1444.0, + "y": 722.0, + "width": 76.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1808069825, + "version": 1, + "versionNonce": 2116434306, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00079-7720", + "type": "text", + "x": 1452.0, + "y": 731.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2100865816, + "version": 1, + "versionNonce": 874847285, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open", + "originalText": "Open", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00080-5498", + "type": "rectangle", + "x": 400.0, + "y": 782.0, + "width": 1140.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1203534089, + "version": 1, + "versionNonce": 4317943, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00081-2236", + "type": "text", + "x": 416.0, + "y": 797.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1874053866, + "version": 1, + "versionNonce": 2122597077, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Nightly order sync", + "originalText": "Nightly order sync", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00082-1213", + "type": "text", + "x": 416.0, + "y": 825.0, + "width": 375.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 139278044, + "version": 1, + "versionNonce": 1283560239, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Move completed orders into the warehouse", + "originalText": "Move completed orders into the warehouse", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00083-8550", + "type": "text", + "x": 824.0, + "y": 810.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1573087529, + "version": 1, + "versionNonce": 904050530, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v9", + "originalText": "v9", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00084-7944", + "type": "rectangle", + "x": 994.0, + "y": 808.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1847437444, + "version": 1, + "versionNonce": 279061107, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00085-4926", + "type": "text", + "x": 1004.0, + "y": 814.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2135054138, + "version": 1, + "versionNonce": 1496640391, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00086-7750", + "type": "text", + "x": 1095.0, + "y": 813.0, + "width": 95.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 725281272, + "version": 1, + "versionNonce": 1058058823, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "3:04 AM", + "originalText": "3:04 AM", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00087-6522", + "type": "rectangle", + "x": 1214.0, + "y": 808.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1170988998, + "version": 1, + "versionNonce": 661371309, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00088-6806", + "type": "text", + "x": 1224.0, + "y": 814.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1309975006, + "version": 1, + "versionNonce": 1192931498, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00089-7616", + "type": "rectangle", + "x": 1362.0, + "y": 804.0, + "width": 72.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 539512094, + "version": 1, + "versionNonce": 754513799, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00090-6668", + "type": "text", + "x": 1370.0, + "y": 813.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2035422174, + "version": 1, + "versionNonce": 1332733812, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00091-3991", + "type": "rectangle", + "x": 1444.0, + "y": 804.0, + "width": 76.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 202131799, + "version": 1, + "versionNonce": 1773749398, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00092-7259", + "type": "text", + "x": 1452.0, + "y": 813.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2098368041, + "version": 1, + "versionNonce": 96493610, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open", + "originalText": "Open", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00093-3499", + "type": "rectangle", + "x": 400.0, + "y": 864.0, + "width": 1140.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1828061198, + "version": 1, + "versionNonce": 36226060, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00094-2084", + "type": "text", + "x": 416.0, + "y": 879.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 2063029164, + "version": 1, + "versionNonce": 228672405, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Contract review", + "originalText": "Contract review", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00095-4799", + "type": "text", + "x": 416.0, + "y": 907.0, + "width": 375.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1965387534, + "version": 1, + "versionNonce": 161320734, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Flag unusual clauses and prepare a review note", + "originalText": "Flag unusual clauses and prepare a review note", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00096-1101", + "type": "text", + "x": 824.0, + "y": 892.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 198895438, + "version": 1, + "versionNonce": 397435370, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "draft r3", + "originalText": "draft r3", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00097-5249", + "type": "rectangle", + "x": 994.0, + "y": 890.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 2030035673, + "version": 1, + "versionNonce": 818673406, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00098-5172", + "type": "text", + "x": 1004.0, + "y": 896.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 539368758, + "version": 1, + "versionNonce": 1133632936, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Never run", + "originalText": "Never run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00099-1407", + "type": "text", + "x": 1095.0, + "y": 895.0, + "width": 95.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1013210926, + "version": 1, + "versionNonce": 152046176, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00100-1161", + "type": "rectangle", + "x": 1214.0, + "y": 890.0, + "width": 68.0, + "height": 28.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1606034548, + "version": 1, + "versionNonce": 537233589, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00101-7564", + "type": "text", + "x": 1224.0, + "y": 896.0, + "width": 48.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1427963066, + "version": 1, + "versionNonce": 1834163296, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft", + "originalText": "Draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00102-8642", + "type": "rectangle", + "x": 1362.0, + "y": 886.0, + "width": 72.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1503420307, + "version": 1, + "versionNonce": 1336382264, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00103-7771", + "type": "text", + "x": 1370.0, + "y": 895.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 392173482, + "version": 1, + "versionNonce": 664176744, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00104-2946", + "type": "rectangle", + "x": 1444.0, + "y": 886.0, + "width": 76.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": { + "type": 3 + }, + "seed": 1343311786, + "version": 1, + "versionNonce": 529652258, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00105-9298", + "type": "text", + "x": 1452.0, + "y": 895.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 1423319694, + "version": 1, + "versionNonce": 1366635492, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open", + "originalText": "Open", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00106-7546", + "type": "text", + "x": 400.0, + "y": 1166.0, + "width": 760.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-00" + ], + "frameId": "frame-00004-7124", + "roundness": null, + "seed": 419971090, + "version": 1, + "versionNonce": 1051660726, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "5 workflows | Every Run, including a draft revision, is retained in Activity.", + "originalText": "5 workflows | Every Run, including a draft revision, is retained in Activity.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00107-3590", + "type": "frame", + "x": 1920.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1047681375, + "version": 1, + "versionNonce": 267910118, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "02 New workflow - direct creation" + }, + { + "id": "r-00109-5643", + "type": "rectangle", + "x": 1920.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 2078501908, + "version": 1, + "versionNonce": 45018908, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00110-8148", + "type": "rectangle", + "x": 1920.0, + "y": 300.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1657807126, + "version": 1, + "versionNonce": 838548178, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00111-2082", + "type": "text", + "x": 1944.0, + "y": 324.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 562029954, + "version": 1, + "versionNonce": 738084348, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00112-7991", + "type": "text", + "x": 1944.0, + "y": 353.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1026264163, + "version": 1, + "versionNonce": 1226451813, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00113-7097", + "type": "rectangle", + "x": 1932.0, + "y": 408.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1105378700, + "version": 1, + "versionNonce": 1413894393, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00114-8958", + "type": "ellipse", + "x": 1948.0, + "y": 420.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1185473168, + "version": 1, + "versionNonce": 904185499, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00115-2046", + "type": "text", + "x": 1968.0, + "y": 416.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 607984635, + "version": 1, + "versionNonce": 1051729033, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00116-1448", + "type": "ellipse", + "x": 1948.0, + "y": 472.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 104652390, + "version": 1, + "versionNonce": 506763862, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00117-7946", + "type": "text", + "x": 1968.0, + "y": 468.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1492942084, + "version": 1, + "versionNonce": 1273932104, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00118-9479", + "type": "ellipse", + "x": 1948.0, + "y": 524.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1994607107, + "version": 1, + "versionNonce": 284610339, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00119-4312", + "type": "text", + "x": 1968.0, + "y": 520.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 778736701, + "version": 1, + "versionNonce": 1652364614, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00120-1861", + "type": "text", + "x": 1944.0, + "y": 1146.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 960574767, + "version": 1, + "versionNonce": 1840481642, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00121-7062", + "type": "text", + "x": 1944.0, + "y": 1172.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1410835928, + "version": 1, + "versionNonce": 2110584509, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00122-5016", + "type": "rectangle", + "x": 2130.0, + "y": 300.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 2077632551, + "version": 1, + "versionNonce": 1648183085, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00123-1011", + "type": "text", + "x": 2160.0, + "y": 318.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 2019191807, + "version": 1, + "versionNonce": 899677294, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "New workflow", + "originalText": "New workflow", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00124-6566", + "type": "text", + "x": 2160.0, + "y": 349.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 333240417, + "version": 1, + "versionNonce": 62887508, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Choose a starting point. Each option creates a workflow draft directly.", + "originalText": "Choose a starting point. Each option creates a workflow draft directly.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00125-5720", + "type": "rectangle", + "x": 3242.0, + "y": 322.0, + "width": 88.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1048525063, + "version": 1, + "versionNonce": 1909292836, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00126-7564", + "type": "text", + "x": 3250.0, + "y": 331.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 87149875, + "version": 1, + "versionNonce": 529363887, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Cancel", + "originalText": "Cancel", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00127-5056", + "type": "rectangle", + "x": 2160.0, + "y": 424.0, + "width": 362.0, + "height": 180.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 7660660, + "version": 1, + "versionNonce": 1459646542, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00128-9451", + "type": "text", + "x": 2182.0, + "y": 448.0, + "width": 318.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 576999098, + "version": 1, + "versionNonce": 1818273304, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Describe what you need", + "originalText": "Describe what you need", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00129-7500", + "type": "text", + "x": 2182.0, + "y": 488.0, + "width": 310.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 334533754, + "version": 1, + "versionNonce": 1734837888, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Turn a plain-language goal into a reviewable first draft.", + "originalText": "Turn a plain-language goal into a reviewable first draft.", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00130-1854", + "type": "rectangle", + "x": 2182.0, + "y": 548.0, + "width": 130.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1067996867, + "version": 1, + "versionNonce": 570154809, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00131-4505", + "type": "text", + "x": 2190.0, + "y": 557.0, + "width": 114.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1449787118, + "version": 1, + "versionNonce": 1448760770, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Describe", + "originalText": "Describe", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00132-4674", + "type": "rectangle", + "x": 2550.0, + "y": 424.0, + "width": 362.0, + "height": 180.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1885854156, + "version": 1, + "versionNonce": 1426784786, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00133-1177", + "type": "text", + "x": 2572.0, + "y": 448.0, + "width": 318.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 124382589, + "version": 1, + "versionNonce": 1657275537, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Start blank", + "originalText": "Start blank", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00134-6756", + "type": "text", + "x": 2572.0, + "y": 488.0, + "width": 310.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1744679103, + "version": 1, + "versionNonce": 345652397, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open an empty workflow canvas and add the first node.", + "originalText": "Open an empty workflow canvas and add the first node.", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00135-7802", + "type": "rectangle", + "x": 2572.0, + "y": 548.0, + "width": 130.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 859114451, + "version": 1, + "versionNonce": 865092965, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00136-6134", + "type": "text", + "x": 2580.0, + "y": 557.0, + "width": 114.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1996490564, + "version": 1, + "versionNonce": 1668102960, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Start blank", + "originalText": "Start blank", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00137-6248", + "type": "rectangle", + "x": 2940.0, + "y": 424.0, + "width": 362.0, + "height": 180.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 780101623, + "version": 1, + "versionNonce": 953776377, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00138-2179", + "type": "text", + "x": 2962.0, + "y": 448.0, + "width": 318.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1460198614, + "version": 1, + "versionNonce": 1060399244, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Import YAML", + "originalText": "Import YAML", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00139-6881", + "type": "text", + "x": 2962.0, + "y": 488.0, + "width": 310.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 2119391740, + "version": 1, + "versionNonce": 1875548861, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Create a draft from an existing workflow definition.", + "originalText": "Create a draft from an existing workflow definition.", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00140-6073", + "type": "rectangle", + "x": 2962.0, + "y": 548.0, + "width": 130.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 2035943306, + "version": 1, + "versionNonce": 1220851826, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00141-1476", + "type": "text", + "x": 2970.0, + "y": 557.0, + "width": 114.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1435557830, + "version": 1, + "versionNonce": 1488630631, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Import", + "originalText": "Import", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00142-3165", + "type": "text", + "x": 2160.0, + "y": 650.0, + "width": 180.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 503932428, + "version": 1, + "versionNonce": 1864255781, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TEMPLATES", + "originalText": "TEMPLATES", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00143-5238", + "type": "rectangle", + "x": 2160.0, + "y": 688.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 100021162, + "version": 1, + "versionNonce": 241295709, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00144-8338", + "type": "text", + "x": 2178.0, + "y": 706.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1410640628, + "version": 1, + "versionNonce": 1651414920, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00145-4297", + "type": "text", + "x": 2178.0, + "y": 736.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 629775828, + "version": 1, + "versionNonce": 798050102, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "6 steps | Lark", + "originalText": "6 steps | Lark", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00146-7118", + "type": "text", + "x": 2178.0, + "y": 766.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1921310982, + "version": 1, + "versionNonce": 1499046985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 240 times", + "originalText": "Used 240 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00147-7826", + "type": "rectangle", + "x": 2378.0, + "y": 780.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 42454932, + "version": 1, + "versionNonce": 959259493, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00148-6104", + "type": "text", + "x": 2386.0, + "y": 789.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 73401437, + "version": 1, + "versionNonce": 58509815, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00149-7001", + "type": "rectangle", + "x": 2550.0, + "y": 688.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1371654517, + "version": 1, + "versionNonce": 961234423, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00150-2238", + "type": "text", + "x": 2568.0, + "y": 706.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 631776827, + "version": 1, + "versionNonce": 1990256290, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Escalation triage", + "originalText": "Escalation triage", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00151-4579", + "type": "text", + "x": 2568.0, + "y": 736.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1813641465, + "version": 1, + "versionNonce": 1881968262, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "9 steps | Lark + Linear", + "originalText": "9 steps | Lark + Linear", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00152-3948", + "type": "text", + "x": 2568.0, + "y": 766.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 973746391, + "version": 1, + "versionNonce": 852102617, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 180 times", + "originalText": "Used 180 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00153-6588", + "type": "rectangle", + "x": 2768.0, + "y": 780.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1374100357, + "version": 1, + "versionNonce": 945340954, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00154-9950", + "type": "text", + "x": 2776.0, + "y": 789.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 657624235, + "version": 1, + "versionNonce": 1206950047, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00155-2548", + "type": "rectangle", + "x": 2940.0, + "y": 688.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 999527852, + "version": 1, + "versionNonce": 562099883, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00156-7676", + "type": "text", + "x": 2958.0, + "y": 706.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 517729393, + "version": 1, + "versionNonce": 1620646105, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Invoice follow-up", + "originalText": "Invoice follow-up", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00157-4559", + "type": "text", + "x": 2958.0, + "y": 736.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1902934931, + "version": 1, + "versionNonce": 705221614, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "11 steps | Xero + Gmail", + "originalText": "11 steps | Xero + Gmail", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00158-4687", + "type": "text", + "x": 2958.0, + "y": 766.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 579590472, + "version": 1, + "versionNonce": 1880288115, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 96 times", + "originalText": "Used 96 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00159-4268", + "type": "rectangle", + "x": 3158.0, + "y": 780.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1359438051, + "version": 1, + "versionNonce": 797431452, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00160-4988", + "type": "text", + "x": 3166.0, + "y": 789.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 2115112707, + "version": 1, + "versionNonce": 818885024, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00161-3244", + "type": "rectangle", + "x": 2160.0, + "y": 860.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 462843401, + "version": 1, + "versionNonce": 1612310543, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00162-2355", + "type": "text", + "x": 2178.0, + "y": 878.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 44600397, + "version": 1, + "versionNonce": 1300051572, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Standup digest", + "originalText": "Standup digest", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00163-7859", + "type": "text", + "x": 2178.0, + "y": 908.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 448747415, + "version": 1, + "versionNonce": 910774201, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "5 steps | Lark", + "originalText": "5 steps | Lark", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00164-3980", + "type": "text", + "x": 2178.0, + "y": 938.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1576393772, + "version": 1, + "versionNonce": 1673400139, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 88 times", + "originalText": "Used 88 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00165-1679", + "type": "rectangle", + "x": 2378.0, + "y": 952.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1681655430, + "version": 1, + "versionNonce": 1368236930, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00166-1199", + "type": "text", + "x": 2386.0, + "y": 961.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 993231635, + "version": 1, + "versionNonce": 1976862746, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00167-2742", + "type": "rectangle", + "x": 2550.0, + "y": 860.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 520777183, + "version": 1, + "versionNonce": 908378459, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00168-3379", + "type": "text", + "x": 2568.0, + "y": 878.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 816295054, + "version": 1, + "versionNonce": 1308337046, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "On-call summary", + "originalText": "On-call summary", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00169-8555", + "type": "text", + "x": 2568.0, + "y": 908.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 327595501, + "version": 1, + "versionNonce": 1964822773, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "5 steps | PagerDuty", + "originalText": "5 steps | PagerDuty", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00170-6269", + "type": "text", + "x": 2568.0, + "y": 938.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1393681104, + "version": 1, + "versionNonce": 1899536926, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 61 times", + "originalText": "Used 61 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00171-9949", + "type": "rectangle", + "x": 2768.0, + "y": 952.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1690553178, + "version": 1, + "versionNonce": 1275066386, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00172-3321", + "type": "text", + "x": 2776.0, + "y": 961.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 513460525, + "version": 1, + "versionNonce": 144696948, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00173-9909", + "type": "rectangle", + "x": 2940.0, + "y": 860.0, + "width": 362.0, + "height": 148.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1931629847, + "version": 1, + "versionNonce": 1912899231, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00174-9115", + "type": "text", + "x": 2958.0, + "y": 878.0, + "width": 320.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1831064348, + "version": 1, + "versionNonce": 1311845583, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Contract review", + "originalText": "Contract review", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00175-1870", + "type": "text", + "x": 2958.0, + "y": 908.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1337663713, + "version": 1, + "versionNonce": 17683050, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "7 steps | Drive", + "originalText": "7 steps | Drive", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00176-9476", + "type": "text", + "x": 2958.0, + "y": 938.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1893189053, + "version": 1, + "versionNonce": 1865536677, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Used 44 times", + "originalText": "Used 44 times", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00177-4899", + "type": "rectangle", + "x": 3158.0, + "y": 952.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": { + "type": 3 + }, + "seed": 1263821278, + "version": 1, + "versionNonce": 1768657285, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00178-2998", + "type": "text", + "x": 3166.0, + "y": 961.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-01" + ], + "frameId": "frame-00107-3590", + "roundness": null, + "seed": 1577025033, + "version": 1, + "versionNonce": 1030317630, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Use template", + "originalText": "Use template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00179-4597", + "type": "frame", + "x": 3680.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 941152409, + "version": 1, + "versionNonce": 126634037, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "03 Describe - generated Workflow draft" + }, + { + "id": "r-00181-6614", + "type": "rectangle", + "x": 3680.0, + "y": 300.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 619292691, + "version": 1, + "versionNonce": 1791971550, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00182-7935", + "type": "rectangle", + "x": 3680.0, + "y": 300.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 510935215, + "version": 1, + "versionNonce": 558760984, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00183-1303", + "type": "text", + "x": 3704.0, + "y": 324.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1382309349, + "version": 1, + "versionNonce": 161947161, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00184-1645", + "type": "text", + "x": 3704.0, + "y": 353.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 511242457, + "version": 1, + "versionNonce": 2116271982, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00185-1779", + "type": "rectangle", + "x": 3692.0, + "y": 408.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1826137216, + "version": 1, + "versionNonce": 934124905, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00186-1949", + "type": "ellipse", + "x": 3708.0, + "y": 420.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1494420084, + "version": 1, + "versionNonce": 1336410523, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00187-9795", + "type": "text", + "x": 3728.0, + "y": 416.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1185508963, + "version": 1, + "versionNonce": 677139188, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00188-1023", + "type": "ellipse", + "x": 3708.0, + "y": 472.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 238905058, + "version": 1, + "versionNonce": 1664959803, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00189-5299", + "type": "text", + "x": 3728.0, + "y": 468.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2065615911, + "version": 1, + "versionNonce": 1662655822, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00190-7032", + "type": "ellipse", + "x": 3708.0, + "y": 524.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2060407546, + "version": 1, + "versionNonce": 1468435342, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00191-4129", + "type": "text", + "x": 3728.0, + "y": 520.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 315570214, + "version": 1, + "versionNonce": 531161442, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00192-3271", + "type": "text", + "x": 3704.0, + "y": 1146.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 849471132, + "version": 1, + "versionNonce": 1511307963, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00193-5367", + "type": "text", + "x": 3704.0, + "y": 1172.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 74852097, + "version": 1, + "versionNonce": 1576716008, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00194-2574", + "type": "rectangle", + "x": 3890.0, + "y": 300.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 373612242, + "version": 1, + "versionNonce": 1483147711, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00195-2218", + "type": "text", + "x": 3920.0, + "y": 318.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 927006300, + "version": 1, + "versionNonce": 645785096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Monday feedback summary", + "originalText": "Monday feedback summary", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00196-8625", + "type": "text", + "x": 3920.0, + "y": 349.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1870486891, + "version": 1, + "versionNonce": 1171057319, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "originalText": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00197-7800", + "type": "rectangle", + "x": 4610.0, + "y": 324.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 767683730, + "version": 1, + "versionNonce": 1365705744, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00198-8892", + "type": "text", + "x": 4618.0, + "y": 333.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 750143146, + "version": 1, + "versionNonce": 2094369600, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00199-7387", + "type": "rectangle", + "x": 4692.0, + "y": 324.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1154249968, + "version": 1, + "versionNonce": 1075623388, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00200-2223", + "type": "text", + "x": 4700.0, + "y": 333.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 825736954, + "version": 1, + "versionNonce": 1309283410, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00201-5142", + "type": "rectangle", + "x": 4794.0, + "y": 324.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 587856776, + "version": 1, + "versionNonce": 2043684792, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00202-9653", + "type": "text", + "x": 4802.0, + "y": 333.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 171104060, + "version": 1, + "versionNonce": 546655221, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00203-3868", + "type": "rectangle", + "x": 4896.0, + "y": 324.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 818346034, + "version": 1, + "versionNonce": 1139283946, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00204-6848", + "type": "text", + "x": 4904.0, + "y": 333.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1604873388, + "version": 1, + "versionNonce": 1490443945, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00205-6698", + "type": "rectangle", + "x": 4988.0, + "y": 324.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 2146384416, + "version": 1, + "versionNonce": 1341071092, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00206-4992", + "type": "text", + "x": 4996.0, + "y": 333.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 470042625, + "version": 1, + "versionNonce": 1196619506, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00207-4841", + "type": "rectangle", + "x": 3900.0, + "y": 392.0, + "width": 1210.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1258635159, + "version": 1, + "versionNonce": 887923239, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00208-1965", + "type": "line", + "x": 3980.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2136311406, + "version": 1, + "versionNonce": 1713398008, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00209-7004", + "type": "line", + "x": 4100.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 466026979, + "version": 1, + "versionNonce": 1472119802, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00210-4238", + "type": "line", + "x": 4220.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 553811249, + "version": 1, + "versionNonce": 606831489, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00211-5985", + "type": "line", + "x": 4340.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2131559997, + "version": 1, + "versionNonce": 1641707317, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00212-3414", + "type": "line", + "x": 4460.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1297666127, + "version": 1, + "versionNonce": 620837169, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00213-3697", + "type": "line", + "x": 4580.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 985854720, + "version": 1, + "versionNonce": 496962118, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00214-9074", + "type": "line", + "x": 4700.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 174981774, + "version": 1, + "versionNonce": 1609837859, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00215-5198", + "type": "line", + "x": 4820.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1602817946, + "version": 1, + "versionNonce": 589261530, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00216-7133", + "type": "line", + "x": 4940.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1980012199, + "version": 1, + "versionNonce": 621240189, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00217-5895", + "type": "line", + "x": 5060.0, + "y": 392.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1225561655, + "version": 1, + "versionNonce": 1258889751, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00218-9929", + "type": "line", + "x": 3900.0, + "y": 472.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1439276568, + "version": 1, + "versionNonce": 111380559, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00219-2964", + "type": "line", + "x": 3900.0, + "y": 592.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 706725634, + "version": 1, + "versionNonce": 25545325, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00220-1782", + "type": "line", + "x": 3900.0, + "y": 712.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 938635866, + "version": 1, + "versionNonce": 228319219, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00221-2642", + "type": "line", + "x": 3900.0, + "y": 832.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1189114470, + "version": 1, + "versionNonce": 922071722, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00222-2518", + "type": "line", + "x": 3900.0, + "y": 952.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 796561954, + "version": 1, + "versionNonce": 1369816104, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00223-8007", + "type": "line", + "x": 3900.0, + "y": 1072.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1467756532, + "version": 1, + "versionNonce": 499905299, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00224-3596", + "type": "line", + "x": 3900.0, + "y": 1192.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 448038822, + "version": 1, + "versionNonce": 627198832, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00225-1760", + "type": "rectangle", + "x": 3916.0, + "y": 408.0, + "width": 211.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 561905591, + "version": 1, + "versionNonce": 1611409252, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00226-6752", + "type": "text", + "x": 3926.0, + "y": 414.0, + "width": 191.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1524315040, + "version": 1, + "versionNonce": 1525813120, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Description", + "originalText": "Created from Description", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "a-00227-5847", + "type": "arrow", + "x": 4158.0, + "y": 564.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1133340364, + "version": 1, + "versionNonce": 1411973777, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00228-6844", + "type": "arrow", + "x": 4406.0, + "y": 564.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1143874693, + "version": 1, + "versionNonce": 1684112365, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00229-6943", + "type": "arrow", + "x": 4654.0, + "y": 564.0, + "width": 214.0, + "height": 236.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 698984606, + "version": 1, + "versionNonce": 1824010118, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -214.0, + 236.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00230-5055", + "type": "arrow", + "x": 4654.0, + "y": 800.0, + "width": 214.0, + "height": 236.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1600932963, + "version": 1, + "versionNonce": 1340732644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -214.0, + 236.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r-00231-3245", + "type": "rectangle", + "x": 3944.0, + "y": 504.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1085264909, + "version": 1, + "versionNonce": 633434405, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00232-5976", + "type": "rectangle", + "x": 3944.0, + "y": 504.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 449599964, + "version": 1, + "versionNonce": 145448097, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00233-4556", + "type": "ellipse", + "x": 3938.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 858687473, + "version": 1, + "versionNonce": 1814785680, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00234-1469", + "type": "ellipse", + "x": 4152.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1457975126, + "version": 1, + "versionNonce": 738179719, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00235-1753", + "type": "rectangle", + "x": 3958.0, + "y": 518.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 668158073, + "version": 1, + "versionNonce": 1415605121, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00236-8751", + "type": "text", + "x": 3958.0, + "y": 527.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1099423128, + "version": 1, + "versionNonce": 890450943, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "T", + "originalText": "T", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00237-1649", + "type": "text", + "x": 4004.0, + "y": 517.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 681593593, + "version": 1, + "versionNonce": 331548110, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule every Monday", + "originalText": "Schedule every Monday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00238-3951", + "type": "text", + "x": 4004.0, + "y": 543.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1267982350, + "version": 1, + "versionNonce": 217028593, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00239-3485", + "type": "line", + "x": 3956.0, + "y": 566.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1323999936, + "version": 1, + "versionNonce": 1114575607, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00240-6075", + "type": "text", + "x": 3958.0, + "y": 580.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1448462034, + "version": 1, + "versionNonce": 2015177290, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Every Monday at 09:00", + "originalText": "Every Monday at 09:00", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00241-6353", + "type": "rectangle", + "x": 4192.0, + "y": 504.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 65897026, + "version": 1, + "versionNonce": 1881701955, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00242-3683", + "type": "rectangle", + "x": 4192.0, + "y": 504.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 170322328, + "version": 1, + "versionNonce": 2077990909, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00243-4761", + "type": "ellipse", + "x": 4186.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1530932214, + "version": 1, + "versionNonce": 2109535548, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00244-1428", + "type": "ellipse", + "x": 4400.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 181399569, + "version": 1, + "versionNonce": 1933626037, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00245-4452", + "type": "rectangle", + "x": 4206.0, + "y": 518.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 419512166, + "version": 1, + "versionNonce": 749242005, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00246-4984", + "type": "text", + "x": 4206.0, + "y": 527.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 115767746, + "version": 1, + "versionNonce": 1027248310, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00247-9971", + "type": "text", + "x": 4252.0, + "y": 517.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2012936015, + "version": 1, + "versionNonce": 1612582283, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Collect recent feedback", + "originalText": "Collect recent feedback", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00248-6635", + "type": "text", + "x": 4252.0, + "y": 543.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1109297118, + "version": 1, + "versionNonce": 1633746459, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark messages", + "originalText": "Lark messages", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00249-1229", + "type": "line", + "x": 4204.0, + "y": 566.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1795410792, + "version": 1, + "versionNonce": 1474969056, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00250-6728", + "type": "text", + "x": 4206.0, + "y": 580.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 188871057, + "version": 1, + "versionNonce": 155849285, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00251-9341", + "type": "rectangle", + "x": 4440.0, + "y": 504.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1093332563, + "version": 1, + "versionNonce": 1971200117, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00252-3917", + "type": "rectangle", + "x": 4440.0, + "y": 504.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 292432474, + "version": 1, + "versionNonce": 1980452580, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00253-5590", + "type": "ellipse", + "x": 4434.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1213868931, + "version": 1, + "versionNonce": 572326681, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00254-8688", + "type": "ellipse", + "x": 4648.0, + "y": 558.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 766985476, + "version": 1, + "versionNonce": 224026526, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00255-1604", + "type": "rectangle", + "x": 4454.0, + "y": 518.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 49670202, + "version": 1, + "versionNonce": 1095324737, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00256-2131", + "type": "text", + "x": 4454.0, + "y": 527.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1868916435, + "version": 1, + "versionNonce": 46642639, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00257-8932", + "type": "text", + "x": 4500.0, + "y": 517.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1589721236, + "version": 1, + "versionNonce": 1545931885, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Group feedback themes", + "originalText": "Group feedback themes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00258-3549", + "type": "text", + "x": 4500.0, + "y": 543.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1948341332, + "version": 1, + "versionNonce": 264452942, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00259-6431", + "type": "line", + "x": 4452.0, + "y": 566.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2018958728, + "version": 1, + "versionNonce": 918349221, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00260-1936", + "type": "text", + "x": 4454.0, + "y": 580.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 655971894, + "version": 1, + "versionNonce": 866724465, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00261-8922", + "type": "rectangle", + "x": 4440.0, + "y": 740.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 201762234, + "version": 1, + "versionNonce": 439560707, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00262-2696", + "type": "rectangle", + "x": 4440.0, + "y": 740.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1847938695, + "version": 1, + "versionNonce": 1147472528, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00263-4560", + "type": "ellipse", + "x": 4434.0, + "y": 794.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 416758094, + "version": 1, + "versionNonce": 1333191385, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00264-2645", + "type": "ellipse", + "x": 4648.0, + "y": 794.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 944383850, + "version": 1, + "versionNonce": 998145644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00265-8336", + "type": "rectangle", + "x": 4454.0, + "y": 754.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 267960454, + "version": 1, + "versionNonce": 2061737178, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00266-9171", + "type": "text", + "x": 4454.0, + "y": 763.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 958312472, + "version": 1, + "versionNonce": 1510403854, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00267-1569", + "type": "text", + "x": 4500.0, + "y": 753.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 213216345, + "version": 1, + "versionNonce": 1957588969, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft concise summary", + "originalText": "Draft concise summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00268-6545", + "type": "text", + "x": 4500.0, + "y": 779.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1540860241, + "version": 1, + "versionNonce": 2138444123, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00269-8391", + "type": "line", + "x": 4452.0, + "y": 802.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1564867787, + "version": 1, + "versionNonce": 595906136, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00270-6614", + "type": "text", + "x": 4454.0, + "y": 816.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1293146404, + "version": 1, + "versionNonce": 73526054, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00271-8366", + "type": "rectangle", + "x": 4440.0, + "y": 976.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 958929808, + "version": 1, + "versionNonce": 565380060, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00272-8669", + "type": "rectangle", + "x": 4440.0, + "y": 976.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 685586677, + "version": 1, + "versionNonce": 1123562527, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00273-8780", + "type": "ellipse", + "x": 4434.0, + "y": 1030.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1663423181, + "version": 1, + "versionNonce": 1667460693, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00274-5315", + "type": "ellipse", + "x": 4648.0, + "y": 1030.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 346522571, + "version": 1, + "versionNonce": 1043907599, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00275-2668", + "type": "rectangle", + "x": 4454.0, + "y": 990.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1108071382, + "version": 1, + "versionNonce": 2040281320, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00276-4296", + "type": "text", + "x": 4454.0, + "y": 999.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 632025954, + "version": 1, + "versionNonce": 542459938, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00277-7532", + "type": "text", + "x": 4500.0, + "y": 989.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1415755504, + "version": 1, + "versionNonce": 1869832633, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Post feedback summary", + "originalText": "Post feedback summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00278-2411", + "type": "text", + "x": 4500.0, + "y": 1015.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 865337512, + "version": 1, + "versionNonce": 1632647139, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00279-3204", + "type": "line", + "x": 4452.0, + "y": 1038.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 896537753, + "version": 1, + "versionNonce": 1121235239, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00280-1167", + "type": "text", + "x": 4454.0, + "y": 1052.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2033260413, + "version": 1, + "versionNonce": 1811351210, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00281-8695", + "type": "rectangle", + "x": 3916.0, + "y": 1082.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1095424036, + "version": 1, + "versionNonce": 114314282, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00282-8319", + "type": "text", + "x": 3924.0, + "y": 1091.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 775126905, + "version": 1, + "versionNonce": 274829809, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00283-1034", + "type": "rectangle", + "x": 3916.0, + "y": 1118.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 74579709, + "version": 1, + "versionNonce": 397347995, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00284-5314", + "type": "text", + "x": 3924.0, + "y": 1127.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1917992649, + "version": 1, + "versionNonce": 2122747661, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00285-3748", + "type": "rectangle", + "x": 3916.0, + "y": 1154.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1488384017, + "version": 1, + "versionNonce": 258931500, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00286-7228", + "type": "text", + "x": 3924.0, + "y": 1163.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1115102660, + "version": 1, + "versionNonce": 2014631740, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00287-2834", + "type": "rectangle", + "x": 4556.0, + "y": 1106.0, + "width": 138.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1096816586, + "version": 1, + "versionNonce": 600526684, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00288-1413", + "type": "rectangle", + "x": 4566.0, + "y": 1122.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 2144961277, + "version": 1, + "versionNonce": 698302925, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00289-6722", + "type": "rectangle", + "x": 4606.0, + "y": 1122.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 488970030, + "version": 1, + "versionNonce": 1656664951, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00290-6169", + "type": "rectangle", + "x": 4646.0, + "y": 1122.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 342539583, + "version": 1, + "versionNonce": 542933312, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00291-9429", + "type": "rectangle", + "x": 4566.0, + "y": 1150.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1190326036, + "version": 1, + "versionNonce": 862601533, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00292-6447", + "type": "rectangle", + "x": 4606.0, + "y": 1150.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 313574622, + "version": 1, + "versionNonce": 2128119794, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00293-9302", + "type": "rectangle", + "x": 4720.0, + "y": 392.0, + "width": 390.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#e5e7eb", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 858710666, + "version": 1, + "versionNonce": 1417432104, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00294-3138", + "type": "text", + "x": 4740.0, + "y": 410.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 606075863, + "version": 1, + "versionNonce": 471407912, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Node configuration", + "originalText": "Node configuration", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00295-3449", + "type": "text", + "x": 4740.0, + "y": 440.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 882316004, + "version": 1, + "versionNonce": 2058637860, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule every Monday", + "originalText": "Schedule every Monday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00296-4145", + "type": "text", + "x": 5064.0, + "y": 409.0, + "width": 24.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1001425296, + "version": 1, + "versionNonce": 1612682349, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x", + "originalText": "x", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00297-1710", + "type": "line", + "x": 4720.0, + "y": 468.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1618034771, + "version": 1, + "versionNonce": 980149182, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00298-9610", + "type": "text", + "x": 4740.0, + "y": 492.0, + "width": 71.82, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 790426006, + "version": 1, + "versionNonce": 1674773579, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NODE NAME", + "originalText": "NODE NAME", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00299-3915", + "type": "rectangle", + "x": 4740.0, + "y": 514.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1416091167, + "version": 1, + "versionNonce": 1038382237, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00300-3680", + "type": "text", + "x": 4752.0, + "y": 525.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 66141781, + "version": 1, + "versionNonce": 726820429, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule every Monday", + "originalText": "Schedule every Monday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00301-6634", + "type": "text", + "x": 4740.0, + "y": 580.0, + "width": 31.919999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2014475214, + "version": 1, + "versionNonce": 1864518595, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TYPE", + "originalText": "TYPE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00302-2676", + "type": "rectangle", + "x": 4740.0, + "y": 602.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 58914059, + "version": 1, + "versionNonce": 1558790760, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00303-1587", + "type": "text", + "x": 4752.0, + "y": 613.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1184971013, + "version": 1, + "versionNonce": 261181265, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00304-5414", + "type": "text", + "x": 4740.0, + "y": 668.0, + "width": 71.82, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1197572192, + "version": 1, + "versionNonce": 1285598344, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "FREQUENCY", + "originalText": "FREQUENCY", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00305-8730", + "type": "rectangle", + "x": 4740.0, + "y": 690.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1536659718, + "version": 1, + "versionNonce": 2110000981, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00306-3013", + "type": "text", + "x": 4752.0, + "y": 701.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1059428017, + "version": 1, + "versionNonce": 2142435693, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Every Monday at 09:00", + "originalText": "Every Monday at 09:00", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00307-6275", + "type": "text", + "x": 4740.0, + "y": 756.0, + "width": 63.839999999999996, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1276402982, + "version": 1, + "versionNonce": 786694950, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TIMEZONE", + "originalText": "TIMEZONE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00308-2487", + "type": "rectangle", + "x": 4740.0, + "y": 778.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 126078861, + "version": 1, + "versionNonce": 318476850, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00309-4116", + "type": "text", + "x": 4752.0, + "y": 789.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1603733352, + "version": 1, + "versionNonce": 264845589, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Asia/Shanghai", + "originalText": "Asia/Shanghai", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00310-5448", + "type": "line", + "x": 4720.0, + "y": 1148.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1869707965, + "version": 1, + "versionNonce": 1882828120, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00311-5257", + "type": "rectangle", + "x": 4914.0, + "y": 1162.0, + "width": 78.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 1147673080, + "version": 1, + "versionNonce": 332343003, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00312-6488", + "type": "text", + "x": 4922.0, + "y": 1171.0, + "width": 62.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 1828813987, + "version": 1, + "versionNonce": 1973693551, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Delete", + "originalText": "Delete", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00313-6658", + "type": "rectangle", + "x": 5002.0, + "y": 1162.0, + "width": 88.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": { + "type": 3 + }, + "seed": 638741813, + "version": 1, + "versionNonce": 1527692760, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00314-7692", + "type": "text", + "x": 5010.0, + "y": 1171.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-02" + ], + "frameId": "frame-00179-4597", + "roundness": null, + "seed": 2007675807, + "version": 1, + "versionNonce": 1973788005, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Apply", + "originalText": "Apply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00315-3022", + "type": "frame", + "x": 160.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 42557187, + "version": 1, + "versionNonce": 983898766, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "04 Start blank - empty Workflow draft" + }, + { + "id": "r-00317-5462", + "type": "rectangle", + "x": 160.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 2283132, + "version": 1, + "versionNonce": 17327505, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00318-8515", + "type": "rectangle", + "x": 160.0, + "y": 1480.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1259272673, + "version": 1, + "versionNonce": 444069262, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00319-4624", + "type": "text", + "x": 184.0, + "y": 1504.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1937579970, + "version": 1, + "versionNonce": 2040487640, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00320-7714", + "type": "text", + "x": 184.0, + "y": 1533.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1678315007, + "version": 1, + "versionNonce": 1814353859, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00321-4560", + "type": "rectangle", + "x": 172.0, + "y": 1588.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 858902989, + "version": 1, + "versionNonce": 659842308, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00322-5118", + "type": "ellipse", + "x": 188.0, + "y": 1600.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 438963308, + "version": 1, + "versionNonce": 2081649833, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00323-6978", + "type": "text", + "x": 208.0, + "y": 1596.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 880520027, + "version": 1, + "versionNonce": 2048586288, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00324-8218", + "type": "ellipse", + "x": 188.0, + "y": 1652.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 953335281, + "version": 1, + "versionNonce": 1712703414, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00325-5938", + "type": "text", + "x": 208.0, + "y": 1648.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1120101386, + "version": 1, + "versionNonce": 550130064, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00326-5417", + "type": "ellipse", + "x": 188.0, + "y": 1704.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 452349976, + "version": 1, + "versionNonce": 854362230, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00327-6114", + "type": "text", + "x": 208.0, + "y": 1700.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1305364217, + "version": 1, + "versionNonce": 1391595430, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00328-1183", + "type": "text", + "x": 184.0, + "y": 2326.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1310101204, + "version": 1, + "versionNonce": 2016589689, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00329-8284", + "type": "text", + "x": 184.0, + "y": 2352.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1095768232, + "version": 1, + "versionNonce": 394241251, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00330-2690", + "type": "rectangle", + "x": 370.0, + "y": 1480.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 12503718, + "version": 1, + "versionNonce": 944448609, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00331-5130", + "type": "text", + "x": 400.0, + "y": 1498.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1131450871, + "version": 1, + "versionNonce": 405947081, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Untitled workflow", + "originalText": "Untitled workflow", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00332-7144", + "type": "text", + "x": 400.0, + "y": 1529.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1453471729, + "version": 1, + "versionNonce": 1302574345, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "originalText": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00333-4287", + "type": "rectangle", + "x": 1090.0, + "y": 1504.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 2017795932, + "version": 1, + "versionNonce": 1033670969, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00334-3746", + "type": "text", + "x": 1098.0, + "y": 1513.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1004426732, + "version": 1, + "versionNonce": 137356904, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00335-2664", + "type": "rectangle", + "x": 1172.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 729875190, + "version": 1, + "versionNonce": 876892230, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00336-9820", + "type": "text", + "x": 1180.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1352267811, + "version": 1, + "versionNonce": 507442455, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00337-8978", + "type": "rectangle", + "x": 1274.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 458022298, + "version": 1, + "versionNonce": 313088696, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00338-3382", + "type": "text", + "x": 1282.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1923686879, + "version": 1, + "versionNonce": 884670832, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00339-8902", + "type": "rectangle", + "x": 1376.0, + "y": 1504.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1944820142, + "version": 1, + "versionNonce": 1878136582, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00340-8063", + "type": "text", + "x": 1384.0, + "y": 1513.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 231096130, + "version": 1, + "versionNonce": 823537119, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00341-5301", + "type": "rectangle", + "x": 1468.0, + "y": 1504.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 511275896, + "version": 1, + "versionNonce": 1650692772, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00342-6841", + "type": "text", + "x": 1476.0, + "y": 1513.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1962365347, + "version": 1, + "versionNonce": 632369453, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00343-7129", + "type": "rectangle", + "x": 380.0, + "y": 1572.0, + "width": 1210.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1783601389, + "version": 1, + "versionNonce": 2130566188, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00344-1682", + "type": "line", + "x": 460.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 516782545, + "version": 1, + "versionNonce": 374773894, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00345-8064", + "type": "line", + "x": 580.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 850915205, + "version": 1, + "versionNonce": 2131168694, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00346-1436", + "type": "line", + "x": 700.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 410612334, + "version": 1, + "versionNonce": 1121739985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00347-6513", + "type": "line", + "x": 820.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1025833937, + "version": 1, + "versionNonce": 347215111, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00348-4791", + "type": "line", + "x": 940.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 339796898, + "version": 1, + "versionNonce": 2108855840, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00349-3736", + "type": "line", + "x": 1060.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1279470552, + "version": 1, + "versionNonce": 1594583881, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00350-8665", + "type": "line", + "x": 1180.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1091985185, + "version": 1, + "versionNonce": 13547251, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00351-2748", + "type": "line", + "x": 1300.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1107782610, + "version": 1, + "versionNonce": 508066236, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00352-4133", + "type": "line", + "x": 1420.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1988609540, + "version": 1, + "versionNonce": 1396654306, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00353-5915", + "type": "line", + "x": 1540.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 229708895, + "version": 1, + "versionNonce": 1745747624, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00354-5164", + "type": "line", + "x": 380.0, + "y": 1652.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 808700537, + "version": 1, + "versionNonce": 1010909192, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00355-1254", + "type": "line", + "x": 380.0, + "y": 1772.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 561426437, + "version": 1, + "versionNonce": 201138841, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00356-7306", + "type": "line", + "x": 380.0, + "y": 1892.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 720971524, + "version": 1, + "versionNonce": 650587655, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00357-7744", + "type": "line", + "x": 380.0, + "y": 2012.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 356346214, + "version": 1, + "versionNonce": 1085209217, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00358-9499", + "type": "line", + "x": 380.0, + "y": 2132.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1638361495, + "version": 1, + "versionNonce": 1486709379, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00359-7012", + "type": "line", + "x": 380.0, + "y": 2252.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1955058767, + "version": 1, + "versionNonce": 76519465, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00360-6302", + "type": "line", + "x": 380.0, + "y": 2372.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 496145280, + "version": 1, + "versionNonce": 1512397418, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00361-4618", + "type": "rectangle", + "x": 396.0, + "y": 1588.0, + "width": 164.4, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1230651726, + "version": 1, + "versionNonce": 1643179365, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00362-4197", + "type": "text", + "x": 406.0, + "y": 1594.0, + "width": 144.4, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 780112178, + "version": 1, + "versionNonce": 1423736381, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Blank", + "originalText": "Created from Blank", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00363-6216", + "type": "rectangle", + "x": 929.0, + "y": 1895.0, + "width": 112.0, + "height": 112.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1086815180, + "version": 1, + "versionNonce": 1631195342, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00364-3127", + "type": "text", + "x": 929.0, + "y": 1923.0, + "width": 112.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 665448921, + "version": 1, + "versionNonce": 570616313, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 32, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00365-9217", + "type": "text", + "x": 835.0, + "y": 2025.0, + "width": 300.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 213473822, + "version": 1, + "versionNonce": 1836158962, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add first step", + "originalText": "Add first step", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00366-6391", + "type": "text", + "x": 765.0, + "y": 2063.0, + "width": 440.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 208552174, + "version": 1, + "versionNonce": 432886256, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Start this workflow by adding the first step.", + "originalText": "Start this workflow by adding the first step.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00367-4867", + "type": "rectangle", + "x": 396.0, + "y": 2262.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1860302457, + "version": 1, + "versionNonce": 1221754845, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00368-6644", + "type": "text", + "x": 404.0, + "y": 2271.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1753532650, + "version": 1, + "versionNonce": 1174014866, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00369-8813", + "type": "rectangle", + "x": 396.0, + "y": 2298.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1896250950, + "version": 1, + "versionNonce": 1880989829, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00370-8271", + "type": "text", + "x": 404.0, + "y": 2307.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 1011863622, + "version": 1, + "versionNonce": 1784772485, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00371-3884", + "type": "rectangle", + "x": 396.0, + "y": 2334.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": { + "type": 3 + }, + "seed": 1487119837, + "version": 1, + "versionNonce": 1731683853, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00372-8045", + "type": "text", + "x": 404.0, + "y": 2343.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-03" + ], + "frameId": "frame-00315-3022", + "roundness": null, + "seed": 2131149974, + "version": 1, + "versionNonce": 1344082925, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00373-4230", + "type": "frame", + "x": 1920.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 894017045, + "version": 1, + "versionNonce": 132502651, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "05 Import YAML - imported Workflow draft" + }, + { + "id": "r-00375-6590", + "type": "rectangle", + "x": 1920.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1066768897, + "version": 1, + "versionNonce": 571719350, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00376-7755", + "type": "rectangle", + "x": 1920.0, + "y": 1480.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1650838828, + "version": 1, + "versionNonce": 565701919, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00377-9643", + "type": "text", + "x": 1944.0, + "y": 1504.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 548016263, + "version": 1, + "versionNonce": 279775350, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00378-9274", + "type": "text", + "x": 1944.0, + "y": 1533.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1050175930, + "version": 1, + "versionNonce": 1737295992, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00379-8077", + "type": "rectangle", + "x": 1932.0, + "y": 1588.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 652033221, + "version": 1, + "versionNonce": 446347078, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00380-2291", + "type": "ellipse", + "x": 1948.0, + "y": 1600.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 948885946, + "version": 1, + "versionNonce": 371961739, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00381-1824", + "type": "text", + "x": 1968.0, + "y": 1596.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 620696319, + "version": 1, + "versionNonce": 1859692279, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00382-4337", + "type": "ellipse", + "x": 1948.0, + "y": 1652.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 612296472, + "version": 1, + "versionNonce": 1267435577, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00383-1700", + "type": "text", + "x": 1968.0, + "y": 1648.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 889149494, + "version": 1, + "versionNonce": 1611736151, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00384-2721", + "type": "ellipse", + "x": 1948.0, + "y": 1704.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 198661835, + "version": 1, + "versionNonce": 1011842532, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00385-6958", + "type": "text", + "x": 1968.0, + "y": 1700.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1247346228, + "version": 1, + "versionNonce": 604293774, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00386-6695", + "type": "text", + "x": 1944.0, + "y": 2326.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1264159160, + "version": 1, + "versionNonce": 696200860, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00387-7296", + "type": "text", + "x": 1944.0, + "y": 2352.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 963781976, + "version": 1, + "versionNonce": 129929634, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00388-1776", + "type": "rectangle", + "x": 2130.0, + "y": 1480.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 383026340, + "version": 1, + "versionNonce": 1814126832, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00389-9515", + "type": "text", + "x": 2160.0, + "y": 1498.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 167239364, + "version": 1, + "versionNonce": 1423445626, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Daily order exception report", + "originalText": "Daily order exception report", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00390-2947", + "type": "text", + "x": 2160.0, + "y": 1529.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1974425555, + "version": 1, + "versionNonce": 798039837, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 1 \u00b7 Imported just now", + "originalText": "Current draft \u00b7 revision 1 \u00b7 Imported just now", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00391-6290", + "type": "rectangle", + "x": 2850.0, + "y": 1504.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1889887093, + "version": 1, + "versionNonce": 1968710775, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00392-6895", + "type": "text", + "x": 2858.0, + "y": 1513.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2058324767, + "version": 1, + "versionNonce": 1458336462, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00393-1231", + "type": "rectangle", + "x": 2932.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1363548493, + "version": 1, + "versionNonce": 770861111, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00394-3538", + "type": "text", + "x": 2940.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 447585636, + "version": 1, + "versionNonce": 1595914718, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00395-9017", + "type": "rectangle", + "x": 3034.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 426106756, + "version": 1, + "versionNonce": 180552981, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00396-9297", + "type": "text", + "x": 3042.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 229377819, + "version": 1, + "versionNonce": 1051576803, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00397-2057", + "type": "rectangle", + "x": 3136.0, + "y": 1504.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 979702586, + "version": 1, + "versionNonce": 975810671, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00398-4302", + "type": "text", + "x": 3144.0, + "y": 1513.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1861849773, + "version": 1, + "versionNonce": 1573531956, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00399-6299", + "type": "rectangle", + "x": 3228.0, + "y": 1504.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 518830164, + "version": 1, + "versionNonce": 1990591369, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00400-3272", + "type": "text", + "x": 3236.0, + "y": 1513.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1759040550, + "version": 1, + "versionNonce": 46565691, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00401-6395", + "type": "rectangle", + "x": 2140.0, + "y": 1572.0, + "width": 1210.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 800882108, + "version": 1, + "versionNonce": 1524822334, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00402-9735", + "type": "line", + "x": 2220.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1124803193, + "version": 1, + "versionNonce": 1780043816, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00403-1639", + "type": "line", + "x": 2340.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1473242142, + "version": 1, + "versionNonce": 1125577164, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00404-7780", + "type": "line", + "x": 2460.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 470611028, + "version": 1, + "versionNonce": 1819928665, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00405-4476", + "type": "line", + "x": 2580.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1982717731, + "version": 1, + "versionNonce": 1928603786, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00406-4184", + "type": "line", + "x": 2700.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 555336086, + "version": 1, + "versionNonce": 1694509733, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00407-5605", + "type": "line", + "x": 2820.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 613531729, + "version": 1, + "versionNonce": 1384965803, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00408-7932", + "type": "line", + "x": 2940.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 343632542, + "version": 1, + "versionNonce": 919502337, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00409-9000", + "type": "line", + "x": 3060.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 449773164, + "version": 1, + "versionNonce": 2023689952, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00410-9883", + "type": "line", + "x": 3180.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 360880803, + "version": 1, + "versionNonce": 420610877, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00411-4016", + "type": "line", + "x": 3300.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 702926291, + "version": 1, + "versionNonce": 1260009592, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00412-5247", + "type": "line", + "x": 2140.0, + "y": 1652.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 113496579, + "version": 1, + "versionNonce": 245319262, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00413-9227", + "type": "line", + "x": 2140.0, + "y": 1772.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 401820249, + "version": 1, + "versionNonce": 1634534107, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00414-5874", + "type": "line", + "x": 2140.0, + "y": 1892.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1169817713, + "version": 1, + "versionNonce": 851760780, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00415-7242", + "type": "line", + "x": 2140.0, + "y": 2012.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2031198241, + "version": 1, + "versionNonce": 316920407, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00416-6290", + "type": "line", + "x": 2140.0, + "y": 2132.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2087033620, + "version": 1, + "versionNonce": 1987312844, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00417-4072", + "type": "line", + "x": 2140.0, + "y": 2252.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 933729539, + "version": 1, + "versionNonce": 922584770, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00418-3639", + "type": "line", + "x": 2140.0, + "y": 2372.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1956549775, + "version": 1, + "versionNonce": 460277284, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00419-8145", + "type": "rectangle", + "x": 2156.0, + "y": 1588.0, + "width": 172.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 870691716, + "version": 1, + "versionNonce": 1564414197, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00420-7124", + "type": "text", + "x": 2166.0, + "y": 1594.0, + "width": 152.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 387367722, + "version": 1, + "versionNonce": 1133770345, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Import", + "originalText": "Created from Import", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "a-00421-7100", + "type": "arrow", + "x": 2398.0, + "y": 1744.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1133459987, + "version": 1, + "versionNonce": 2147272685, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00422-8081", + "type": "arrow", + "x": 2646.0, + "y": 1744.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 359074719, + "version": 1, + "versionNonce": 1573270907, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00423-7143", + "type": "arrow", + "x": 2894.0, + "y": 1744.0, + "width": 214.0, + "height": 236.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1336653315, + "version": 1, + "versionNonce": 1863663777, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -214.0, + 236.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r-00424-6209", + "type": "rectangle", + "x": 2184.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 690332460, + "version": 1, + "versionNonce": 817501434, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00425-9762", + "type": "rectangle", + "x": 2184.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 931309064, + "version": 1, + "versionNonce": 131757949, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00426-3882", + "type": "ellipse", + "x": 2178.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 589036927, + "version": 1, + "versionNonce": 125765451, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00427-5425", + "type": "ellipse", + "x": 2392.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1576632388, + "version": 1, + "versionNonce": 1555900735, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00428-6896", + "type": "rectangle", + "x": 2198.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 363975874, + "version": 1, + "versionNonce": 413198224, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00429-5115", + "type": "text", + "x": 2198.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 451147796, + "version": 1, + "versionNonce": 1505883401, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "T", + "originalText": "T", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00430-6605", + "type": "text", + "x": 2244.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2016158021, + "version": 1, + "versionNonce": 1275248107, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule every weekday", + "originalText": "Schedule every weekday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00431-1541", + "type": "text", + "x": 2244.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1913742875, + "version": 1, + "versionNonce": 1343190612, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00432-8108", + "type": "line", + "x": 2196.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1218824084, + "version": 1, + "versionNonce": 558676590, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00433-2958", + "type": "text", + "x": 2198.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 216631248, + "version": 1, + "versionNonce": 485935105, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00434-2326", + "type": "rectangle", + "x": 2432.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1105355300, + "version": 1, + "versionNonce": 1154669100, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00435-7870", + "type": "rectangle", + "x": 2432.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "#0891b2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 578100903, + "version": 1, + "versionNonce": 363214318, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00436-7145", + "type": "ellipse", + "x": 2426.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "#0891b2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1927497171, + "version": 1, + "versionNonce": 898768112, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00437-3586", + "type": "ellipse", + "x": 2640.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "#0891b2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1989017601, + "version": 1, + "versionNonce": 1484151114, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00438-9110", + "type": "rectangle", + "x": 2446.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 590375721, + "version": 1, + "versionNonce": 1028010992, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00439-1242", + "type": "text", + "x": 2446.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#0891b2", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2092560323, + "version": 1, + "versionNonce": 2060118286, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "D", + "originalText": "D", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00440-9327", + "type": "text", + "x": 2492.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1157642155, + "version": 1, + "versionNonce": 724129063, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Find order exceptions", + "originalText": "Find order exceptions", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00441-7404", + "type": "text", + "x": 2492.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 935463053, + "version": 1, + "versionNonce": 631626389, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Data query", + "originalText": "Data query", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00442-5116", + "type": "line", + "x": 2444.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1231730879, + "version": 1, + "versionNonce": 1812108254, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00443-5214", + "type": "text", + "x": 2446.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1247118585, + "version": 1, + "versionNonce": 2129318375, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Orders", + "originalText": "Orders", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00444-1039", + "type": "rectangle", + "x": 2680.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 113432608, + "version": 1, + "versionNonce": 1070713033, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00445-1701", + "type": "rectangle", + "x": 2680.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1725622613, + "version": 1, + "versionNonce": 1197335082, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00446-1169", + "type": "ellipse", + "x": 2674.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 674711124, + "version": 1, + "versionNonce": 866386896, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00447-3852", + "type": "ellipse", + "x": 2888.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 359942413, + "version": 1, + "versionNonce": 790658582, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00448-2853", + "type": "rectangle", + "x": 2694.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1349647054, + "version": 1, + "versionNonce": 1656665502, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00449-1880", + "type": "text", + "x": 2694.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1162698083, + "version": 1, + "versionNonce": 965254476, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00450-1611", + "type": "text", + "x": 2740.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2076173996, + "version": 1, + "versionNonce": 1357515334, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Summarize exceptions", + "originalText": "Summarize exceptions", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00451-3509", + "type": "text", + "x": 2740.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 80224444, + "version": 1, + "versionNonce": 745539407, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00452-6291", + "type": "line", + "x": 2692.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2132993665, + "version": 1, + "versionNonce": 868582197, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00453-8652", + "type": "text", + "x": 2694.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 888454877, + "version": 1, + "versionNonce": 1677801418, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00454-4336", + "type": "rectangle", + "x": 2680.0, + "y": 1920.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1032351350, + "version": 1, + "versionNonce": 1386739540, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00455-1877", + "type": "rectangle", + "x": 2680.0, + "y": 1920.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2091494065, + "version": 1, + "versionNonce": 1954718944, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00456-5613", + "type": "ellipse", + "x": 2674.0, + "y": 1974.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1142287261, + "version": 1, + "versionNonce": 1790329915, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00457-3305", + "type": "ellipse", + "x": 2888.0, + "y": 1974.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 628828323, + "version": 1, + "versionNonce": 1146136573, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00458-3452", + "type": "rectangle", + "x": 2694.0, + "y": 1934.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 2042464788, + "version": 1, + "versionNonce": 596321335, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00459-7182", + "type": "text", + "x": 2694.0, + "y": 1943.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 575847588, + "version": 1, + "versionNonce": 1442388324, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00460-2993", + "type": "text", + "x": 2740.0, + "y": 1933.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1751620069, + "version": 1, + "versionNonce": 562758755, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Send operations summary", + "originalText": "Send operations summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00461-3804", + "type": "text", + "x": 2740.0, + "y": 1959.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2030209434, + "version": 1, + "versionNonce": 1047402106, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00462-1113", + "type": "line", + "x": 2692.0, + "y": 1982.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 241326731, + "version": 1, + "versionNonce": 87554638, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00463-1426", + "type": "text", + "x": 2694.0, + "y": 1996.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 463866723, + "version": 1, + "versionNonce": 1342771574, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00464-1147", + "type": "rectangle", + "x": 2156.0, + "y": 2262.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 559676673, + "version": 1, + "versionNonce": 1205560567, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00465-7139", + "type": "text", + "x": 2164.0, + "y": 2271.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1913006616, + "version": 1, + "versionNonce": 2132308979, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00466-7509", + "type": "rectangle", + "x": 2156.0, + "y": 2298.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1416230354, + "version": 1, + "versionNonce": 386160336, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00467-6199", + "type": "text", + "x": 2164.0, + "y": 2307.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1580684773, + "version": 1, + "versionNonce": 1595253345, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00468-4215", + "type": "rectangle", + "x": 2156.0, + "y": 2334.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1136802612, + "version": 1, + "versionNonce": 1777703197, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00469-5174", + "type": "text", + "x": 2164.0, + "y": 2343.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1642294323, + "version": 1, + "versionNonce": 780318946, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00470-3921", + "type": "rectangle", + "x": 2796.0, + "y": 2286.0, + "width": 138.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1349111837, + "version": 1, + "versionNonce": 2048119173, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00471-6316", + "type": "rectangle", + "x": 2806.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 215472825, + "version": 1, + "versionNonce": 780004938, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00472-1428", + "type": "rectangle", + "x": 2846.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 784717305, + "version": 1, + "versionNonce": 643934769, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00473-2790", + "type": "rectangle", + "x": 2886.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 56625910, + "version": 1, + "versionNonce": 186612890, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00474-9236", + "type": "rectangle", + "x": 2806.0, + "y": 2330.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 2013795494, + "version": 1, + "versionNonce": 2008008781, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00475-9318", + "type": "rectangle", + "x": 2960.0, + "y": 1572.0, + "width": 390.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#e5e7eb", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 846798613, + "version": 1, + "versionNonce": 949354913, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00476-8202", + "type": "text", + "x": 2980.0, + "y": 1590.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 684792509, + "version": 1, + "versionNonce": 1569638094, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Node configuration", + "originalText": "Node configuration", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00477-4558", + "type": "text", + "x": 2980.0, + "y": 1620.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 44571204, + "version": 1, + "versionNonce": 2133681118, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Find order exceptions", + "originalText": "Find order exceptions", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00478-3476", + "type": "text", + "x": 3304.0, + "y": 1589.0, + "width": 24.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1919668463, + "version": 1, + "versionNonce": 1215516350, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x", + "originalText": "x", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00479-9275", + "type": "line", + "x": 2960.0, + "y": 1648.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1987261006, + "version": 1, + "versionNonce": 1386567819, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00480-3744", + "type": "text", + "x": 2980.0, + "y": 1672.0, + "width": 71.82, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1505466352, + "version": 1, + "versionNonce": 540012733, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NODE NAME", + "originalText": "NODE NAME", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00481-6406", + "type": "rectangle", + "x": 2980.0, + "y": 1694.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 888787557, + "version": 1, + "versionNonce": 2035742007, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00482-3361", + "type": "text", + "x": 2992.0, + "y": 1705.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 423651487, + "version": 1, + "versionNonce": 318838013, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Find order exceptions", + "originalText": "Find order exceptions", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00483-9355", + "type": "text", + "x": 2980.0, + "y": 1760.0, + "width": 31.919999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 846448227, + "version": 1, + "versionNonce": 2093466681, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TYPE", + "originalText": "TYPE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00484-5633", + "type": "rectangle", + "x": 2980.0, + "y": 1782.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 167250139, + "version": 1, + "versionNonce": 1689055260, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00485-9527", + "type": "text", + "x": 2992.0, + "y": 1793.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1234329338, + "version": 1, + "versionNonce": 1937225415, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Data query", + "originalText": "Data query", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00486-2659", + "type": "text", + "x": 2980.0, + "y": 1848.0, + "width": 47.879999999999995, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 2001238317, + "version": 1, + "versionNonce": 2117212282, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SOURCE", + "originalText": "SOURCE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00487-4248", + "type": "rectangle", + "x": 2980.0, + "y": 1870.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 849091520, + "version": 1, + "versionNonce": 1864618112, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00488-3093", + "type": "text", + "x": 2992.0, + "y": 1881.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 530027516, + "version": 1, + "versionNonce": 110130534, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Orders", + "originalText": "Orders", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00489-7683", + "type": "text", + "x": 2980.0, + "y": 1936.0, + "width": 47.879999999999995, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1368585613, + "version": 1, + "versionNonce": 1821626862, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "FILTER", + "originalText": "FILTER", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00490-5771", + "type": "rectangle", + "x": 2980.0, + "y": 1958.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 48068245, + "version": 1, + "versionNonce": 1332841767, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00491-3538", + "type": "text", + "x": 2992.0, + "y": 1969.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 1627277313, + "version": 1, + "versionNonce": 99958022, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "status = exception", + "originalText": "status = exception", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00492-7766", + "type": "line", + "x": 2960.0, + "y": 2328.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 728408978, + "version": 1, + "versionNonce": 1116456365, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00493-8594", + "type": "rectangle", + "x": 3154.0, + "y": 2342.0, + "width": 78.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1540293338, + "version": 1, + "versionNonce": 633615825, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00494-4373", + "type": "text", + "x": 3162.0, + "y": 2351.0, + "width": 62.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 484679513, + "version": 1, + "versionNonce": 744165405, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Delete", + "originalText": "Delete", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00495-6070", + "type": "rectangle", + "x": 3242.0, + "y": 2342.0, + "width": 88.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": { + "type": 3 + }, + "seed": 1414343000, + "version": 1, + "versionNonce": 1576107066, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00496-4873", + "type": "text", + "x": 3250.0, + "y": 2351.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-04" + ], + "frameId": "frame-00373-4230", + "roundness": null, + "seed": 430208822, + "version": 1, + "versionNonce": 1255412657, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Apply", + "originalText": "Apply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00497-1095", + "type": "frame", + "x": 3680.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 13269160, + "version": 1, + "versionNonce": 1011063864, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "06 Template - populated Workflow draft" + }, + { + "id": "r-00499-9798", + "type": "rectangle", + "x": 3680.0, + "y": 1480.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 583711047, + "version": 1, + "versionNonce": 1326515533, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00500-7284", + "type": "rectangle", + "x": 3680.0, + "y": 1480.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1973535551, + "version": 1, + "versionNonce": 1968115255, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00501-7180", + "type": "text", + "x": 3704.0, + "y": 1504.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1994264986, + "version": 1, + "versionNonce": 1024805867, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00502-9317", + "type": "text", + "x": 3704.0, + "y": 1533.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1335769668, + "version": 1, + "versionNonce": 1244840994, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00503-2598", + "type": "rectangle", + "x": 3692.0, + "y": 1588.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1870449888, + "version": 1, + "versionNonce": 986152151, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00504-5460", + "type": "ellipse", + "x": 3708.0, + "y": 1600.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 298446521, + "version": 1, + "versionNonce": 1972974574, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00505-3572", + "type": "text", + "x": 3728.0, + "y": 1596.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 933370169, + "version": 1, + "versionNonce": 743733077, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00506-8790", + "type": "ellipse", + "x": 3708.0, + "y": 1652.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 239847797, + "version": 1, + "versionNonce": 1471327341, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00507-7587", + "type": "text", + "x": 3728.0, + "y": 1648.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1206099618, + "version": 1, + "versionNonce": 128006646, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00508-7215", + "type": "ellipse", + "x": 3708.0, + "y": 1704.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 614389668, + "version": 1, + "versionNonce": 1171936549, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00509-2420", + "type": "text", + "x": 3728.0, + "y": 1700.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 480704553, + "version": 1, + "versionNonce": 2096635357, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00510-9056", + "type": "text", + "x": 3704.0, + "y": 2326.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1615369727, + "version": 1, + "versionNonce": 745251070, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00511-5137", + "type": "text", + "x": 3704.0, + "y": 2352.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1823868379, + "version": 1, + "versionNonce": 1050462843, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00512-9815", + "type": "rectangle", + "x": 3890.0, + "y": 1480.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 302045654, + "version": 1, + "versionNonce": 648983536, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00513-6256", + "type": "text", + "x": 3920.0, + "y": 1498.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1324179997, + "version": 1, + "versionNonce": 1617935925, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Standup digest copy", + "originalText": "Standup digest copy", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00514-2995", + "type": "text", + "x": 3920.0, + "y": 1529.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1499233681, + "version": 1, + "versionNonce": 2037723742, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "originalText": "Current draft \u00b7 revision 1 \u00b7 Saved just now", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00515-1650", + "type": "rectangle", + "x": 4610.0, + "y": 1504.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1993396562, + "version": 1, + "versionNonce": 1291552109, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00516-8686", + "type": "text", + "x": 4618.0, + "y": 1513.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 2047653892, + "version": 1, + "versionNonce": 1485901770, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00517-5899", + "type": "rectangle", + "x": 4692.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 2091871812, + "version": 1, + "versionNonce": 1865289533, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00518-5391", + "type": "text", + "x": 4700.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 397901424, + "version": 1, + "versionNonce": 1559987500, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00519-1489", + "type": "rectangle", + "x": 4794.0, + "y": 1504.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1420860191, + "version": 1, + "versionNonce": 315094120, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00520-5452", + "type": "text", + "x": 4802.0, + "y": 1513.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 602156002, + "version": 1, + "versionNonce": 1958438664, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00521-4830", + "type": "rectangle", + "x": 4896.0, + "y": 1504.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1354937448, + "version": 1, + "versionNonce": 74933065, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00522-8477", + "type": "text", + "x": 4904.0, + "y": 1513.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 504762185, + "version": 1, + "versionNonce": 1279662512, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00523-6450", + "type": "rectangle", + "x": 4988.0, + "y": 1504.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1901702757, + "version": 1, + "versionNonce": 1048416333, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00524-8015", + "type": "text", + "x": 4996.0, + "y": 1513.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1584852074, + "version": 1, + "versionNonce": 986627134, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00525-7260", + "type": "rectangle", + "x": 3900.0, + "y": 1572.0, + "width": 1210.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 213177491, + "version": 1, + "versionNonce": 486439660, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00526-6797", + "type": "line", + "x": 3980.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 389912412, + "version": 1, + "versionNonce": 446990868, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00527-1965", + "type": "line", + "x": 4100.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1702639914, + "version": 1, + "versionNonce": 1750772127, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00528-6675", + "type": "line", + "x": 4220.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1954427704, + "version": 1, + "versionNonce": 1381922415, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00529-7564", + "type": "line", + "x": 4340.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 357567464, + "version": 1, + "versionNonce": 721517804, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00530-6028", + "type": "line", + "x": 4460.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 700799571, + "version": 1, + "versionNonce": 962551275, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00531-6008", + "type": "line", + "x": 4580.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 670800506, + "version": 1, + "versionNonce": 1057176836, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00532-3279", + "type": "line", + "x": 4700.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 649671098, + "version": 1, + "versionNonce": 121188035, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00533-1125", + "type": "line", + "x": 4820.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1584205375, + "version": 1, + "versionNonce": 1974068546, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00534-6559", + "type": "line", + "x": 4940.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 67846565, + "version": 1, + "versionNonce": 1422612681, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00535-4204", + "type": "line", + "x": 5060.0, + "y": 1572.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 2002138020, + "version": 1, + "versionNonce": 366471899, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00536-1153", + "type": "line", + "x": 3900.0, + "y": 1652.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 54308665, + "version": 1, + "versionNonce": 1457200466, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00537-6206", + "type": "line", + "x": 3900.0, + "y": 1772.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1846780993, + "version": 1, + "versionNonce": 103213386, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00538-6951", + "type": "line", + "x": 3900.0, + "y": 1892.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1958819957, + "version": 1, + "versionNonce": 76138036, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00539-6205", + "type": "line", + "x": 3900.0, + "y": 2012.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 325532565, + "version": 1, + "versionNonce": 695414663, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00540-9039", + "type": "line", + "x": 3900.0, + "y": 2132.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1375210318, + "version": 1, + "versionNonce": 604281084, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00541-9956", + "type": "line", + "x": 3900.0, + "y": 2252.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1316341297, + "version": 1, + "versionNonce": 858486099, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00542-3627", + "type": "line", + "x": 3900.0, + "y": 2372.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1146529667, + "version": 1, + "versionNonce": 1789878911, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00543-4744", + "type": "rectangle", + "x": 3916.0, + "y": 1588.0, + "width": 187.79999999999998, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 295580701, + "version": 1, + "versionNonce": 1187394902, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00544-6569", + "type": "text", + "x": 3926.0, + "y": 1594.0, + "width": 167.79999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1021866757, + "version": 1, + "versionNonce": 452667072, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Template", + "originalText": "Created from Template", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "a-00545-2651", + "type": "arrow", + "x": 4158.0, + "y": 1744.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 2064985107, + "version": 1, + "versionNonce": 11266225, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00546-4084", + "type": "arrow", + "x": 4406.0, + "y": 1744.0, + "width": 34.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1278195399, + "version": 1, + "versionNonce": 1073963741, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 34.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00547-6394", + "type": "arrow", + "x": 4654.0, + "y": 1744.0, + "width": 214.0, + "height": 236.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 269929932, + "version": 1, + "versionNonce": 773525106, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -214.0, + 236.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r-00548-5270", + "type": "rectangle", + "x": 3944.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1659994984, + "version": 1, + "versionNonce": 652166569, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00549-3885", + "type": "rectangle", + "x": 3944.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1585045107, + "version": 1, + "versionNonce": 1854066836, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00550-7196", + "type": "ellipse", + "x": 3938.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 790591997, + "version": 1, + "versionNonce": 1358778253, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00551-3693", + "type": "ellipse", + "x": 4152.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#6941c6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 616519725, + "version": 1, + "versionNonce": 1668155666, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00552-1396", + "type": "rectangle", + "x": 3958.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 134673925, + "version": 1, + "versionNonce": 88651768, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00553-1046", + "type": "text", + "x": 3958.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 703087946, + "version": 1, + "versionNonce": 713543490, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "T", + "originalText": "T", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00554-4252", + "type": "text", + "x": 4004.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1181732128, + "version": 1, + "versionNonce": 1082862052, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule every weekday", + "originalText": "Schedule every weekday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00555-5468", + "type": "text", + "x": 4004.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1388780674, + "version": 1, + "versionNonce": 1235523764, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00556-7371", + "type": "line", + "x": 3956.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1956863056, + "version": 1, + "versionNonce": 875601681, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00557-8914", + "type": "text", + "x": 3958.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1033267833, + "version": 1, + "versionNonce": 1154085775, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00558-5488", + "type": "rectangle", + "x": 4192.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1855843825, + "version": 1, + "versionNonce": 234716122, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00559-4857", + "type": "rectangle", + "x": 4192.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 833133788, + "version": 1, + "versionNonce": 147176378, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00560-7541", + "type": "ellipse", + "x": 4186.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 810676266, + "version": 1, + "versionNonce": 1404609099, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00561-9634", + "type": "ellipse", + "x": 4400.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 2060259672, + "version": 1, + "versionNonce": 450662015, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00562-7183", + "type": "rectangle", + "x": 4206.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 496691920, + "version": 1, + "versionNonce": 920159656, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00563-6321", + "type": "text", + "x": 4206.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 386508028, + "version": 1, + "versionNonce": 1385710002, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00564-2249", + "type": "text", + "x": 4252.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1805018423, + "version": 1, + "versionNonce": 1813340642, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Collect contributor updates", + "originalText": "Collect contributor updates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00565-6661", + "type": "text", + "x": 4252.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 555689753, + "version": 1, + "versionNonce": 496194664, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark messages", + "originalText": "Lark messages", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00566-2127", + "type": "line", + "x": 4204.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 470541522, + "version": 1, + "versionNonce": 973754534, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00567-1911", + "type": "text", + "x": 4206.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 957680163, + "version": 1, + "versionNonce": 1653296336, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00568-9041", + "type": "rectangle", + "x": 4440.0, + "y": 1684.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 631735678, + "version": 1, + "versionNonce": 545648037, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00569-9696", + "type": "rectangle", + "x": 4440.0, + "y": 1684.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1643719722, + "version": 1, + "versionNonce": 1044977644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00570-3014", + "type": "ellipse", + "x": 4434.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1669564179, + "version": 1, + "versionNonce": 1259848295, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00571-4383", + "type": "ellipse", + "x": 4648.0, + "y": 1738.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 841662619, + "version": 1, + "versionNonce": 1733074358, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00572-1616", + "type": "rectangle", + "x": 4454.0, + "y": 1698.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 440910706, + "version": 1, + "versionNonce": 1970073991, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00573-4747", + "type": "text", + "x": 4454.0, + "y": 1707.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 332335135, + "version": 1, + "versionNonce": 1179587402, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00574-8409", + "type": "text", + "x": 4500.0, + "y": 1697.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 780181255, + "version": 1, + "versionNonce": 971194053, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft standup digest", + "originalText": "Draft standup digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00575-9688", + "type": "text", + "x": 4500.0, + "y": 1723.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1200460183, + "version": 1, + "versionNonce": 642197253, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00576-8477", + "type": "line", + "x": 4452.0, + "y": 1746.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 149300529, + "version": 1, + "versionNonce": 865281785, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00577-9523", + "type": "text", + "x": 4454.0, + "y": 1760.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 895949961, + "version": 1, + "versionNonce": 1804729281, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Group updates into completed, planned, and blocked.", + "originalText": "Group updates into completed, planned, and blocked.", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00578-4135", + "type": "rectangle", + "x": 4440.0, + "y": 1920.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1887236953, + "version": 1, + "versionNonce": 689768362, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00579-7435", + "type": "rectangle", + "x": 4440.0, + "y": 1920.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1960400721, + "version": 1, + "versionNonce": 1929740314, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00580-7801", + "type": "ellipse", + "x": 4434.0, + "y": 1974.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1726870499, + "version": 1, + "versionNonce": 1957094805, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00581-8753", + "type": "ellipse", + "x": 4648.0, + "y": 1974.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1649220325, + "version": 1, + "versionNonce": 303547821, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00582-2231", + "type": "rectangle", + "x": 4454.0, + "y": 1934.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1908750776, + "version": 1, + "versionNonce": 1953786600, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00583-2416", + "type": "text", + "x": 4454.0, + "y": 1943.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 539535662, + "version": 1, + "versionNonce": 426552255, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00584-6318", + "type": "text", + "x": 4500.0, + "y": 1933.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 192529532, + "version": 1, + "versionNonce": 1924122947, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Post standup digest", + "originalText": "Post standup digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00585-4294", + "type": "text", + "x": 4500.0, + "y": 1959.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 2016805091, + "version": 1, + "versionNonce": 675891455, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00586-5611", + "type": "line", + "x": 4452.0, + "y": 1982.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1773474006, + "version": 1, + "versionNonce": 1488189502, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00587-8138", + "type": "text", + "x": 4454.0, + "y": 1996.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 386975751, + "version": 1, + "versionNonce": 1419569263, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00588-8647", + "type": "rectangle", + "x": 3916.0, + "y": 2262.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 798988729, + "version": 1, + "versionNonce": 1216325153, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00589-9535", + "type": "text", + "x": 3924.0, + "y": 2271.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 713338221, + "version": 1, + "versionNonce": 1854319591, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00590-7903", + "type": "rectangle", + "x": 3916.0, + "y": 2298.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 522809342, + "version": 1, + "versionNonce": 48214625, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00591-8586", + "type": "text", + "x": 3924.0, + "y": 2307.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 738394344, + "version": 1, + "versionNonce": 486059083, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00592-2828", + "type": "rectangle", + "x": 3916.0, + "y": 2334.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 155554173, + "version": 1, + "versionNonce": 15579895, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00593-8271", + "type": "text", + "x": 3924.0, + "y": 2343.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 974369482, + "version": 1, + "versionNonce": 419794290, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00594-6875", + "type": "rectangle", + "x": 4556.0, + "y": 2286.0, + "width": 138.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1311360359, + "version": 1, + "versionNonce": 1920727665, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00595-8356", + "type": "rectangle", + "x": 4566.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 172972468, + "version": 1, + "versionNonce": 124692797, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00596-6567", + "type": "rectangle", + "x": 4606.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1929504929, + "version": 1, + "versionNonce": 1439612762, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00597-8461", + "type": "rectangle", + "x": 4646.0, + "y": 2302.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1963318816, + "version": 1, + "versionNonce": 724971308, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00598-8636", + "type": "rectangle", + "x": 4566.0, + "y": 2330.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 388520946, + "version": 1, + "versionNonce": 1786284098, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00599-3996", + "type": "rectangle", + "x": 4720.0, + "y": 1572.0, + "width": 390.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#e5e7eb", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1496277612, + "version": 1, + "versionNonce": 498707589, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00600-9761", + "type": "text", + "x": 4740.0, + "y": 1590.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 446073869, + "version": 1, + "versionNonce": 97658155, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Node configuration", + "originalText": "Node configuration", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00601-5542", + "type": "text", + "x": 4740.0, + "y": 1620.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 528703378, + "version": 1, + "versionNonce": 505295562, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft standup digest", + "originalText": "Draft standup digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00602-5607", + "type": "text", + "x": 5064.0, + "y": 1589.0, + "width": 24.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1392839513, + "version": 1, + "versionNonce": 264196685, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x", + "originalText": "x", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00603-7533", + "type": "line", + "x": 4720.0, + "y": 1648.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1757016018, + "version": 1, + "versionNonce": 366308299, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00604-9756", + "type": "text", + "x": 4740.0, + "y": 1672.0, + "width": 71.82, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 282852408, + "version": 1, + "versionNonce": 758647785, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NODE NAME", + "originalText": "NODE NAME", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00605-5746", + "type": "rectangle", + "x": 4740.0, + "y": 1694.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1287028775, + "version": 1, + "versionNonce": 17715181, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00606-3310", + "type": "text", + "x": 4752.0, + "y": 1705.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1067327293, + "version": 1, + "versionNonce": 1276057975, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft standup digest", + "originalText": "Draft standup digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00607-1543", + "type": "text", + "x": 4740.0, + "y": 1760.0, + "width": 31.919999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1752171904, + "version": 1, + "versionNonce": 1237918507, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TYPE", + "originalText": "TYPE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00608-6245", + "type": "rectangle", + "x": 4740.0, + "y": 1782.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 539171088, + "version": 1, + "versionNonce": 1486646985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00609-1921", + "type": "text", + "x": 4752.0, + "y": 1793.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1321631672, + "version": 1, + "versionNonce": 1113997790, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00610-8938", + "type": "text", + "x": 4740.0, + "y": 1848.0, + "width": 87.77999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1397446847, + "version": 1, + "versionNonce": 1040708949, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "INSTRUCTION", + "originalText": "INSTRUCTION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00611-4773", + "type": "rectangle", + "x": 4740.0, + "y": 1870.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 631037415, + "version": 1, + "versionNonce": 64135026, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00612-2557", + "type": "text", + "x": 4752.0, + "y": 1881.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1124744145, + "version": 1, + "versionNonce": 1399709537, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Group updates into completed, planned, and blocked.", + "originalText": "Group updates into completed, planned, and blocked.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00613-1305", + "type": "text", + "x": 4740.0, + "y": 1936.0, + "width": 31.919999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1943106480, + "version": 1, + "versionNonce": 1725508972, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TONE", + "originalText": "TONE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00614-8273", + "type": "rectangle", + "x": 4740.0, + "y": 1958.0, + "width": 350.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1394505852, + "version": 1, + "versionNonce": 795661565, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00615-9589", + "type": "text", + "x": 4752.0, + "y": 1969.0, + "width": 326.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1441801691, + "version": 1, + "versionNonce": 1229826638, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Concise", + "originalText": "Concise", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00616-3663", + "type": "line", + "x": 4720.0, + "y": 2328.0, + "width": 390.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 520687506, + "version": 1, + "versionNonce": 1675645074, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 390.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00617-5680", + "type": "rectangle", + "x": 4914.0, + "y": 2342.0, + "width": 78.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1987390011, + "version": 1, + "versionNonce": 259098794, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00618-9233", + "type": "text", + "x": 4922.0, + "y": 2351.0, + "width": 62.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 265786387, + "version": 1, + "versionNonce": 1380529045, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Delete", + "originalText": "Delete", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00619-5489", + "type": "rectangle", + "x": 5002.0, + "y": 2342.0, + "width": 88.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": { + "type": 3 + }, + "seed": 1599552097, + "version": 1, + "versionNonce": 1699680222, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00620-9055", + "type": "text", + "x": 5010.0, + "y": 2351.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-05" + ], + "frameId": "frame-00497-1095", + "roundness": null, + "seed": 1902297582, + "version": 1, + "versionNonce": 102090287, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Apply", + "originalText": "Apply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00621-6168", + "type": "frame", + "x": 160.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1335595176, + "version": 1, + "versionNonce": 1982957977, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "07 Run - unified execution dialog" + }, + { + "id": "r-00623-3774", + "type": "rectangle", + "x": 160.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 264703568, + "version": 1, + "versionNonce": 512641121, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00624-2684", + "type": "rectangle", + "x": 160.0, + "y": 2660.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1835520340, + "version": 1, + "versionNonce": 1430948710, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00625-5229", + "type": "text", + "x": 184.0, + "y": 2684.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 748051209, + "version": 1, + "versionNonce": 1857717936, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00626-6185", + "type": "text", + "x": 184.0, + "y": 2713.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 895057130, + "version": 1, + "versionNonce": 871166264, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00627-9394", + "type": "rectangle", + "x": 172.0, + "y": 2768.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1263755682, + "version": 1, + "versionNonce": 1629150105, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00628-3669", + "type": "ellipse", + "x": 188.0, + "y": 2780.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1436962452, + "version": 1, + "versionNonce": 553795908, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00629-4663", + "type": "text", + "x": 208.0, + "y": 2776.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1845037515, + "version": 1, + "versionNonce": 577200592, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00630-1782", + "type": "ellipse", + "x": 188.0, + "y": 2832.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1788926571, + "version": 1, + "versionNonce": 829065470, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00631-3758", + "type": "text", + "x": 208.0, + "y": 2828.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1289547133, + "version": 1, + "versionNonce": 1135084330, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00632-6707", + "type": "ellipse", + "x": 188.0, + "y": 2884.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1863333264, + "version": 1, + "versionNonce": 1943284102, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00633-4567", + "type": "text", + "x": 208.0, + "y": 2880.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1369666237, + "version": 1, + "versionNonce": 1082240096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00634-5762", + "type": "text", + "x": 184.0, + "y": 3506.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1858150985, + "version": 1, + "versionNonce": 1352858898, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00635-7427", + "type": "text", + "x": 184.0, + "y": 3532.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1209760612, + "version": 1, + "versionNonce": 651814990, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00636-7123", + "type": "rectangle", + "x": 370.0, + "y": 2660.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1098956797, + "version": 1, + "versionNonce": 77499511, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00637-5661", + "type": "text", + "x": 400.0, + "y": 2678.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 338743391, + "version": 1, + "versionNonce": 1523752307, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00638-1203", + "type": "text", + "x": 400.0, + "y": 2709.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 2120049584, + "version": 1, + "versionNonce": 1233321378, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft revision 12 | Saved just now", + "originalText": "Draft revision 12 | Saved just now", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00639-5220", + "type": "rectangle", + "x": 1090.0, + "y": 2684.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 457269733, + "version": 1, + "versionNonce": 1794181280, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00640-7071", + "type": "text", + "x": 1098.0, + "y": 2693.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 593012798, + "version": 1, + "versionNonce": 1351448565, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00641-7279", + "type": "rectangle", + "x": 1172.0, + "y": 2684.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1022752697, + "version": 1, + "versionNonce": 1487218531, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00642-6153", + "type": "text", + "x": 1180.0, + "y": 2693.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1148329110, + "version": 1, + "versionNonce": 1872175802, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00643-9698", + "type": "rectangle", + "x": 1274.0, + "y": 2684.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 553342051, + "version": 1, + "versionNonce": 568013523, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00644-8967", + "type": "text", + "x": 1282.0, + "y": 2693.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 581638745, + "version": 1, + "versionNonce": 1945938699, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00645-4266", + "type": "rectangle", + "x": 1376.0, + "y": 2684.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2145146059, + "version": 1, + "versionNonce": 1713648253, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00646-8193", + "type": "text", + "x": 1384.0, + "y": 2693.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1434098424, + "version": 1, + "versionNonce": 1426648699, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00647-6525", + "type": "rectangle", + "x": 1468.0, + "y": 2684.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 16639806, + "version": 1, + "versionNonce": 1244303036, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00648-3443", + "type": "text", + "x": 1476.0, + "y": 2693.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 84444767, + "version": 1, + "versionNonce": 1185344103, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00649-5261", + "type": "rectangle", + "x": 380.0, + "y": 2752.0, + "width": 1210.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 371201612, + "version": 1, + "versionNonce": 1783128515, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00650-2404", + "type": "line", + "x": 460.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1874521093, + "version": 1, + "versionNonce": 1860623560, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00651-4455", + "type": "line", + "x": 580.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 519001862, + "version": 1, + "versionNonce": 343290136, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00652-3810", + "type": "line", + "x": 700.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 2043590355, + "version": 1, + "versionNonce": 287229034, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00653-6359", + "type": "line", + "x": 820.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1191520855, + "version": 1, + "versionNonce": 959746715, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00654-1950", + "type": "line", + "x": 940.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 333080134, + "version": 1, + "versionNonce": 305507027, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00655-6460", + "type": "line", + "x": 1060.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 285147285, + "version": 1, + "versionNonce": 21692403, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00656-5362", + "type": "line", + "x": 1180.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 293326957, + "version": 1, + "versionNonce": 1704114958, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00657-3534", + "type": "line", + "x": 1300.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 13335880, + "version": 1, + "versionNonce": 1740160025, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00658-1199", + "type": "line", + "x": 1420.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1923443264, + "version": 1, + "versionNonce": 519190335, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00659-1531", + "type": "line", + "x": 1540.0, + "y": 2752.0, + "width": 0.0, + "height": 818.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 374796352, + "version": 1, + "versionNonce": 632379758, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 818.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00660-8799", + "type": "line", + "x": 380.0, + "y": 2832.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1384175795, + "version": 1, + "versionNonce": 269411512, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00661-8116", + "type": "line", + "x": 380.0, + "y": 2952.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 698916087, + "version": 1, + "versionNonce": 630539690, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00662-9600", + "type": "line", + "x": 380.0, + "y": 3072.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1378573391, + "version": 1, + "versionNonce": 689890238, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00663-8992", + "type": "line", + "x": 380.0, + "y": 3192.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1680944557, + "version": 1, + "versionNonce": 107411811, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00664-3488", + "type": "line", + "x": 380.0, + "y": 3312.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 118936427, + "version": 1, + "versionNonce": 358490931, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00665-1615", + "type": "line", + "x": 380.0, + "y": 3432.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 764295867, + "version": 1, + "versionNonce": 140577477, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00666-6627", + "type": "line", + "x": 380.0, + "y": 3552.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 945227285, + "version": 1, + "versionNonce": 705895750, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00667-5192", + "type": "rectangle", + "x": 396.0, + "y": 2768.0, + "width": 211.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1351934798, + "version": 1, + "versionNonce": 823020715, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00668-9010", + "type": "text", + "x": 406.0, + "y": 2774.0, + "width": 191.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 351823424, + "version": 1, + "versionNonce": 627091793, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Description", + "originalText": "Created from Description", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "a-00669-5641", + "type": "arrow", + "x": 622.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1735411040, + "version": 1, + "versionNonce": 1234830506, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00670-4193", + "type": "arrow", + "x": 857.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1238020008, + "version": 1, + "versionNonce": 1453343242, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00671-3682", + "type": "arrow", + "x": 1092.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 966878207, + "version": 1, + "versionNonce": 535674304, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00672-8935", + "type": "arrow", + "x": 1327.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1996301009, + "version": 1, + "versionNonce": 1945497720, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r-00673-1090", + "type": "rectangle", + "x": 408.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1539642225, + "version": 1, + "versionNonce": 286520743, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00674-3610", + "type": "rectangle", + "x": 408.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 525600533, + "version": 1, + "versionNonce": 1301250791, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00675-1938", + "type": "ellipse", + "x": 402.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1626280680, + "version": 1, + "versionNonce": 1937840531, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00676-1672", + "type": "ellipse", + "x": 616.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1355541774, + "version": 1, + "versionNonce": 1232653855, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00677-1905", + "type": "rectangle", + "x": 422.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1965882266, + "version": 1, + "versionNonce": 384896288, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00678-9426", + "type": "text", + "x": 422.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 502185270, + "version": 1, + "versionNonce": 2006344122, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00679-2210", + "type": "text", + "x": 468.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 2140444339, + "version": 1, + "versionNonce": 679230138, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Receive conversation", + "originalText": "Receive conversation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00680-9798", + "type": "text", + "x": 468.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 583819061, + "version": 1, + "versionNonce": 1437084906, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00681-9967", + "type": "line", + "x": 420.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 492486786, + "version": 1, + "versionNonce": 862385765, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00682-6166", + "type": "text", + "x": 422.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 400440906, + "version": 1, + "versionNonce": 940949837, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00683-7189", + "type": "rectangle", + "x": 643.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 86818184, + "version": 1, + "versionNonce": 1172330759, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00684-1114", + "type": "rectangle", + "x": 643.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 613632002, + "version": 1, + "versionNonce": 346351460, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00685-2072", + "type": "ellipse", + "x": 637.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 947813562, + "version": 1, + "versionNonce": 1331074750, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00686-7668", + "type": "ellipse", + "x": 851.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1684457035, + "version": 1, + "versionNonce": 717980958, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00687-9606", + "type": "rectangle", + "x": 657.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 182823325, + "version": 1, + "versionNonce": 1357675811, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00688-9174", + "type": "text", + "x": 657.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 359048982, + "version": 1, + "versionNonce": 1248019457, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00689-4030", + "type": "text", + "x": 703.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 2054115972, + "version": 1, + "versionNonce": 555545128, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Classify severity", + "originalText": "Classify severity", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00690-5924", + "type": "text", + "x": 703.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1626068460, + "version": 1, + "versionNonce": 675749125, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00691-3495", + "type": "line", + "x": 655.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1493913794, + "version": 1, + "versionNonce": 59876336, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00692-2631", + "type": "text", + "x": 657.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1201761537, + "version": 1, + "versionNonce": 1858395643, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00693-3250", + "type": "rectangle", + "x": 878.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2116145164, + "version": 1, + "versionNonce": 1692680107, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00694-9569", + "type": "rectangle", + "x": 878.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1719656967, + "version": 1, + "versionNonce": 2012532251, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00695-1919", + "type": "ellipse", + "x": 872.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1973920380, + "version": 1, + "versionNonce": 1563358155, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00696-2701", + "type": "ellipse", + "x": 1086.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 152893008, + "version": 1, + "versionNonce": 944168241, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00697-7256", + "type": "rectangle", + "x": 892.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1301149965, + "version": 1, + "versionNonce": 723963251, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00698-1581", + "type": "text", + "x": 892.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1672094018, + "version": 1, + "versionNonce": 798425033, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "C", + "originalText": "C", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00699-1871", + "type": "text", + "x": 938.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1412979856, + "version": 1, + "versionNonce": 874176475, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Route escalation", + "originalText": "Route escalation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00700-7667", + "type": "text", + "x": 938.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 369161676, + "version": 1, + "versionNonce": 519918366, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Condition", + "originalText": "Condition", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00701-9245", + "type": "line", + "x": 890.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 875497073, + "version": 1, + "versionNonce": 529460830, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00702-1858", + "type": "text", + "x": 892.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1965460775, + "version": 1, + "versionNonce": 1105400250, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00703-3881", + "type": "rectangle", + "x": 1113.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1786578219, + "version": 1, + "versionNonce": 1842522189, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00704-4074", + "type": "rectangle", + "x": 1113.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1948663373, + "version": 1, + "versionNonce": 833867633, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00705-9213", + "type": "ellipse", + "x": 1107.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 987530962, + "version": 1, + "versionNonce": 867024397, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00706-1126", + "type": "ellipse", + "x": 1321.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 927424134, + "version": 1, + "versionNonce": 574305206, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00707-4155", + "type": "rectangle", + "x": 1127.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1675113547, + "version": 1, + "versionNonce": 1445008074, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00708-2587", + "type": "text", + "x": 1127.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1599187897, + "version": 1, + "versionNonce": 314196333, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00709-6859", + "type": "text", + "x": 1173.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 256577242, + "version": 1, + "versionNonce": 200733866, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Prepare reply", + "originalText": "Prepare reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00710-5881", + "type": "text", + "x": 1173.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 201967757, + "version": 1, + "versionNonce": 1847098056, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00711-6665", + "type": "line", + "x": 1125.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 253542094, + "version": 1, + "versionNonce": 1411892624, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00712-3652", + "type": "text", + "x": 1127.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 210866144, + "version": 1, + "versionNonce": 2079951145, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00713-1364", + "type": "rectangle", + "x": 1348.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 791665051, + "version": 1, + "versionNonce": 260983099, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00714-9589", + "type": "rectangle", + "x": 1348.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1615085635, + "version": 1, + "versionNonce": 2084886188, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00715-4141", + "type": "ellipse", + "x": 1342.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1445676767, + "version": 1, + "versionNonce": 1454422789, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00716-2356", + "type": "ellipse", + "x": 1556.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1134807647, + "version": 1, + "versionNonce": 2124789795, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00717-4412", + "type": "rectangle", + "x": 1362.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2096433420, + "version": 1, + "versionNonce": 1894162501, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00718-9440", + "type": "text", + "x": 1362.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1276634921, + "version": 1, + "versionNonce": 1749450876, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00719-5053", + "type": "text", + "x": 1408.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 289684735, + "version": 1, + "versionNonce": 1651903209, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Post response", + "originalText": "Post response", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00720-7418", + "type": "text", + "x": 1408.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 942779454, + "version": 1, + "versionNonce": 1154029419, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00721-6023", + "type": "line", + "x": 1360.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1041459920, + "version": 1, + "versionNonce": 1113836228, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00722-1257", + "type": "text", + "x": 1362.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1038593907, + "version": 1, + "versionNonce": 1420960356, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00723-1233", + "type": "rectangle", + "x": 396.0, + "y": 3442.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 134996035, + "version": 1, + "versionNonce": 1822806514, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00724-8702", + "type": "text", + "x": 404.0, + "y": 3451.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1418456198, + "version": 1, + "versionNonce": 1202473603, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00725-8016", + "type": "rectangle", + "x": 396.0, + "y": 3478.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 863436371, + "version": 1, + "versionNonce": 43457973, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00726-6165", + "type": "text", + "x": 404.0, + "y": 3487.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 231310837, + "version": 1, + "versionNonce": 1587757779, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00727-2929", + "type": "rectangle", + "x": 396.0, + "y": 3514.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 455863319, + "version": 1, + "versionNonce": 1103653178, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00728-6623", + "type": "text", + "x": 404.0, + "y": 3523.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 129363551, + "version": 1, + "versionNonce": 887085750, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00729-6442", + "type": "rectangle", + "x": 1436.0, + "y": 3466.0, + "width": 138.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2061332793, + "version": 1, + "versionNonce": 826210874, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00730-8612", + "type": "rectangle", + "x": 1446.0, + "y": 3482.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1374878400, + "version": 1, + "versionNonce": 649041325, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00731-1237", + "type": "rectangle", + "x": 1486.0, + "y": 3482.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1800758125, + "version": 1, + "versionNonce": 1446991354, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00732-5043", + "type": "rectangle", + "x": 1526.0, + "y": 3482.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2044732385, + "version": 1, + "versionNonce": 1060056474, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00733-1511", + "type": "rectangle", + "x": 1446.0, + "y": 3510.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 260625986, + "version": 1, + "versionNonce": 885092522, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00734-7690", + "type": "rectangle", + "x": 1486.0, + "y": 3510.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1181948538, + "version": 1, + "versionNonce": 1307051156, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00735-1838", + "type": "rectangle", + "x": 630.0, + "y": 2780.0, + "width": 710.0, + "height": 720.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1195675598, + "version": 1, + "versionNonce": 784888001, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00736-4398", + "type": "text", + "x": 658.0, + "y": 2806.0, + "width": 450.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 7377640, + "version": 1, + "versionNonce": 1113225476, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run workflow", + "originalText": "Run workflow", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00737-2704", + "type": "text", + "x": 658.0, + "y": 2842.0, + "width": 520.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 68989324, + "version": 1, + "versionNonce": 1211023632, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Confirm the revision, inputs, and external effects.", + "originalText": "Confirm the revision, inputs, and external effects.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00738-1328", + "type": "rectangle", + "x": 658.0, + "y": 2882.0, + "width": 125.39999999999999, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 671020051, + "version": 1, + "versionNonce": 2119410364, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00739-1600", + "type": "text", + "x": 668.0, + "y": 2888.0, + "width": 105.39999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 982501393, + "version": 1, + "versionNonce": 587161097, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft", + "originalText": "Current draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00740-9432", + "type": "text", + "x": 784.0, + "y": 2888.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1386488965, + "version": 1, + "versionNonce": 182697192, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 12", + "originalText": "revision 12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00741-6903", + "type": "line", + "x": 658.0, + "y": 2924.0, + "width": 654.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1813448233, + "version": 1, + "versionNonce": 1961986914, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 654.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00742-6345", + "type": "text", + "x": 658.0, + "y": 2948.0, + "width": 63.839999999999996, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1286714496, + "version": 1, + "versionNonce": 845626787, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WORKFLOW", + "originalText": "WORKFLOW", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00743-6351", + "type": "rectangle", + "x": 658.0, + "y": 2970.0, + "width": 654.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 709458413, + "version": 1, + "versionNonce": 2091317450, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00744-8759", + "type": "text", + "x": 670.0, + "y": 2981.0, + "width": 630.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1598937951, + "version": 1, + "versionNonce": 1791343393, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00745-7355", + "type": "text", + "x": 658.0, + "y": 3032.0, + "width": 95.75999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 738818174, + "version": 1, + "versionNonce": 740359722, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "CONVERSATION", + "originalText": "CONVERSATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00746-4062", + "type": "rectangle", + "x": 658.0, + "y": 3054.0, + "width": 654.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 600644620, + "version": 1, + "versionNonce": 1113677480, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00747-3824", + "type": "text", + "x": 670.0, + "y": 3065.0, + "width": 630.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 354765155, + "version": 1, + "versionNonce": 1994811971, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "support-thread-4821", + "originalText": "support-thread-4821", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00748-6076", + "type": "text", + "x": 658.0, + "y": 3116.0, + "width": 87.77999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1524890806, + "version": 1, + "versionNonce": 463119808, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ENVIRONMENT", + "originalText": "ENVIRONMENT", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00749-9588", + "type": "rectangle", + "x": 658.0, + "y": 3138.0, + "width": 320.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 252548668, + "version": 1, + "versionNonce": 1429960973, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00750-6517", + "type": "text", + "x": 670.0, + "y": 3149.0, + "width": 296.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 208146282, + "version": 1, + "versionNonce": 1093961504, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Production", + "originalText": "Production", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00751-6718", + "type": "text", + "x": 992.0, + "y": 3116.0, + "width": 87.77999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 32730803, + "version": 1, + "versionNonce": 1707520601, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "CONNECTIONS", + "originalText": "CONNECTIONS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00752-7440", + "type": "rectangle", + "x": 992.0, + "y": 3138.0, + "width": 320.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 204754046, + "version": 1, + "versionNonce": 1116675910, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00753-2540", + "type": "text", + "x": 1004.0, + "y": 3149.0, + "width": 296.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 954446614, + "version": 1, + "versionNonce": 700351423, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2 ready", + "originalText": "2 ready", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00754-4736", + "type": "rectangle", + "x": 658.0, + "y": 3210.0, + "width": 654.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 93217028, + "version": 1, + "versionNonce": 2025770482, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00755-6592", + "type": "text", + "x": 676.0, + "y": 3228.0, + "width": 180.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1750698218, + "version": 1, + "versionNonce": 1784082183, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "External effects", + "originalText": "External effects", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00756-7158", + "type": "text", + "x": 676.0, + "y": 3257.0, + "width": 560.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 316575485, + "version": 1, + "versionNonce": 1374309769, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "May post a Lark reply and create a Linear issue.", + "originalText": "May post a Lark reply and create a Linear issue.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00757-4995", + "type": "rectangle", + "x": 658.0, + "y": 3316.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1359610764, + "version": 1, + "versionNonce": 1155938014, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00758-3038", + "type": "text", + "x": 662.0, + "y": 3317.0, + "width": 12.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1287824553, + "version": 1, + "versionNonce": 2035235317, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x", + "originalText": "x", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00759-2587", + "type": "text", + "x": 688.0, + "y": 3316.0, + "width": 590.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 23413047, + "version": 1, + "versionNonce": 593190923, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I understand this workflow may change external systems.", + "originalText": "I understand this workflow may change external systems.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00760-5097", + "type": "rectangle", + "x": 658.0, + "y": 3354.0, + "width": 654.0, + "height": 50.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 1793560371, + "version": 1, + "versionNonce": 704073180, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00761-8385", + "type": "text", + "x": 676.0, + "y": 3370.0, + "width": 500.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1601891834, + "version": 1, + "versionNonce": 1874437740, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "This Run will be saved in Activity.", + "originalText": "This Run will be saved in Activity.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00762-3987", + "type": "rectangle", + "x": 1108.0, + "y": 3432.0, + "width": 88.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 36841297, + "version": 1, + "versionNonce": 1578175133, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00763-8352", + "type": "text", + "x": 1116.0, + "y": 3441.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 938441835, + "version": 1, + "versionNonce": 581524656, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Cancel", + "originalText": "Cancel", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00764-9108", + "type": "rectangle", + "x": 1208.0, + "y": 3432.0, + "width": 104.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": { + "type": 3 + }, + "seed": 2002249062, + "version": 1, + "versionNonce": 1579862378, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00765-6520", + "type": "text", + "x": 1216.0, + "y": 3441.0, + "width": 88.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-06" + ], + "frameId": "frame-00621-6168", + "roundness": null, + "seed": 1777013436, + "version": 1, + "versionNonce": 1406243362, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00766-9912", + "type": "frame", + "x": 1920.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1167356674, + "version": 1, + "versionNonce": 1202723792, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "08 Running draft - Studio canvas and Run console" + }, + { + "id": "r-00768-7589", + "type": "rectangle", + "x": 1920.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1306734867, + "version": 1, + "versionNonce": 967805843, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00769-7913", + "type": "rectangle", + "x": 1920.0, + "y": 2660.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 266690246, + "version": 1, + "versionNonce": 923504940, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00770-9475", + "type": "text", + "x": 1944.0, + "y": 2684.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 399424586, + "version": 1, + "versionNonce": 1948165667, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00771-6490", + "type": "text", + "x": 1944.0, + "y": 2713.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1959761229, + "version": 1, + "versionNonce": 493527462, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00772-2975", + "type": "rectangle", + "x": 1932.0, + "y": 2768.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 743748905, + "version": 1, + "versionNonce": 1650982753, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00773-5870", + "type": "ellipse", + "x": 1948.0, + "y": 2780.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1882113973, + "version": 1, + "versionNonce": 1012712994, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00774-7670", + "type": "text", + "x": 1968.0, + "y": 2776.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 2048170525, + "version": 1, + "versionNonce": 36018558, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00775-7598", + "type": "ellipse", + "x": 1948.0, + "y": 2832.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1952415864, + "version": 1, + "versionNonce": 437129007, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00776-1669", + "type": "text", + "x": 1968.0, + "y": 2828.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 870477167, + "version": 1, + "versionNonce": 1240460602, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00777-4006", + "type": "ellipse", + "x": 1948.0, + "y": 2884.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1915246364, + "version": 1, + "versionNonce": 342770438, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00778-1503", + "type": "text", + "x": 1968.0, + "y": 2880.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 889364815, + "version": 1, + "versionNonce": 1788776711, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00779-1086", + "type": "text", + "x": 1944.0, + "y": 3506.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1163209318, + "version": 1, + "versionNonce": 1832489162, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00780-7636", + "type": "text", + "x": 1944.0, + "y": 3532.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1129508535, + "version": 1, + "versionNonce": 511673390, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00781-6151", + "type": "rectangle", + "x": 2130.0, + "y": 2660.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 529366411, + "version": 1, + "versionNonce": 603024501, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00782-8559", + "type": "text", + "x": 2160.0, + "y": 2678.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 2008538993, + "version": 1, + "versionNonce": 1109193733, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00783-3546", + "type": "text", + "x": 2160.0, + "y": 2709.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1833606511, + "version": 1, + "versionNonce": 1974571545, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 12 \u00b7 Run R-1042", + "originalText": "Current draft \u00b7 revision 12 \u00b7 Run R-1042", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00784-8223", + "type": "rectangle", + "x": 2850.0, + "y": 2684.0, + "width": 72.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 408188278, + "version": 1, + "versionNonce": 439608098, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00785-8017", + "type": "text", + "x": 2858.0, + "y": 2693.0, + "width": 56.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1767734769, + "version": 1, + "versionNonce": 634476131, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run", + "originalText": "Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00786-6366", + "type": "rectangle", + "x": 2932.0, + "y": 2684.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 645260193, + "version": 1, + "versionNonce": 507675990, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00787-7232", + "type": "text", + "x": 2940.0, + "y": 2693.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1766991975, + "version": 1, + "versionNonce": 1180791775, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Add node", + "originalText": "Add node", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00788-4468", + "type": "rectangle", + "x": 3034.0, + "y": 2684.0, + "width": 92.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 320118522, + "version": 1, + "versionNonce": 1794295374, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00789-3919", + "type": "text", + "x": 3042.0, + "y": 2693.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1414270827, + "version": 1, + "versionNonce": 73519545, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit YAML", + "originalText": "Edit YAML", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00790-5805", + "type": "rectangle", + "x": 3136.0, + "y": 2684.0, + "width": 82.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 381150672, + "version": 1, + "versionNonce": 646318421, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00791-4965", + "type": "text", + "x": 3144.0, + "y": 2693.0, + "width": 66.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 464701986, + "version": 1, + "versionNonce": 533093662, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save", + "originalText": "Save", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00792-9502", + "type": "rectangle", + "x": 3228.0, + "y": 2684.0, + "width": 102.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1422624951, + "version": 1, + "versionNonce": 965535836, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00793-6774", + "type": "text", + "x": 3236.0, + "y": 2693.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1385902500, + "version": 1, + "versionNonce": 1190563522, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Publish", + "originalText": "Publish", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00794-4247", + "type": "rectangle", + "x": 2140.0, + "y": 2752.0, + "width": 1210.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#f7f9fc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 631200014, + "version": 1, + "versionNonce": 1134623848, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-00795-3418", + "type": "line", + "x": 2220.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 468096881, + "version": 1, + "versionNonce": 1803084471, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00796-7800", + "type": "line", + "x": 2340.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1042748181, + "version": 1, + "versionNonce": 433296966, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00797-4351", + "type": "line", + "x": 2460.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1394182147, + "version": 1, + "versionNonce": 292635120, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00798-8512", + "type": "line", + "x": 2580.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 49387112, + "version": 1, + "versionNonce": 1363848487, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00799-1006", + "type": "line", + "x": 2700.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 435866220, + "version": 1, + "versionNonce": 1895743746, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00800-9302", + "type": "line", + "x": 2820.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 10818659, + "version": 1, + "versionNonce": 1422797747, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00801-2417", + "type": "line", + "x": 2940.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1079206377, + "version": 1, + "versionNonce": 1098967171, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00802-7848", + "type": "line", + "x": 3060.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1705221951, + "version": 1, + "versionNonce": 448231308, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00803-4473", + "type": "line", + "x": 3180.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1610657485, + "version": 1, + "versionNonce": 1494761790, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00804-7537", + "type": "line", + "x": 3300.0, + "y": 2752.0, + "width": 0.0, + "height": 550.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1464601018, + "version": 1, + "versionNonce": 1713044849, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 550.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00805-2229", + "type": "line", + "x": 2140.0, + "y": 2832.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 25727634, + "version": 1, + "versionNonce": 1890959823, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00806-9309", + "type": "line", + "x": 2140.0, + "y": 2952.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 784465071, + "version": 1, + "versionNonce": 1855004892, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00807-9532", + "type": "line", + "x": 2140.0, + "y": 3072.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1197933052, + "version": 1, + "versionNonce": 225343614, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "l-00808-2649", + "type": "line", + "x": 2140.0, + "y": 3192.0, + "width": 1210.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 584570659, + "version": 1, + "versionNonce": 133301959, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1210.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-00809-7632", + "type": "rectangle", + "x": 2156.0, + "y": 2768.0, + "width": 211.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "#f4f3ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 682550494, + "version": 1, + "versionNonce": 886501665, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00810-9919", + "type": "text", + "x": 2166.0, + "y": 2774.0, + "width": 191.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 574335559, + "version": 1, + "versionNonce": 1273982100, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Created from Description", + "originalText": "Created from Description", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "a-00811-7000", + "type": "arrow", + "x": 2382.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 2109049260, + "version": 1, + "versionNonce": 1298295333, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00812-7687", + "type": "arrow", + "x": 2617.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1172484149, + "version": 1, + "versionNonce": 276116607, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00813-7677", + "type": "arrow", + "x": 2852.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1394246063, + "version": 1, + "versionNonce": 1055665585, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "a-00814-9393", + "type": "arrow", + "x": 3087.0, + "y": 3072.0, + "width": 21.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#94a3b8", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 591647628, + "version": 1, + "versionNonce": 1453089024, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 21.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r-00815-5550", + "type": "rectangle", + "x": 2168.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 2022419854, + "version": 1, + "versionNonce": 1728531545, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00816-1797", + "type": "rectangle", + "x": 2168.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 343046575, + "version": 1, + "versionNonce": 1007792019, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00817-1334", + "type": "ellipse", + "x": 2162.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1556342876, + "version": 1, + "versionNonce": 344947113, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00818-4264", + "type": "ellipse", + "x": 2376.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 465047116, + "version": 1, + "versionNonce": 959769450, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00819-4463", + "type": "rectangle", + "x": 2182.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1292349539, + "version": 1, + "versionNonce": 750957524, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00820-2002", + "type": "text", + "x": 2182.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1437605211, + "version": 1, + "versionNonce": 1244157040, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00821-5547", + "type": "text", + "x": 2228.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1732536508, + "version": 1, + "versionNonce": 141326564, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Receive conversation", + "originalText": "Receive conversation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00822-8931", + "type": "text", + "x": 2228.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 295020566, + "version": 1, + "versionNonce": 1192557027, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00823-7935", + "type": "line", + "x": 2180.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 80940091, + "version": 1, + "versionNonce": 114546869, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00824-1863", + "type": "text", + "x": 2182.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 782295453, + "version": 1, + "versionNonce": 2059382994, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00825-4369", + "type": "rectangle", + "x": 2292.0, + "y": 3100.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1136338833, + "version": 1, + "versionNonce": 1616116577, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00826-4610", + "type": "text", + "x": 2302.0, + "y": 3106.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1294040740, + "version": 1, + "versionNonce": 701092009, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Completed", + "originalText": "Completed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00827-2525", + "type": "rectangle", + "x": 2403.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 170210790, + "version": 1, + "versionNonce": 42163312, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00828-5347", + "type": "rectangle", + "x": 2403.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 526868154, + "version": 1, + "versionNonce": 1771633455, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00829-4382", + "type": "ellipse", + "x": 2397.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 873741308, + "version": 1, + "versionNonce": 1738539033, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00830-9303", + "type": "ellipse", + "x": 2611.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 420742110, + "version": 1, + "versionNonce": 1233190555, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00831-5950", + "type": "rectangle", + "x": 2417.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 254905881, + "version": 1, + "versionNonce": 2103058197, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00832-5274", + "type": "text", + "x": 2417.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1597993615, + "version": 1, + "versionNonce": 1776329874, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00833-2245", + "type": "text", + "x": 2463.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1125881760, + "version": 1, + "versionNonce": 1020786367, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Classify severity", + "originalText": "Classify severity", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00834-8553", + "type": "text", + "x": 2463.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 777884801, + "version": 1, + "versionNonce": 1130149808, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00835-4216", + "type": "line", + "x": 2415.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 690388516, + "version": 1, + "versionNonce": 556945495, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00836-7197", + "type": "text", + "x": 2417.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 501004562, + "version": 1, + "versionNonce": 980881315, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00837-8675", + "type": "rectangle", + "x": 2527.0, + "y": 3100.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 739461831, + "version": 1, + "versionNonce": 1910722748, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00838-3877", + "type": "text", + "x": 2537.0, + "y": 3106.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 819713027, + "version": 1, + "versionNonce": 352554479, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Completed", + "originalText": "Completed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00839-2314", + "type": "rectangle", + "x": 2638.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 2020250382, + "version": 1, + "versionNonce": 825200439, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00840-2378", + "type": "rectangle", + "x": 2638.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1775431504, + "version": 1, + "versionNonce": 386605644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00841-9688", + "type": "ellipse", + "x": 2632.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 627825572, + "version": 1, + "versionNonce": 545114134, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00842-3365", + "type": "ellipse", + "x": 2846.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#9333ea", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1489584169, + "version": 1, + "versionNonce": 1231822529, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00843-5259", + "type": "rectangle", + "x": 2652.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1065904823, + "version": 1, + "versionNonce": 1400831271, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00844-9723", + "type": "text", + "x": 2652.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#9333ea", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 183919060, + "version": 1, + "versionNonce": 940535995, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "C", + "originalText": "C", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00845-6876", + "type": "text", + "x": 2698.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 537000968, + "version": 1, + "versionNonce": 917008339, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Route escalation", + "originalText": "Route escalation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00846-2467", + "type": "text", + "x": 2698.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 454219524, + "version": 1, + "versionNonce": 1399161995, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Condition", + "originalText": "Condition", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00847-5583", + "type": "line", + "x": 2650.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1937093596, + "version": 1, + "versionNonce": 142800059, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00848-1287", + "type": "text", + "x": 2652.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 715828565, + "version": 1, + "versionNonce": 1850126652, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00849-3226", + "type": "rectangle", + "x": 2762.0, + "y": 3100.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1468903405, + "version": 1, + "versionNonce": 723693215, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00850-7026", + "type": "text", + "x": 2772.0, + "y": 3106.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 45711453, + "version": 1, + "versionNonce": 1917960020, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Completed", + "originalText": "Completed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00851-2140", + "type": "rectangle", + "x": 2873.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1403448402, + "version": 1, + "versionNonce": 1106255703, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00852-7543", + "type": "rectangle", + "x": 2873.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 454243197, + "version": 1, + "versionNonce": 142104088, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00853-8431", + "type": "ellipse", + "x": 2867.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1972853069, + "version": 1, + "versionNonce": 1482641560, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00854-1032", + "type": "ellipse", + "x": 3081.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1002214007, + "version": 1, + "versionNonce": 1532602623, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00855-1196", + "type": "rectangle", + "x": 2887.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1834539982, + "version": 1, + "versionNonce": 940577799, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00856-8625", + "type": "text", + "x": 2887.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 901998987, + "version": 1, + "versionNonce": 673918729, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00857-6566", + "type": "text", + "x": 2933.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 811873049, + "version": 1, + "versionNonce": 46778221, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Prepare reply", + "originalText": "Prepare reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00858-9586", + "type": "text", + "x": 2933.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 528449385, + "version": 1, + "versionNonce": 249164798, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI task", + "originalText": "AI task", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00859-5374", + "type": "line", + "x": 2885.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 638260911, + "version": 1, + "versionNonce": 1548557845, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00860-1163", + "type": "text", + "x": 2887.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1160719713, + "version": 1, + "versionNonce": 1999744293, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00861-4143", + "type": "rectangle", + "x": 2997.0, + "y": 3100.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 826968603, + "version": 1, + "versionNonce": 309775269, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00862-6963", + "type": "text", + "x": 3007.0, + "y": 3106.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1963570549, + "version": 1, + "versionNonce": 421325296, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00863-4707", + "type": "rectangle", + "x": 3108.0, + "y": 3012.0, + "width": 214.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#dbe3ee", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1458489577, + "version": 1, + "versionNonce": 2090231505, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00864-7806", + "type": "rectangle", + "x": 3108.0, + "y": 3012.0, + "width": 4.0, + "height": 126.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1570052948, + "version": 1, + "versionNonce": 1541671706, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00865-5606", + "type": "ellipse", + "x": 3102.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 2136107000, + "version": 1, + "versionNonce": 1291400813, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00866-9559", + "type": "ellipse", + "x": 3316.0, + "y": 3066.0, + "width": 12.0, + "height": 12.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#16a34a", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 74789972, + "version": 1, + "versionNonce": 1419994094, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00867-6800", + "type": "rectangle", + "x": 3122.0, + "y": 3026.0, + "width": 34.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1064373050, + "version": 1, + "versionNonce": 1576856453, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00868-4658", + "type": "text", + "x": 3122.0, + "y": 3035.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#16a34a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 820323544, + "version": 1, + "versionNonce": 1159284941, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "I", + "originalText": "I", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00869-8088", + "type": "text", + "x": 3168.0, + "y": 3025.0, + "width": 138.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1195646691, + "version": 1, + "versionNonce": 949179336, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Post response", + "originalText": "Post response", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00870-6951", + "type": "text", + "x": 3168.0, + "y": 3051.0, + "width": 138.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 297785022, + "version": 1, + "versionNonce": 1163060932, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark message", + "originalText": "Lark message", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00871-1817", + "type": "line", + "x": 3120.0, + "y": 3074.0, + "width": 190.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#edf2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1491988118, + "version": 1, + "versionNonce": 341876426, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00872-3256", + "type": "text", + "x": 3122.0, + "y": 3088.0, + "width": 186.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1391013844, + "version": 1, + "versionNonce": 1691245364, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Configuration ready", + "originalText": "Configuration ready", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00873-9870", + "type": "rectangle", + "x": 3232.0, + "y": 3100.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1459370651, + "version": 1, + "versionNonce": 517536360, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00874-4838", + "type": "text", + "x": 3242.0, + "y": 3106.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 569488916, + "version": 1, + "versionNonce": 1054718801, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Queued", + "originalText": "Queued", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00875-9874", + "type": "rectangle", + "x": 2156.0, + "y": 3174.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 56903298, + "version": 1, + "versionNonce": 455176734, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00876-8656", + "type": "text", + "x": 2164.0, + "y": 3183.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1151689615, + "version": 1, + "versionNonce": 1758814099, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "+", + "originalText": "+", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00877-5112", + "type": "rectangle", + "x": 2156.0, + "y": 3210.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 832724557, + "version": 1, + "versionNonce": 2096305900, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00878-5450", + "type": "text", + "x": 2164.0, + "y": 3219.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1881198251, + "version": 1, + "versionNonce": 531475576, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "-", + "originalText": "-", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00879-6019", + "type": "rectangle", + "x": 2156.0, + "y": 3246.0, + "width": 34.0, + "height": 32.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1715848749, + "version": 1, + "versionNonce": 613832295, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00880-3256", + "type": "text", + "x": 2164.0, + "y": 3255.0, + "width": 18.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1141816727, + "version": 1, + "versionNonce": 1606335222, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Fit", + "originalText": "Fit", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00881-1346", + "type": "rectangle", + "x": 3196.0, + "y": 3198.0, + "width": 138.0, + "height": 86.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1147193344, + "version": 1, + "versionNonce": 1269166334, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00882-3204", + "type": "rectangle", + "x": 3206.0, + "y": 3214.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 101303035, + "version": 1, + "versionNonce": 1183427008, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00883-8864", + "type": "rectangle", + "x": 3246.0, + "y": 3214.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 766672484, + "version": 1, + "versionNonce": 24884338, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00884-4886", + "type": "rectangle", + "x": 3286.0, + "y": 3214.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1906157106, + "version": 1, + "versionNonce": 498085187, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00885-7754", + "type": "rectangle", + "x": 3206.0, + "y": 3242.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 2068229417, + "version": 1, + "versionNonce": 2008217548, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00886-2277", + "type": "rectangle", + "x": 3246.0, + "y": 3242.0, + "width": 28.0, + "height": 14.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 60145970, + "version": 1, + "versionNonce": 325736396, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00887-2276", + "type": "rectangle", + "x": 2130.0, + "y": 3312.0, + "width": 1230.0, + "height": 268.0, + "angle": 0, + "strokeColor": "#d8e0ea", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 637607979, + "version": 1, + "versionNonce": 1934452287, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00888-5363", + "type": "rectangle", + "x": 2130.0, + "y": 3306.0, + "width": 1230.0, + "height": 6.0, + "angle": 0, + "strokeColor": "#cbd5e1", + "backgroundColor": "#cbd5e1", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 2137838865, + "version": 1, + "versionNonce": 1348211843, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00889-3210", + "type": "text", + "x": 2152.0, + "y": 3330.0, + "width": 180.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 436562449, + "version": 1, + "versionNonce": 1219561034, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run console", + "originalText": "Run console", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00890-3002", + "type": "rectangle", + "x": 2272.0, + "y": 3326.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 517851492, + "version": 1, + "versionNonce": 2080679417, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00891-9814", + "type": "text", + "x": 2282.0, + "y": 3332.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 711789803, + "version": 1, + "versionNonce": 1470195526, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00892-3979", + "type": "text", + "x": 2378.0, + "y": 3333.0, + "width": 620.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1082794499, + "version": 1, + "versionNonce": 545916413, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "R-1042 \u00b7 Current draft \u00b7 revision 12 \u00b7 Activity record created", + "originalText": "R-1042 \u00b7 Current draft \u00b7 revision 12 \u00b7 Activity record created", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00893-6150", + "type": "rectangle", + "x": 3228.0, + "y": 3324.0, + "width": 102.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 1444097667, + "version": 1, + "versionNonce": 380261699, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00894-3274", + "type": "text", + "x": 3236.0, + "y": 3333.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 524227971, + "version": 1, + "versionNonce": 1404088058, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "View Run", + "originalText": "View Run", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-00895-3928", + "type": "line", + "x": 2130.0, + "y": 3370.0, + "width": 1230.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#eef2f7", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 273599875, + "version": 1, + "versionNonce": 1052759692, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1230.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-00896-4226", + "type": "text", + "x": 2154.0, + "y": 3390.0, + "width": 120.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1776535786, + "version": 1, + "versionNonce": 242962355, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "14:32:18.102", + "originalText": "14:32:18.102", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00897-8244", + "type": "ellipse", + "x": 2282.0, + "y": 3392.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 694193511, + "version": 1, + "versionNonce": 1541544691, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00898-6865", + "type": "text", + "x": 2306.0, + "y": 3390.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 102955767, + "version": 1, + "versionNonce": 1311449904, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Receive conversation", + "originalText": "Receive conversation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00899-7464", + "type": "text", + "x": 2580.0, + "y": 3390.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 357802203, + "version": 1, + "versionNonce": 1853601676, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "completed", + "originalText": "completed", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00900-1051", + "type": "text", + "x": 2154.0, + "y": 3427.0, + "width": 120.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 899132846, + "version": 1, + "versionNonce": 393027744, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "14:32:22.364", + "originalText": "14:32:22.364", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00901-8604", + "type": "ellipse", + "x": 2282.0, + "y": 3429.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 474770966, + "version": 1, + "versionNonce": 1951022823, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00902-2365", + "type": "text", + "x": 2306.0, + "y": 3427.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 633414579, + "version": 1, + "versionNonce": 621594999, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Classify severity", + "originalText": "Classify severity", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00903-3831", + "type": "text", + "x": 2580.0, + "y": 3427.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1238015723, + "version": 1, + "versionNonce": 1914158670, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "completed", + "originalText": "completed", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00904-1767", + "type": "text", + "x": 2154.0, + "y": 3464.0, + "width": 120.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1125675215, + "version": 1, + "versionNonce": 1899314510, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "14:32:22.481", + "originalText": "14:32:22.481", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00905-4324", + "type": "ellipse", + "x": 2282.0, + "y": 3466.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1244151849, + "version": 1, + "versionNonce": 200406158, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00906-7639", + "type": "text", + "x": 2306.0, + "y": 3464.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1828085261, + "version": 1, + "versionNonce": 1299074631, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Route escalation", + "originalText": "Route escalation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00907-2381", + "type": "text", + "x": 2580.0, + "y": 3464.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1133711920, + "version": 1, + "versionNonce": 273937652, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "completed", + "originalText": "completed", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00908-2750", + "type": "text", + "x": 2154.0, + "y": 3501.0, + "width": 120.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1000903520, + "version": 1, + "versionNonce": 552166564, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "14:32:23.019", + "originalText": "14:32:23.019", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00909-5334", + "type": "ellipse", + "x": 2282.0, + "y": 3503.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1509717006, + "version": 1, + "versionNonce": 1114573910, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00910-5531", + "type": "text", + "x": 2306.0, + "y": 3501.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1066129765, + "version": 1, + "versionNonce": 905239886, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Prepare reply", + "originalText": "Prepare reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00911-8947", + "type": "text", + "x": 2580.0, + "y": 3501.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 1586300931, + "version": 1, + "versionNonce": 818552653, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "running", + "originalText": "running", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00912-6724", + "type": "rectangle", + "x": 2820.0, + "y": 3386.0, + "width": 510.0, + "height": 134.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": { + "type": 3 + }, + "seed": 682942710, + "version": 1, + "versionNonce": 1868758929, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00913-1687", + "type": "text", + "x": 2838.0, + "y": 3402.0, + "width": 100.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 125809329, + "version": 1, + "versionNonce": 1749000729, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "OUTPUT", + "originalText": "OUTPUT", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00914-9355", + "type": "text", + "x": 2838.0, + "y": 3432.0, + "width": 470.0, + "height": 35.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-07" + ], + "frameId": "frame-00766-9912", + "roundness": null, + "seed": 994395690, + "version": 1, + "versionNonce": 1698928548, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Preparing a concise response for\nthe support conversation...", + "originalText": "Preparing a concise response for\nthe support conversation...", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-00915-7388", + "type": "frame", + "x": 3680.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 2102803155, + "version": 1, + "versionNonce": 122669913, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "09 Activity - filtered by Workflow" + }, + { + "id": "r-00917-8055", + "type": "rectangle", + "x": 3680.0, + "y": 2660.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 269565715, + "version": 1, + "versionNonce": 1474459692, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00918-6410", + "type": "rectangle", + "x": 3680.0, + "y": 2660.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 636772677, + "version": 1, + "versionNonce": 568207315, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00919-1708", + "type": "text", + "x": 3704.0, + "y": 2684.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1624536992, + "version": 1, + "versionNonce": 421763042, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00920-2148", + "type": "text", + "x": 3704.0, + "y": 2713.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 188005114, + "version": 1, + "versionNonce": 1970547828, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00921-3701", + "type": "ellipse", + "x": 3708.0, + "y": 2780.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1601667601, + "version": 1, + "versionNonce": 1830890056, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00922-8467", + "type": "text", + "x": 3728.0, + "y": 2776.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 154604669, + "version": 1, + "versionNonce": 1033679298, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00923-4137", + "type": "rectangle", + "x": 3692.0, + "y": 2820.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1109106125, + "version": 1, + "versionNonce": 681737659, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-00924-2458", + "type": "ellipse", + "x": 3708.0, + "y": 2832.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1071598003, + "version": 1, + "versionNonce": 1471443355, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00925-1321", + "type": "text", + "x": 3728.0, + "y": 2828.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1598079504, + "version": 1, + "versionNonce": 1041322705, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-00926-2122", + "type": "ellipse", + "x": 3708.0, + "y": 2884.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1007118558, + "version": 1, + "versionNonce": 562283323, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00927-8092", + "type": "text", + "x": 3728.0, + "y": 2880.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 522402826, + "version": 1, + "versionNonce": 1882151492, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00928-3751", + "type": "text", + "x": 3704.0, + "y": 3506.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 81356534, + "version": 1, + "versionNonce": 673515689, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00929-2465", + "type": "text", + "x": 3704.0, + "y": 3532.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1422209843, + "version": 1, + "versionNonce": 1300101480, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00930-7258", + "type": "rectangle", + "x": 3890.0, + "y": 2660.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1078279721, + "version": 1, + "versionNonce": 1137453978, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00931-6232", + "type": "text", + "x": 3920.0, + "y": 2678.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 938250550, + "version": 1, + "versionNonce": 361540548, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00932-8541", + "type": "text", + "x": 3920.0, + "y": 2709.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1928347303, + "version": 1, + "versionNonce": 352539802, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Every Run, newest first. Filtered to Weekly feedback digest.", + "originalText": "Every Run, newest first. Filtered to Weekly feedback digest.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00933-5011", + "type": "text", + "x": 3920.0, + "y": 2766.0, + "width": 47.879999999999995, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1158873313, + "version": 1, + "versionNonce": 1246247978, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SEARCH", + "originalText": "SEARCH", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00934-9259", + "type": "rectangle", + "x": 3920.0, + "y": 2788.0, + "width": 330.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1915180347, + "version": 1, + "versionNonce": 2135720997, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00935-4385", + "type": "text", + "x": 3932.0, + "y": 2799.0, + "width": 306.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 952423530, + "version": 1, + "versionNonce": 416686538, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Search Run or outcome", + "originalText": "Search Run or outcome", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00936-3270", + "type": "rectangle", + "x": 4276.0, + "y": 2788.0, + "width": 286.4, + "height": 30.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 269587880, + "version": 1, + "versionNonce": 1420797682, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00937-2628", + "type": "text", + "x": 4286.0, + "y": 2795.0, + "width": 266.4, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1887768555, + "version": 1, + "versionNonce": 995895073, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflow: Weekly feedback digest", + "originalText": "Workflow: Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00938-2948", + "type": "rectangle", + "x": 4570.4, + "y": 2788.0, + "width": 81.39999999999999, + "height": 30.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 146896043, + "version": 1, + "versionNonce": 1006385573, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00939-9079", + "type": "text", + "x": 4580.4, + "y": 2795.0, + "width": 61.39999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1482971281, + "version": 1, + "versionNonce": 2051199147, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "All 24", + "originalText": "All 24", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00940-6698", + "type": "rectangle", + "x": 4659.799999999999, + "y": 2788.0, + "width": 122.39999999999999, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1097380485, + "version": 1, + "versionNonce": 1675419043, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00941-2784", + "type": "text", + "x": 4669.799999999999, + "y": 2795.0, + "width": 102.39999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1778509493, + "version": 1, + "versionNonce": 719629525, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Needs you 1", + "originalText": "Needs you 1", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00942-7121", + "type": "rectangle", + "x": 4790.199999999999, + "y": 2788.0, + "width": 106.0, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1828436867, + "version": 1, + "versionNonce": 1244211036, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00943-5820", + "type": "text", + "x": 4800.199999999999, + "y": 2795.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 57759196, + "version": 1, + "versionNonce": 930305403, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running 1", + "originalText": "Running 1", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00944-7561", + "type": "rectangle", + "x": 4904.199999999999, + "y": 2788.0, + "width": 97.8, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 485581907, + "version": 1, + "versionNonce": 491200571, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00945-2760", + "type": "text", + "x": 4914.199999999999, + "y": 2795.0, + "width": 77.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1465163868, + "version": 1, + "versionNonce": 688721895, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed 2", + "originalText": "Failed 2", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00946-8876", + "type": "rectangle", + "x": 3920.0, + "y": 2854.0, + "width": 1170.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 451830490, + "version": 1, + "versionNonce": 2002970646, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00947-8849", + "type": "text", + "x": 3934.0, + "y": 2867.0, + "width": 77.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1641184055, + "version": 1, + "versionNonce": 513383972, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "STATUS", + "originalText": "STATUS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00948-9409", + "type": "text", + "x": 4039.0, + "y": 2867.0, + "width": 207.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1665005948, + "version": 1, + "versionNonce": 1266938498, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WORKFLOW", + "originalText": "WORKFLOW", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00949-6933", + "type": "text", + "x": 4274.0, + "y": 2867.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1116680998, + "version": 1, + "versionNonce": 671667380, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ORIGIN", + "originalText": "ORIGIN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00950-8503", + "type": "text", + "x": 4394.0, + "y": 2867.0, + "width": 147.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 465726952, + "version": 1, + "versionNonce": 1459797791, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "REVISION", + "originalText": "REVISION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00951-8141", + "type": "text", + "x": 4569.0, + "y": 2867.0, + "width": 87.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 763207252, + "version": 1, + "versionNonce": 670289688, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WHEN", + "originalText": "WHEN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00952-9392", + "type": "text", + "x": 4684.0, + "y": 2867.0, + "width": 47.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2005694677, + "version": 1, + "versionNonce": 648841078, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TOOK", + "originalText": "TOOK", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00953-4264", + "type": "text", + "x": 4759.0, + "y": 2867.0, + "width": 62.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 937031218, + "version": 1, + "versionNonce": 617424416, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "USAGE", + "originalText": "USAGE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00954-2699", + "type": "text", + "x": 4849.0, + "y": 2867.0, + "width": 227.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 428273670, + "version": 1, + "versionNonce": 161372999, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "OUTCOME", + "originalText": "OUTCOME", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00955-7382", + "type": "rectangle", + "x": 3920.0, + "y": 2896.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1341984198, + "version": 1, + "versionNonce": 661083846, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00956-9569", + "type": "rectangle", + "x": 3932.0, + "y": 2917.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 574608755, + "version": 1, + "versionNonce": 1307205192, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00957-1074", + "type": "text", + "x": 3942.0, + "y": 2923.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1867497651, + "version": 1, + "versionNonce": 806892786, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00958-2063", + "type": "text", + "x": 4037.0, + "y": 2910.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 320524457, + "version": 1, + "versionNonce": 435226442, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00959-5650", + "type": "text", + "x": 4037.0, + "y": 2935.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1782496454, + "version": 1, + "versionNonce": 1785535339, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 you", + "originalText": "Manual \u00b7 you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00960-3133", + "type": "text", + "x": 4272.0, + "y": 2921.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 640626447, + "version": 1, + "versionNonce": 644696830, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual", + "originalText": "Manual", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00961-5662", + "type": "text", + "x": 4392.0, + "y": 2911.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1700452649, + "version": 1, + "versionNonce": 383563321, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft", + "originalText": "Current draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00962-1990", + "type": "text", + "x": 4392.0, + "y": 2936.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 241587694, + "version": 1, + "versionNonce": 576107764, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 12", + "originalText": "revision 12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00963-5722", + "type": "text", + "x": 4567.0, + "y": 2921.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1357031811, + "version": 1, + "versionNonce": 698667728, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "18s ago", + "originalText": "18s ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00964-5015", + "type": "text", + "x": 4678.0, + "y": 2921.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 9670527, + "version": 1, + "versionNonce": 83713243, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:18", + "originalText": "00:18", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00965-1595", + "type": "text", + "x": 4753.0, + "y": 2921.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 547131990, + "version": 1, + "versionNonce": 23339615, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.03", + "originalText": "$0.03", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00966-6892", + "type": "text", + "x": 4847.0, + "y": 2911.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 919811284, + "version": 1, + "versionNonce": 19325027, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Preparing summary", + "originalText": "Preparing summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00967-6487", + "type": "text", + "x": 4847.0, + "y": 2936.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1355849966, + "version": 1, + "versionNonce": 702458979, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00968-8320", + "type": "rectangle", + "x": 3920.0, + "y": 2966.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 146532674, + "version": 1, + "versionNonce": 1826100321, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00969-7159", + "type": "rectangle", + "x": 3932.0, + "y": 2987.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1437982884, + "version": 1, + "versionNonce": 622577015, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00970-5011", + "type": "text", + "x": 3942.0, + "y": 2993.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2018776248, + "version": 1, + "versionNonce": 814788980, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00971-2295", + "type": "text", + "x": 4037.0, + "y": 2980.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1179413282, + "version": 1, + "versionNonce": 2139679292, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00972-8020", + "type": "text", + "x": 4037.0, + "y": 3005.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1819109187, + "version": 1, + "versionNonce": 2143810499, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule \u00b7 weekly", + "originalText": "Schedule \u00b7 weekly", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00973-9592", + "type": "text", + "x": 4272.0, + "y": 2991.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1339222480, + "version": 1, + "versionNonce": 1986889523, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00974-8445", + "type": "text", + "x": 4392.0, + "y": 2981.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 788672061, + "version": 1, + "versionNonce": 929577813, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00975-9871", + "type": "text", + "x": 4392.0, + "y": 3006.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 517892475, + "version": 1, + "versionNonce": 1686217822, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v7", + "originalText": "v7", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00976-2451", + "type": "text", + "x": 4567.0, + "y": 2991.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 482580259, + "version": 1, + "versionNonce": 1301053173, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2h ago", + "originalText": "2h ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00977-1468", + "type": "text", + "x": 4678.0, + "y": 2991.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1426043237, + "version": 1, + "versionNonce": 905415011, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "01:42", + "originalText": "01:42", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00978-9850", + "type": "text", + "x": 4753.0, + "y": 2991.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 74931719, + "version": 1, + "versionNonce": 1567136689, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.18", + "originalText": "$0.18", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00979-8950", + "type": "text", + "x": 4847.0, + "y": 2981.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 147785999, + "version": 1, + "versionNonce": 759280548, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Posted 1 summary", + "originalText": "Posted 1 summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00980-6260", + "type": "text", + "x": 4847.0, + "y": 3006.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1229207222, + "version": 1, + "versionNonce": 725175900, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00981-2899", + "type": "rectangle", + "x": 3920.0, + "y": 3036.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1169626779, + "version": 1, + "versionNonce": 1671431951, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00982-5754", + "type": "rectangle", + "x": 3932.0, + "y": 3057.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1680979924, + "version": 1, + "versionNonce": 3144021, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00983-4754", + "type": "text", + "x": 3942.0, + "y": 3063.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 513001364, + "version": 1, + "versionNonce": 915472207, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Needs you", + "originalText": "Needs you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00984-1739", + "type": "text", + "x": 4037.0, + "y": 3050.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1157164912, + "version": 1, + "versionNonce": 1920193660, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00985-7356", + "type": "text", + "x": 4037.0, + "y": 3075.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1583099943, + "version": 1, + "versionNonce": 32872170, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule \u00b7 weekly", + "originalText": "Schedule \u00b7 weekly", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00986-6715", + "type": "text", + "x": 4272.0, + "y": 3061.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1035802322, + "version": 1, + "versionNonce": 1348144114, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00987-7289", + "type": "text", + "x": 4392.0, + "y": 3051.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1909202492, + "version": 1, + "versionNonce": 677639500, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00988-2710", + "type": "text", + "x": 4392.0, + "y": 3076.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 468610391, + "version": 1, + "versionNonce": 299932835, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v7", + "originalText": "v7", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00989-2678", + "type": "text", + "x": 4567.0, + "y": 3061.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 550566432, + "version": 1, + "versionNonce": 1389331183, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "1w ago", + "originalText": "1w ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00990-9121", + "type": "text", + "x": 4678.0, + "y": 3061.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 455430823, + "version": 1, + "versionNonce": 461885311, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:56", + "originalText": "00:56", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00991-6705", + "type": "text", + "x": 4753.0, + "y": 3061.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1302147114, + "version": 1, + "versionNonce": 329407773, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.11", + "originalText": "$0.11", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00992-7204", + "type": "text", + "x": 4847.0, + "y": 3051.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1229562169, + "version": 1, + "versionNonce": 164218482, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Approval requested", + "originalText": "Approval requested", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00993-4914", + "type": "text", + "x": 4847.0, + "y": 3076.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 610602839, + "version": 1, + "versionNonce": 1375994194, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-00994-7737", + "type": "rectangle", + "x": 3920.0, + "y": 3106.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1697667312, + "version": 1, + "versionNonce": 1034601927, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-00995-6082", + "type": "rectangle", + "x": 3932.0, + "y": 3127.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 103296232, + "version": 1, + "versionNonce": 1943729058, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-00996-5829", + "type": "text", + "x": 3942.0, + "y": 3133.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2001367762, + "version": 1, + "versionNonce": 962412002, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00997-8318", + "type": "text", + "x": 4037.0, + "y": 3120.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 460381995, + "version": 1, + "versionNonce": 198656461, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00998-1123", + "type": "text", + "x": 4037.0, + "y": 3145.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 959113245, + "version": 1, + "versionNonce": 441079832, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 Calvin", + "originalText": "Manual \u00b7 Calvin", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-00999-7101", + "type": "text", + "x": 4272.0, + "y": 3131.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1424579179, + "version": 1, + "versionNonce": 2046103282, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual", + "originalText": "Manual", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01000-4027", + "type": "text", + "x": 4392.0, + "y": 3121.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 25243938, + "version": 1, + "versionNonce": 437392227, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft", + "originalText": "Draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01001-1212", + "type": "text", + "x": 4392.0, + "y": 3146.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1814784157, + "version": 1, + "versionNonce": 1426478041, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 9", + "originalText": "revision 9", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01002-7393", + "type": "text", + "x": 4567.0, + "y": 3131.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2135392859, + "version": 1, + "versionNonce": 2021143985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "8d ago", + "originalText": "8d ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01003-8771", + "type": "text", + "x": 4678.0, + "y": 3131.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1239288194, + "version": 1, + "versionNonce": 1976383809, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:12", + "originalText": "00:12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01004-2924", + "type": "text", + "x": 4753.0, + "y": 3131.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1657333523, + "version": 1, + "versionNonce": 2003138615, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.02", + "originalText": "$0.02", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01005-4456", + "type": "text", + "x": 4847.0, + "y": 3121.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 225269456, + "version": 1, + "versionNonce": 143496150, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Lark connection expired", + "originalText": "Lark connection expired", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01006-2363", + "type": "text", + "x": 4847.0, + "y": 3146.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1316926903, + "version": 1, + "versionNonce": 176892050, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01007-1956", + "type": "rectangle", + "x": 3920.0, + "y": 3176.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 4685290, + "version": 1, + "versionNonce": 1488541980, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01008-9882", + "type": "rectangle", + "x": 3932.0, + "y": 3197.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": { + "type": 3 + }, + "seed": 1588321659, + "version": 1, + "versionNonce": 1499301191, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01009-8393", + "type": "text", + "x": 3942.0, + "y": 3203.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 612332585, + "version": 1, + "versionNonce": 1362633068, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01010-4483", + "type": "text", + "x": 4037.0, + "y": 3190.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2118328490, + "version": 1, + "versionNonce": 1225207226, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01011-9156", + "type": "text", + "x": 4037.0, + "y": 3215.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 974996037, + "version": 1, + "versionNonce": 1198960868, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run again \u00b7 R-1001", + "originalText": "Run again \u00b7 R-1001", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01012-7385", + "type": "text", + "x": 4272.0, + "y": 3201.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1035134280, + "version": 1, + "versionNonce": 127684011, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run again", + "originalText": "Run again", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01013-7979", + "type": "text", + "x": 4392.0, + "y": 3191.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1143323710, + "version": 1, + "versionNonce": 337980859, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01014-6082", + "type": "text", + "x": 4392.0, + "y": 3216.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1936542364, + "version": 1, + "versionNonce": 1050406248, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v6", + "originalText": "v6", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01015-8026", + "type": "text", + "x": 4567.0, + "y": 3201.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1292624638, + "version": 1, + "versionNonce": 1584417773, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "15d ago", + "originalText": "15d ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01016-5401", + "type": "text", + "x": 4678.0, + "y": 3201.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 653226794, + "version": 1, + "versionNonce": 2077091314, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "01:37", + "originalText": "01:37", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01017-1777", + "type": "text", + "x": 4753.0, + "y": 3201.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1374064592, + "version": 1, + "versionNonce": 319301424, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.17", + "originalText": "$0.17", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01018-5679", + "type": "text", + "x": 4847.0, + "y": 3191.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 708591503, + "version": 1, + "versionNonce": 2052924079, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Posted 1 summary", + "originalText": "Posted 1 summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01019-7836", + "type": "text", + "x": 4847.0, + "y": 3216.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 1457117390, + "version": 1, + "versionNonce": 551835569, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01020-9266", + "type": "text", + "x": 3920.0, + "y": 3528.0, + "width": 720.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-08" + ], + "frameId": "frame-00915-7388", + "roundness": null, + "seed": 2132029393, + "version": 1, + "versionNonce": 413112535, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity remains the authoritative Run history. Run again and Retry append linked records.", + "originalText": "Activity remains the authoritative Run history. Run again and Retry append linked records.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01021-4215", + "type": "frame", + "x": 160.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1470221932, + "version": 1, + "versionNonce": 529599512, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "10 Activity - all retained Runs" + }, + { + "id": "r-01023-2710", + "type": "rectangle", + "x": 160.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 154895358, + "version": 1, + "versionNonce": 279466254, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01024-8035", + "type": "rectangle", + "x": 160.0, + "y": 3840.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1312133163, + "version": 1, + "versionNonce": 815875021, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01025-5799", + "type": "text", + "x": 184.0, + "y": 3864.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 82850982, + "version": 1, + "versionNonce": 1698831270, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01026-3996", + "type": "text", + "x": 184.0, + "y": 3893.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1209805788, + "version": 1, + "versionNonce": 589599272, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01027-5753", + "type": "ellipse", + "x": 188.0, + "y": 3960.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 454141735, + "version": 1, + "versionNonce": 1578542880, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01028-1249", + "type": "text", + "x": 208.0, + "y": 3956.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1760506649, + "version": 1, + "versionNonce": 419806219, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01029-5767", + "type": "rectangle", + "x": 172.0, + "y": 4000.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 678890122, + "version": 1, + "versionNonce": 1233477269, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01030-4878", + "type": "ellipse", + "x": 188.0, + "y": 4012.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 178391629, + "version": 1, + "versionNonce": 280090824, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01031-8339", + "type": "text", + "x": 208.0, + "y": 4008.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 454268906, + "version": 1, + "versionNonce": 952708790, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01032-7096", + "type": "ellipse", + "x": 188.0, + "y": 4064.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 155079917, + "version": 1, + "versionNonce": 396581399, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01033-9879", + "type": "text", + "x": 208.0, + "y": 4060.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 200594321, + "version": 1, + "versionNonce": 403599266, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01034-4871", + "type": "text", + "x": 184.0, + "y": 4686.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1350631134, + "version": 1, + "versionNonce": 498163020, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01035-3113", + "type": "text", + "x": 184.0, + "y": 4712.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1163154911, + "version": 1, + "versionNonce": 423438732, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01036-3848", + "type": "rectangle", + "x": 370.0, + "y": 3840.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1912223252, + "version": 1, + "versionNonce": 1248879966, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01037-8798", + "type": "text", + "x": 400.0, + "y": 3858.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2110906731, + "version": 1, + "versionNonce": 2008634642, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01038-2496", + "type": "text", + "x": 400.0, + "y": 3889.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1275954698, + "version": 1, + "versionNonce": 2046865976, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Every Run, newest first. Search, inspect, and recover from one place.", + "originalText": "Every Run, newest first. Search, inspect, and recover from one place.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01039-7736", + "type": "text", + "x": 400.0, + "y": 3950.0, + "width": 47.879999999999995, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 983650162, + "version": 1, + "versionNonce": 675651254, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SEARCH", + "originalText": "SEARCH", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01040-7959", + "type": "rectangle", + "x": 400.0, + "y": 3972.0, + "width": 380.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 208116880, + "version": 1, + "versionNonce": 869720307, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01041-9938", + "type": "text", + "x": 412.0, + "y": 3983.0, + "width": 356.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1351731155, + "version": 1, + "versionNonce": 1262659687, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Search workflow, Run, or outcome", + "originalText": "Search workflow, Run, or outcome", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01042-6592", + "type": "rectangle", + "x": 808.0, + "y": 3972.0, + "width": 89.6, + "height": 30.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1616746485, + "version": 1, + "versionNonce": 1948970189, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01043-2271", + "type": "text", + "x": 818.0, + "y": 3979.0, + "width": 69.6, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 137804265, + "version": 1, + "versionNonce": 1286635654, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "All 128", + "originalText": "All 128", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01044-9140", + "type": "rectangle", + "x": 905.6, + "y": 3972.0, + "width": 122.39999999999999, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 2054519454, + "version": 1, + "versionNonce": 1657379592, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01045-7754", + "type": "text", + "x": 915.6, + "y": 3979.0, + "width": 102.39999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1648186173, + "version": 1, + "versionNonce": 903299293, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Needs you 2", + "originalText": "Needs you 2", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01046-5194", + "type": "rectangle", + "x": 1036.0, + "y": 3972.0, + "width": 106.0, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1898654865, + "version": 1, + "versionNonce": 469193996, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01047-3183", + "type": "text", + "x": 1046.0, + "y": 3979.0, + "width": 86.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 985591591, + "version": 1, + "versionNonce": 970826280, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running 3", + "originalText": "Running 3", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01048-8008", + "type": "rectangle", + "x": 1150.0, + "y": 3972.0, + "width": 97.8, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 482327101, + "version": 1, + "versionNonce": 35983373, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01049-1166", + "type": "text", + "x": 1160.0, + "y": 3979.0, + "width": 77.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 801078951, + "version": 1, + "versionNonce": 1767173427, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed 7", + "originalText": "Failed 7", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01050-8792", + "type": "rectangle", + "x": 1255.8, + "y": 3972.0, + "width": 89.6, + "height": 30.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1084489131, + "version": 1, + "versionNonce": 2095106651, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01051-1307", + "type": "text", + "x": 1265.8, + "y": 3979.0, + "width": 69.6, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1261660929, + "version": 1, + "versionNonce": 686258718, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Mine 18", + "originalText": "Mine 18", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01052-6766", + "type": "rectangle", + "x": 400.0, + "y": 4038.0, + "width": 1170.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 724688338, + "version": 1, + "versionNonce": 135214163, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01053-6866", + "type": "text", + "x": 414.0, + "y": 4051.0, + "width": 77.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1435256748, + "version": 1, + "versionNonce": 2122177457, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "STATUS", + "originalText": "STATUS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01054-6488", + "type": "text", + "x": 519.0, + "y": 4051.0, + "width": 207.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 528989184, + "version": 1, + "versionNonce": 2014747567, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WORKFLOW", + "originalText": "WORKFLOW", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01055-5325", + "type": "text", + "x": 754.0, + "y": 4051.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1270208017, + "version": 1, + "versionNonce": 1070089138, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ORIGIN", + "originalText": "ORIGIN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01056-8329", + "type": "text", + "x": 874.0, + "y": 4051.0, + "width": 147.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1415691307, + "version": 1, + "versionNonce": 68865039, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "REVISION", + "originalText": "REVISION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01057-9057", + "type": "text", + "x": 1049.0, + "y": 4051.0, + "width": 87.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 674000188, + "version": 1, + "versionNonce": 1551573424, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WHEN", + "originalText": "WHEN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01058-8557", + "type": "text", + "x": 1164.0, + "y": 4051.0, + "width": 47.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 393386394, + "version": 1, + "versionNonce": 1514002877, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "TOOK", + "originalText": "TOOK", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01059-2448", + "type": "text", + "x": 1239.0, + "y": 4051.0, + "width": 62.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 148133636, + "version": 1, + "versionNonce": 1154199888, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "USAGE", + "originalText": "USAGE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01060-3329", + "type": "text", + "x": 1329.0, + "y": 4051.0, + "width": 227.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 17015447, + "version": 1, + "versionNonce": 1169301658, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "OUTCOME", + "originalText": "OUTCOME", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01061-5381", + "type": "rectangle", + "x": 400.0, + "y": 4080.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 369213418, + "version": 1, + "versionNonce": 890959920, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01062-8239", + "type": "rectangle", + "x": 412.0, + "y": 4101.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 712333823, + "version": 1, + "versionNonce": 851358147, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01063-7964", + "type": "text", + "x": 422.0, + "y": 4107.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 726897006, + "version": 1, + "versionNonce": 181640494, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01064-5891", + "type": "text", + "x": 517.0, + "y": 4094.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2066452166, + "version": 1, + "versionNonce": 15605705, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01065-2661", + "type": "text", + "x": 517.0, + "y": 4119.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 6222373, + "version": 1, + "versionNonce": 1871860489, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 you", + "originalText": "Manual \u00b7 you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01066-6626", + "type": "text", + "x": 752.0, + "y": 4105.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1724046815, + "version": 1, + "versionNonce": 1370771442, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual", + "originalText": "Manual", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01067-9477", + "type": "text", + "x": 872.0, + "y": 4095.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 995238780, + "version": 1, + "versionNonce": 812523867, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft", + "originalText": "Current draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01068-3987", + "type": "text", + "x": 872.0, + "y": 4120.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 922045712, + "version": 1, + "versionNonce": 666448704, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 12", + "originalText": "revision 12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01069-1802", + "type": "text", + "x": 1047.0, + "y": 4105.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1758487042, + "version": 1, + "versionNonce": 359220473, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "18s ago", + "originalText": "18s ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01070-1423", + "type": "text", + "x": 1158.0, + "y": 4105.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 611464078, + "version": 1, + "versionNonce": 677623656, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:18", + "originalText": "00:18", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01071-1760", + "type": "text", + "x": 1233.0, + "y": 4105.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 907792913, + "version": 1, + "versionNonce": 1282370534, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.03", + "originalText": "$0.03", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01072-6611", + "type": "text", + "x": 1327.0, + "y": 4095.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1169994162, + "version": 1, + "versionNonce": 593162790, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Preparing reply", + "originalText": "Preparing reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01073-9133", + "type": "text", + "x": 1327.0, + "y": 4120.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 150056386, + "version": 1, + "versionNonce": 783647598, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01074-9698", + "type": "rectangle", + "x": 400.0, + "y": 4150.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 363262574, + "version": 1, + "versionNonce": 1541453108, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01075-2425", + "type": "rectangle", + "x": 412.0, + "y": 4171.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1702966852, + "version": 1, + "versionNonce": 410738760, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01076-7693", + "type": "text", + "x": 422.0, + "y": 4177.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1604744916, + "version": 1, + "versionNonce": 985313218, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Needs you", + "originalText": "Needs you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01077-6744", + "type": "text", + "x": 517.0, + "y": 4164.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1658123375, + "version": 1, + "versionNonce": 307878247, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Invoice follow-up", + "originalText": "Invoice follow-up", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01078-6574", + "type": "text", + "x": 517.0, + "y": 4189.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1162812280, + "version": 1, + "versionNonce": 431882900, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule \u00b7 weekday", + "originalText": "Schedule \u00b7 weekday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01079-9682", + "type": "text", + "x": 752.0, + "y": 4175.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 768078389, + "version": 1, + "versionNonce": 1299409068, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01080-3702", + "type": "text", + "x": 872.0, + "y": 4165.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 242012840, + "version": 1, + "versionNonce": 1954052168, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01081-6299", + "type": "text", + "x": 872.0, + "y": 4190.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1910670815, + "version": 1, + "versionNonce": 1569339221, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v4", + "originalText": "v4", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01082-2012", + "type": "text", + "x": 1047.0, + "y": 4175.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1954174122, + "version": 1, + "versionNonce": 2132442053, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "42m ago", + "originalText": "42m ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01083-9698", + "type": "text", + "x": 1158.0, + "y": 4175.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 921498190, + "version": 1, + "versionNonce": 1896487282, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "02:18", + "originalText": "02:18", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01084-3502", + "type": "text", + "x": 1233.0, + "y": 4175.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1290175061, + "version": 1, + "versionNonce": 842154507, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.22", + "originalText": "$0.22", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01085-5225", + "type": "text", + "x": 1327.0, + "y": 4165.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1141608513, + "version": 1, + "versionNonce": 221814184, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Approve 4 messages", + "originalText": "Approve 4 messages", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01086-5149", + "type": "text", + "x": 1327.0, + "y": 4190.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2097351256, + "version": 1, + "versionNonce": 1951226907, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01087-5716", + "type": "rectangle", + "x": 400.0, + "y": 4220.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 375375171, + "version": 1, + "versionNonce": 24523514, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01088-3751", + "type": "rectangle", + "x": 412.0, + "y": 4241.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1843540766, + "version": 1, + "versionNonce": 1399773054, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01089-2689", + "type": "text", + "x": 422.0, + "y": 4247.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1039524515, + "version": 1, + "versionNonce": 1640180882, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01090-7063", + "type": "text", + "x": 517.0, + "y": 4234.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1355284802, + "version": 1, + "versionNonce": 417747342, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Weekly feedback digest", + "originalText": "Weekly feedback digest", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01091-2220", + "type": "text", + "x": 517.0, + "y": 4259.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1094021624, + "version": 1, + "versionNonce": 309484263, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule \u00b7 weekly", + "originalText": "Schedule \u00b7 weekly", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01092-3626", + "type": "text", + "x": 752.0, + "y": 4245.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1194114405, + "version": 1, + "versionNonce": 1591323732, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01093-3244", + "type": "text", + "x": 872.0, + "y": 4235.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 382568512, + "version": 1, + "versionNonce": 989751861, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01094-5798", + "type": "text", + "x": 872.0, + "y": 4260.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 674870274, + "version": 1, + "versionNonce": 760341885, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v7", + "originalText": "v7", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01095-1848", + "type": "text", + "x": 1047.0, + "y": 4245.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1645368497, + "version": 1, + "versionNonce": 625474109, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2h ago", + "originalText": "2h ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01096-9821", + "type": "text", + "x": 1158.0, + "y": 4245.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1864957993, + "version": 1, + "versionNonce": 744774795, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "01:42", + "originalText": "01:42", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01097-2139", + "type": "text", + "x": 1233.0, + "y": 4245.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1274288406, + "version": 1, + "versionNonce": 1949814105, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.18", + "originalText": "$0.18", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01098-9145", + "type": "text", + "x": 1327.0, + "y": 4235.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1409724511, + "version": 1, + "versionNonce": 1860241519, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Posted 1 summary", + "originalText": "Posted 1 summary", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01099-7231", + "type": "text", + "x": 1327.0, + "y": 4260.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 330236118, + "version": 1, + "versionNonce": 52607329, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01100-8891", + "type": "rectangle", + "x": 400.0, + "y": 4290.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 644556360, + "version": 1, + "versionNonce": 129456575, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01101-7090", + "type": "rectangle", + "x": 412.0, + "y": 4311.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1804076372, + "version": 1, + "versionNonce": 565213091, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01102-3917", + "type": "text", + "x": 422.0, + "y": 4317.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 992439954, + "version": 1, + "versionNonce": 1502915740, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01103-6400", + "type": "text", + "x": 517.0, + "y": 4304.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2073739349, + "version": 1, + "versionNonce": 2034902948, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Nightly order sync", + "originalText": "Nightly order sync", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01104-2514", + "type": "text", + "x": 517.0, + "y": 4329.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 769827151, + "version": 1, + "versionNonce": 193191609, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule \u00b7 nightly", + "originalText": "Schedule \u00b7 nightly", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01105-9211", + "type": "text", + "x": 752.0, + "y": 4315.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 492487053, + "version": 1, + "versionNonce": 1937855731, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Schedule", + "originalText": "Schedule", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01106-8197", + "type": "text", + "x": 872.0, + "y": 4305.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 669714600, + "version": 1, + "versionNonce": 2010689024, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01107-4577", + "type": "text", + "x": 872.0, + "y": 4330.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1004517327, + "version": 1, + "versionNonce": 363626995, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v9", + "originalText": "v9", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01108-8532", + "type": "text", + "x": 1047.0, + "y": 4315.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1685738987, + "version": 1, + "versionNonce": 1552622611, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "3:04 AM", + "originalText": "3:04 AM", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01109-8599", + "type": "text", + "x": 1158.0, + "y": 4315.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1783870994, + "version": 1, + "versionNonce": 1192080228, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:21", + "originalText": "00:21", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01110-1449", + "type": "text", + "x": 1233.0, + "y": 4315.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1961195095, + "version": 1, + "versionNonce": 1188375148, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.04", + "originalText": "$0.04", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01111-8061", + "type": "text", + "x": 1327.0, + "y": 4305.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1372722388, + "version": 1, + "versionNonce": 126470813, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Warehouse unavailable", + "originalText": "Warehouse unavailable", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01112-8427", + "type": "text", + "x": 1327.0, + "y": 4330.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 373591302, + "version": 1, + "versionNonce": 756395079, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01113-8141", + "type": "rectangle", + "x": 400.0, + "y": 4360.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 352887871, + "version": 1, + "versionNonce": 1134976228, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01114-6239", + "type": "rectangle", + "x": 412.0, + "y": 4381.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 2014741755, + "version": 1, + "versionNonce": 1204588302, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01115-3550", + "type": "text", + "x": 422.0, + "y": 4387.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 600612979, + "version": 1, + "versionNonce": 904048636, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01116-4322", + "type": "text", + "x": 517.0, + "y": 4374.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1941179013, + "version": 1, + "versionNonce": 2074319463, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01117-1709", + "type": "text", + "x": 517.0, + "y": 4399.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1352818522, + "version": 1, + "versionNonce": 1261183158, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry \u00b7 R-1031", + "originalText": "Retry \u00b7 R-1031", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01118-7840", + "type": "text", + "x": 752.0, + "y": 4385.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 141550662, + "version": 1, + "versionNonce": 1891673968, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry", + "originalText": "Retry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01119-3987", + "type": "text", + "x": 872.0, + "y": 4375.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1223319514, + "version": 1, + "versionNonce": 720264863, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft", + "originalText": "Draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01120-5220", + "type": "text", + "x": 872.0, + "y": 4400.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 205797346, + "version": 1, + "versionNonce": 652167091, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 11", + "originalText": "revision 11", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01121-8149", + "type": "text", + "x": 1047.0, + "y": 4385.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 682337045, + "version": 1, + "versionNonce": 1771982235, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Yesterday", + "originalText": "Yesterday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01122-5310", + "type": "text", + "x": 1158.0, + "y": 4385.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 345263492, + "version": 1, + "versionNonce": 1500055530, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:49", + "originalText": "00:49", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01123-4089", + "type": "text", + "x": 1233.0, + "y": 4385.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 207094589, + "version": 1, + "versionNonce": 1985820136, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.09", + "originalText": "$0.09", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01124-4102", + "type": "text", + "x": 1327.0, + "y": 4375.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1495349513, + "version": 1, + "versionNonce": 1087422639, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Posted reply", + "originalText": "Posted reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01125-1370", + "type": "text", + "x": 1327.0, + "y": 4400.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 652680726, + "version": 1, + "versionNonce": 601785155, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01126-4167", + "type": "rectangle", + "x": 400.0, + "y": 4430.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 590191875, + "version": 1, + "versionNonce": 2013601746, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01127-8671", + "type": "rectangle", + "x": 412.0, + "y": 4451.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 315171825, + "version": 1, + "versionNonce": 992009736, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01128-9768", + "type": "text", + "x": 422.0, + "y": 4457.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 480257737, + "version": 1, + "versionNonce": 639235147, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01129-4843", + "type": "text", + "x": 517.0, + "y": 4444.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 691838360, + "version": 1, + "versionNonce": 313933300, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01130-3363", + "type": "text", + "x": 517.0, + "y": 4469.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1570216687, + "version": 1, + "versionNonce": 828250757, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 Calvin", + "originalText": "Manual \u00b7 Calvin", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01131-9022", + "type": "text", + "x": 752.0, + "y": 4455.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2138846314, + "version": 1, + "versionNonce": 1315857445, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual", + "originalText": "Manual", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01132-3221", + "type": "text", + "x": 872.0, + "y": 4445.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 469662412, + "version": 1, + "versionNonce": 882010418, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft", + "originalText": "Draft", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01133-7238", + "type": "text", + "x": 872.0, + "y": 4470.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 466759561, + "version": 1, + "versionNonce": 881834893, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "revision 11", + "originalText": "revision 11", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01134-1457", + "type": "text", + "x": 1047.0, + "y": 4455.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 555397250, + "version": 1, + "versionNonce": 791966997, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Yesterday", + "originalText": "Yesterday", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01135-6904", + "type": "text", + "x": 1158.0, + "y": 4455.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1846128907, + "version": 1, + "versionNonce": 700265260, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:13", + "originalText": "00:13", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01136-3029", + "type": "text", + "x": 1233.0, + "y": 4455.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1700115858, + "version": 1, + "versionNonce": 372530537, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.02", + "originalText": "$0.02", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01137-6837", + "type": "text", + "x": 1327.0, + "y": 4445.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1992559456, + "version": 1, + "versionNonce": 1198051998, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Linear permission denied", + "originalText": "Linear permission denied", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01138-5739", + "type": "text", + "x": 1327.0, + "y": 4470.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1699352191, + "version": 1, + "versionNonce": 1606023465, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01139-8431", + "type": "rectangle", + "x": 400.0, + "y": 4500.0, + "width": 1170.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 975259066, + "version": 1, + "versionNonce": 1206580592, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01140-6017", + "type": "rectangle", + "x": 412.0, + "y": 4521.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#ecfdf3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": { + "type": 3 + }, + "seed": 1422914461, + "version": 1, + "versionNonce": 771818511, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01141-5022", + "type": "text", + "x": 422.0, + "y": 4527.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 175033714, + "version": 1, + "versionNonce": 1831705096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Succeeded", + "originalText": "Succeeded", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01142-6989", + "type": "text", + "x": 517.0, + "y": 4514.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1413929857, + "version": 1, + "versionNonce": 1499140129, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Invoice follow-up", + "originalText": "Invoice follow-up", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01143-2451", + "type": "text", + "x": 517.0, + "y": 4539.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2110426217, + "version": 1, + "versionNonce": 207457843, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 you", + "originalText": "Manual \u00b7 you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01144-2145", + "type": "text", + "x": 752.0, + "y": 4525.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1610347834, + "version": 1, + "versionNonce": 1749915441, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual", + "originalText": "Manual", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01145-6391", + "type": "text", + "x": 872.0, + "y": 4515.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 811353168, + "version": 1, + "versionNonce": 541450157, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published", + "originalText": "Published", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01146-6467", + "type": "text", + "x": 872.0, + "y": 4540.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 880210575, + "version": 1, + "versionNonce": 1791086282, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "v4", + "originalText": "v4", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01147-4319", + "type": "text", + "x": 1047.0, + "y": 4525.0, + "width": 92.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1698528707, + "version": 1, + "versionNonce": 118363608, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2d ago", + "originalText": "2d ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01148-7593", + "type": "text", + "x": 1158.0, + "y": 4525.0, + "width": 60.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1773266851, + "version": 1, + "versionNonce": 418277434, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "02:04", + "originalText": "02:04", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01149-4094", + "type": "text", + "x": 1233.0, + "y": 4525.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 2037543246, + "version": 1, + "versionNonce": 291079103, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.20", + "originalText": "$0.20", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01150-7974", + "type": "text", + "x": 1327.0, + "y": 4515.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 1144725099, + "version": 1, + "versionNonce": 456618535, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Sent 6 reminders", + "originalText": "Sent 6 reminders", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01151-6302", + "type": "text", + "x": 1327.0, + "y": 4540.0, + "width": 228.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 102275159, + "version": 1, + "versionNonce": 92479022, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open details", + "originalText": "Open details", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01152-6630", + "type": "text", + "x": 400.0, + "y": 4710.0, + "width": 680.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-09" + ], + "frameId": "frame-01021-4215", + "roundness": null, + "seed": 665073723, + "version": 1, + "versionNonce": 1410584096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Showing 7 of 128 Runs | New records appear here as soon as execution starts.", + "originalText": "Showing 7 of 128 Runs | New records appear here as soon as execution starts.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01153-9609", + "type": "frame", + "x": 1920.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1791026401, + "version": 1, + "versionNonce": 1587049567, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "11 Run detail - immutable record" + }, + { + "id": "r-01155-7542", + "type": "rectangle", + "x": 1920.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 307912599, + "version": 1, + "versionNonce": 245690432, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01156-9595", + "type": "rectangle", + "x": 1920.0, + "y": 3840.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1106910841, + "version": 1, + "versionNonce": 1563778443, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01157-5623", + "type": "text", + "x": 1944.0, + "y": 3864.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1184679661, + "version": 1, + "versionNonce": 2141739967, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01158-6010", + "type": "text", + "x": 1944.0, + "y": 3893.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1146241242, + "version": 1, + "versionNonce": 1580683705, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01159-1873", + "type": "ellipse", + "x": 1948.0, + "y": 3960.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1404060231, + "version": 1, + "versionNonce": 924192210, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01160-3563", + "type": "text", + "x": 1968.0, + "y": 3956.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 777093970, + "version": 1, + "versionNonce": 1836241192, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01161-8207", + "type": "rectangle", + "x": 1932.0, + "y": 4000.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 526781430, + "version": 1, + "versionNonce": 397393966, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01162-1592", + "type": "ellipse", + "x": 1948.0, + "y": 4012.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 675026638, + "version": 1, + "versionNonce": 1608466201, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01163-7414", + "type": "text", + "x": 1968.0, + "y": 4008.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 447542424, + "version": 1, + "versionNonce": 1178576371, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01164-4491", + "type": "ellipse", + "x": 1948.0, + "y": 4064.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1206200348, + "version": 1, + "versionNonce": 860970499, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01165-7692", + "type": "text", + "x": 1968.0, + "y": 4060.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1622397633, + "version": 1, + "versionNonce": 245332790, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01166-3972", + "type": "text", + "x": 1944.0, + "y": 4686.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1581322754, + "version": 1, + "versionNonce": 664936387, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01167-8232", + "type": "text", + "x": 1944.0, + "y": 4712.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1000276939, + "version": 1, + "versionNonce": 383040744, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01168-9980", + "type": "rectangle", + "x": 2130.0, + "y": 3840.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 604361372, + "version": 1, + "versionNonce": 445344838, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01169-4037", + "type": "text", + "x": 2160.0, + "y": 3858.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 171539509, + "version": 1, + "versionNonce": 1515081711, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run #R-1042", + "originalText": "Run #R-1042", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01170-9547", + "type": "text", + "x": 2160.0, + "y": 3889.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1294265044, + "version": 1, + "versionNonce": 1355021010, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage | Started 18 seconds ago", + "originalText": "Customer escalation triage | Started 18 seconds ago", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01171-3664", + "type": "rectangle", + "x": 3032.0, + "y": 3862.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1135973755, + "version": 1, + "versionNonce": 135574504, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01172-6712", + "type": "text", + "x": 3040.0, + "y": 3871.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 539665594, + "version": 1, + "versionNonce": 517682926, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open workflow", + "originalText": "Open workflow", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01173-1329", + "type": "rectangle", + "x": 3170.0, + "y": 3862.0, + "width": 160.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1821469843, + "version": 1, + "versionNonce": 1280376890, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01174-6639", + "type": "text", + "x": 3178.0, + "y": 3871.0, + "width": 144.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 669290266, + "version": 1, + "versionNonce": 177666391, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run again", + "originalText": "Run again", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01175-4307", + "type": "rectangle", + "x": 2160.0, + "y": 3950.0, + "width": 780.0, + "height": 748.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 679252086, + "version": 1, + "versionNonce": 2150384, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01176-4420", + "type": "rectangle", + "x": 2184.0, + "y": 3974.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1464139590, + "version": 1, + "versionNonce": 1189923185, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01177-8499", + "type": "text", + "x": 2194.0, + "y": 3980.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 692742478, + "version": 1, + "versionNonce": 1751204298, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01178-2225", + "type": "text", + "x": 2184.0, + "y": 4022.0, + "width": 520.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1659417197, + "version": 1, + "versionNonce": 228088899, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01179-3967", + "type": "text", + "x": 2184.0, + "y": 4060.0, + "width": 360.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#6941c6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1331882075, + "version": 1, + "versionNonce": 1267570546, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Current draft \u00b7 revision 12", + "originalText": "Current draft \u00b7 revision 12", + "fontSize": 16, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01180-5838", + "type": "text", + "x": 2184.0, + "y": 4118.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1773549917, + "version": 1, + "versionNonce": 1227660565, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ORIGIN", + "originalText": "ORIGIN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01181-3286", + "type": "text", + "x": 2184.0, + "y": 4144.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 305628545, + "version": 1, + "versionNonce": 734555794, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manual \u00b7 you", + "originalText": "Manual \u00b7 you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01182-8335", + "type": "text", + "x": 2544.0, + "y": 4118.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 37557640, + "version": 1, + "versionNonce": 1657834644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "STARTED", + "originalText": "STARTED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01183-9145", + "type": "text", + "x": 2544.0, + "y": 4144.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 563288836, + "version": 1, + "versionNonce": 294730023, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Aug 3, 2026 at 14:32:18", + "originalText": "Aug 3, 2026 at 14:32:18", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01184-4912", + "type": "text", + "x": 2184.0, + "y": 4202.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1662093858, + "version": 1, + "versionNonce": 1550328339, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ELAPSED", + "originalText": "ELAPSED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01185-3101", + "type": "text", + "x": 2184.0, + "y": 4228.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1959356795, + "version": 1, + "versionNonce": 2035843496, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:18", + "originalText": "00:18", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01186-4060", + "type": "text", + "x": 2544.0, + "y": 4202.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 131552508, + "version": 1, + "versionNonce": 1232724232, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "USAGE", + "originalText": "USAGE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01187-4995", + "type": "text", + "x": 2544.0, + "y": 4228.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 151075209, + "version": 1, + "versionNonce": 1859410905, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.03 \u00b7 5.8k tokens", + "originalText": "$0.03 \u00b7 5.8k tokens", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01188-5518", + "type": "line", + "x": 2184.0, + "y": 4284.0, + "width": 732.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 677383659, + "version": 1, + "versionNonce": 1347419982, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 732.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01189-1071", + "type": "text", + "x": 2184.0, + "y": 4312.0, + "width": 170.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1441007233, + "version": 1, + "versionNonce": 226975697, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "STEP TIMELINE", + "originalText": "STEP TIMELINE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01190-2415", + "type": "ellipse", + "x": 2188.0, + "y": 4358.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1770345286, + "version": 1, + "versionNonce": 1228788697, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-01191-7769", + "type": "line", + "x": 2197.0, + "y": 4376.0, + "width": 0.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1105045524, + "version": 1, + "versionNonce": 696804127, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 38.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01192-1244", + "type": "text", + "x": 2224.0, + "y": 4356.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 512907700, + "version": 1, + "versionNonce": 1921226051, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Receive conversation", + "originalText": "Receive conversation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01193-8768", + "type": "text", + "x": 2590.0, + "y": 4356.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 926563154, + "version": 1, + "versionNonce": 142272309, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "0.8s", + "originalText": "0.8s", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01194-7270", + "type": "ellipse", + "x": 2188.0, + "y": 4416.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 675651774, + "version": 1, + "versionNonce": 1949169348, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-01195-8124", + "type": "line", + "x": 2197.0, + "y": 4434.0, + "width": 0.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1179870274, + "version": 1, + "versionNonce": 1132682529, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 38.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01196-9287", + "type": "text", + "x": 2224.0, + "y": 4414.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1009117522, + "version": 1, + "versionNonce": 1739976505, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Classify severity", + "originalText": "Classify severity", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01197-7336", + "type": "text", + "x": 2590.0, + "y": 4414.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 667889774, + "version": 1, + "versionNonce": 1495798106, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "3.4s", + "originalText": "3.4s", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01198-3774", + "type": "ellipse", + "x": 2188.0, + "y": 4474.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "#067647", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1801259882, + "version": 1, + "versionNonce": 2103762335, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-01199-6512", + "type": "line", + "x": 2197.0, + "y": 4492.0, + "width": 0.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 156539986, + "version": 1, + "versionNonce": 865814784, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 38.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01200-6788", + "type": "text", + "x": 2224.0, + "y": 4472.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1646005103, + "version": 1, + "versionNonce": 1044692166, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Route escalation", + "originalText": "Route escalation", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01201-4667", + "type": "text", + "x": 2590.0, + "y": 4472.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#067647", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1206219759, + "version": 1, + "versionNonce": 1500782679, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "0.1s", + "originalText": "0.1s", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01202-2129", + "type": "ellipse", + "x": 2188.0, + "y": 4532.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 163346251, + "version": 1, + "versionNonce": 986772145, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "l-01203-8309", + "type": "line", + "x": 2197.0, + "y": 4550.0, + "width": 0.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 2110921843, + "version": 1, + "versionNonce": 457970947, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.0, + 38.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01204-5532", + "type": "text", + "x": 2224.0, + "y": 4530.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1525502704, + "version": 1, + "versionNonce": 117368523, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Prepare reply", + "originalText": "Prepare reply", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01205-1401", + "type": "text", + "x": 2590.0, + "y": 4530.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 492738000, + "version": 1, + "versionNonce": 1703463687, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "running 13s", + "originalText": "running 13s", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01206-3976", + "type": "ellipse", + "x": 2188.0, + "y": 4590.0, + "width": 18.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 478808473, + "version": 1, + "versionNonce": 1214632562, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01207-4474", + "type": "text", + "x": 2224.0, + "y": 4588.0, + "width": 320.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 430633900, + "version": 1, + "versionNonce": 1882312051, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Post response", + "originalText": "Post response", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01208-3822", + "type": "text", + "x": 2590.0, + "y": 4588.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1630598189, + "version": 1, + "versionNonce": 1444926363, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "queued", + "originalText": "queued", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01209-3689", + "type": "rectangle", + "x": 2964.0, + "y": 3950.0, + "width": 366.0, + "height": 748.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1633113920, + "version": 1, + "versionNonce": 1379032860, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01210-6763", + "type": "text", + "x": 2988.0, + "y": 3974.0, + "width": 100.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1839670436, + "version": 1, + "versionNonce": 1786776765, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "RECORD", + "originalText": "RECORD", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01211-5163", + "type": "text", + "x": 2988.0, + "y": 4010.0, + "width": 23.939999999999998, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1696743897, + "version": 1, + "versionNonce": 437369059, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "RUN", + "originalText": "RUN", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01212-6338", + "type": "rectangle", + "x": 2988.0, + "y": 4032.0, + "width": 318.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 353684181, + "version": 1, + "versionNonce": 2037572353, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01213-2156", + "type": "text", + "x": 3000.0, + "y": 4043.0, + "width": 294.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 334994565, + "version": 1, + "versionNonce": 1338525200, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "R-1042", + "originalText": "R-1042", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01214-8160", + "type": "text", + "x": 2988.0, + "y": 4094.0, + "width": 63.839999999999996, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1901288591, + "version": 1, + "versionNonce": 878191425, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "REVISION", + "originalText": "REVISION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01215-1040", + "type": "rectangle", + "x": 2988.0, + "y": 4116.0, + "width": 318.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 247654491, + "version": 1, + "versionNonce": 1866776779, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01216-2629", + "type": "text", + "x": 3000.0, + "y": 4127.0, + "width": 294.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 441435174, + "version": 1, + "versionNonce": 1461796060, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "draft-r12", + "originalText": "draft-r12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01217-1718", + "type": "text", + "x": 2988.0, + "y": 4178.0, + "width": 87.77999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 394836356, + "version": 1, + "versionNonce": 1297705584, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ENVIRONMENT", + "originalText": "ENVIRONMENT", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01218-4363", + "type": "rectangle", + "x": 2988.0, + "y": 4200.0, + "width": 318.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1288107424, + "version": 1, + "versionNonce": 3526064, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01219-8481", + "type": "text", + "x": 3000.0, + "y": 4211.0, + "width": 294.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 776206322, + "version": 1, + "versionNonce": 305472349, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Production", + "originalText": "Production", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01220-3276", + "type": "text", + "x": 2988.0, + "y": 4268.0, + "width": 140.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1649995498, + "version": 1, + "versionNonce": 2019038184, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "LINKED RUNS", + "originalText": "LINKED RUNS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01221-9131", + "type": "rectangle", + "x": 2988.0, + "y": 4298.0, + "width": 318.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 138531388, + "version": 1, + "versionNonce": 1556457756, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01222-6430", + "type": "text", + "x": 3004.0, + "y": 4314.0, + "width": 240.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 667757809, + "version": 1, + "versionNonce": 1087569968, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "No linked Runs yet", + "originalText": "No linked Runs yet", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01223-3188", + "type": "text", + "x": 3004.0, + "y": 4341.0, + "width": 270.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 857632447, + "version": 1, + "versionNonce": 535125022, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run again will create one.", + "originalText": "Run again will create one.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01224-5851", + "type": "rectangle", + "x": 2988.0, + "y": 4402.0, + "width": 318.0, + "height": 124.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 579299022, + "version": 1, + "versionNonce": 1236769683, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01225-9957", + "type": "text", + "x": 3004.0, + "y": 4422.0, + "width": 250.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 845777303, + "version": 1, + "versionNonce": 2110233744, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "History is immutable", + "originalText": "History is immutable", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01226-6223", + "type": "text", + "x": 3004.0, + "y": 4456.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 676938201, + "version": 1, + "versionNonce": 1748277419, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "A new attempt keeps this record and links back to R-1042.", + "originalText": "A new attempt keeps this record and links back to R-1042.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01227-4035", + "type": "rectangle", + "x": 2988.0, + "y": 4556.0, + "width": 318.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": { + "type": 3 + }, + "seed": 1863020878, + "version": 1, + "versionNonce": 1620719914, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01228-3453", + "type": "text", + "x": 2996.0, + "y": 4565.0, + "width": 302.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-10" + ], + "frameId": "frame-01153-9609", + "roundness": null, + "seed": 1710282958, + "version": 1, + "versionNonce": 603258407, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run again", + "originalText": "Run again", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01229-7206", + "type": "frame", + "x": 3680.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1100322327, + "version": 1, + "versionNonce": 873909714, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "12 Failed Run - recovery creates a new record" + }, + { + "id": "r-01231-9399", + "type": "rectangle", + "x": 3680.0, + "y": 3840.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 178391605, + "version": 1, + "versionNonce": 2146765733, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01232-3243", + "type": "rectangle", + "x": 3680.0, + "y": 3840.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1039501073, + "version": 1, + "versionNonce": 392175463, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01233-8877", + "type": "text", + "x": 3704.0, + "y": 3864.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 2108347747, + "version": 1, + "versionNonce": 1258928364, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01234-3487", + "type": "text", + "x": 3704.0, + "y": 3893.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1816903620, + "version": 1, + "versionNonce": 206082983, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01235-1685", + "type": "ellipse", + "x": 3708.0, + "y": 3960.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 676988241, + "version": 1, + "versionNonce": 1326421613, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01236-9283", + "type": "text", + "x": 3728.0, + "y": 3956.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 554743158, + "version": 1, + "versionNonce": 185512824, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01237-2207", + "type": "rectangle", + "x": 3692.0, + "y": 4000.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1147198950, + "version": 1, + "versionNonce": 1649858192, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01238-7537", + "type": "ellipse", + "x": 3708.0, + "y": 4012.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1247934334, + "version": 1, + "versionNonce": 1622832554, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01239-9412", + "type": "text", + "x": 3728.0, + "y": 4008.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 809619115, + "version": 1, + "versionNonce": 526583985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01240-1891", + "type": "ellipse", + "x": 3708.0, + "y": 4064.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 2120388456, + "version": 1, + "versionNonce": 2088604356, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01241-9067", + "type": "text", + "x": 3728.0, + "y": 4060.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 780847797, + "version": 1, + "versionNonce": 1768503043, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01242-7549", + "type": "text", + "x": 3704.0, + "y": 4686.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 237241791, + "version": 1, + "versionNonce": 893887481, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01243-2300", + "type": "text", + "x": 3704.0, + "y": 4712.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 381194425, + "version": 1, + "versionNonce": 195328722, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01244-8812", + "type": "rectangle", + "x": 3890.0, + "y": 3840.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 566552843, + "version": 1, + "versionNonce": 1012858160, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01245-5288", + "type": "text", + "x": 3920.0, + "y": 3858.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 181886281, + "version": 1, + "versionNonce": 722018103, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run #R-1038", + "originalText": "Run #R-1038", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01246-3780", + "type": "text", + "x": 3920.0, + "y": 3889.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 559158403, + "version": 1, + "versionNonce": 1076162608, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Nightly order sync | Failed at step 4", + "originalText": "Nightly order sync | Failed at step 4", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01247-3017", + "type": "rectangle", + "x": 4792.0, + "y": 3862.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 2077492936, + "version": 1, + "versionNonce": 152951834, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01248-4280", + "type": "text", + "x": 4800.0, + "y": 3871.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 788930255, + "version": 1, + "versionNonce": 1599242650, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edit workflow", + "originalText": "Edit workflow", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01249-3935", + "type": "rectangle", + "x": 4930.0, + "y": 3862.0, + "width": 160.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 791598317, + "version": 1, + "versionNonce": 914681609, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01250-7665", + "type": "text", + "x": 4938.0, + "y": 3871.0, + "width": 144.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 73939047, + "version": 1, + "versionNonce": 1106303569, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry", + "originalText": "Retry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01251-6250", + "type": "rectangle", + "x": 3920.0, + "y": 3950.0, + "width": 1170.0, + "height": 110.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1113481722, + "version": 1, + "versionNonce": 850584401, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01252-8399", + "type": "rectangle", + "x": 3942.0, + "y": 3974.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 968622757, + "version": 1, + "versionNonce": 1159653043, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01253-1678", + "type": "text", + "x": 3952.0, + "y": 3980.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1985948768, + "version": 1, + "versionNonce": 1856047874, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01254-3930", + "type": "text", + "x": 4050.0, + "y": 3978.0, + "width": 560.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1310324378, + "version": 1, + "versionNonce": 85465478, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Warehouse connection is unavailable", + "originalText": "Warehouse connection is unavailable", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01255-9571", + "type": "text", + "x": 4050.0, + "y": 4012.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 4211880, + "version": 1, + "versionNonce": 1546624873, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Step 4 \u00b7 Write completed orders | The connection rejected the request.", + "originalText": "Step 4 \u00b7 Write completed orders | The connection rejected the request.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01256-3212", + "type": "text", + "x": 3942.0, + "y": 4090.0, + "width": 180.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1421680734, + "version": 1, + "versionNonce": 1777974336, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "WHAT HAPPENED", + "originalText": "WHAT HAPPENED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01257-1392", + "type": "rectangle", + "x": 3920.0, + "y": 4120.0, + "width": 720.0, + "height": 246.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 662729508, + "version": 1, + "versionNonce": 720757923, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01258-8497", + "type": "text", + "x": 3944.0, + "y": 4144.0, + "width": 63.839999999999996, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 237146066, + "version": 1, + "versionNonce": 366582185, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "REVISION", + "originalText": "REVISION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01259-5748", + "type": "rectangle", + "x": 3944.0, + "y": 4166.0, + "width": 672.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 2008840631, + "version": 1, + "versionNonce": 2115243815, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01260-7812", + "type": "text", + "x": 3956.0, + "y": 4177.0, + "width": 648.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1556954296, + "version": 1, + "versionNonce": 799619836, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published \u00b7 v9", + "originalText": "Published \u00b7 v9", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01261-9751", + "type": "text", + "x": 3944.0, + "y": 4228.0, + "width": 95.75999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 979666746, + "version": 1, + "versionNonce": 1895313444, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "FAILED AFTER", + "originalText": "FAILED AFTER", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01262-2715", + "type": "rectangle", + "x": 3944.0, + "y": 4250.0, + "width": 320.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1306389927, + "version": 1, + "versionNonce": 1930575929, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01263-4566", + "type": "text", + "x": 3956.0, + "y": 4261.0, + "width": 296.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1489802079, + "version": 1, + "versionNonce": 1303145170, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "00:21", + "originalText": "00:21", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01264-3880", + "type": "text", + "x": 4280.0, + "y": 4228.0, + "width": 39.9, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1168734432, + "version": 1, + "versionNonce": 677069339, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "USAGE", + "originalText": "USAGE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01265-1512", + "type": "rectangle", + "x": 4280.0, + "y": 4250.0, + "width": 336.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 909825182, + "version": 1, + "versionNonce": 1876151563, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01266-4645", + "type": "text", + "x": 4292.0, + "y": 4261.0, + "width": 312.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1601366958, + "version": 1, + "versionNonce": 1198631571, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "$0.04 \u00b7 8.1k tokens", + "originalText": "$0.04 \u00b7 8.1k tokens", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01267-5877", + "type": "text", + "x": 3944.0, + "y": 4322.0, + "width": 620.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1440189315, + "version": 1, + "versionNonce": 2091150718, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "The previous three completed steps remain visible in this Run record.", + "originalText": "The previous three completed steps remain visible in this Run record.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01268-5000", + "type": "text", + "x": 4692.0, + "y": 4090.0, + "width": 130.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 907594617, + "version": 1, + "versionNonce": 100455082, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "RECOVER", + "originalText": "RECOVER", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01269-6666", + "type": "rectangle", + "x": 4670.0, + "y": 4120.0, + "width": 420.0, + "height": 246.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1622922338, + "version": 1, + "versionNonce": 1072444349, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01270-8504", + "type": "text", + "x": 4694.0, + "y": 4144.0, + "width": 330.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1913731855, + "version": 1, + "versionNonce": 1925209403, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "1. Repair the connection", + "originalText": "1. Repair the connection", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01271-9177", + "type": "rectangle", + "x": 4694.0, + "y": 4176.0, + "width": 372.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 578069136, + "version": 1, + "versionNonce": 635772652, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01272-4578", + "type": "text", + "x": 4702.0, + "y": 4185.0, + "width": 356.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1930458048, + "version": 1, + "versionNonce": 457635521, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Open connections", + "originalText": "Open connections", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01273-3603", + "type": "text", + "x": 4694.0, + "y": 4240.0, + "width": 330.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 232700414, + "version": 1, + "versionNonce": 789394228, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2. Retry this Run", + "originalText": "2. Retry this Run", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01274-7476", + "type": "rectangle", + "x": 4694.0, + "y": 4272.0, + "width": 372.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1104971420, + "version": 1, + "versionNonce": 1741012489, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01275-7461", + "type": "text", + "x": 4702.0, + "y": 4281.0, + "width": 356.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1563406924, + "version": 1, + "versionNonce": 1643382988, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry", + "originalText": "Retry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01276-6131", + "type": "text", + "x": 3920.0, + "y": 4408.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1302002480, + "version": 1, + "versionNonce": 945703775, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "RETRY PREVIEW", + "originalText": "RETRY PREVIEW", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01277-6994", + "type": "rectangle", + "x": 3920.0, + "y": 4438.0, + "width": 1170.0, + "height": 204.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 951808252, + "version": 1, + "versionNonce": 163302927, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01278-5435", + "type": "text", + "x": 3944.0, + "y": 4464.0, + "width": 420.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 323116702, + "version": 1, + "versionNonce": 867259155, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "A new Run will be created", + "originalText": "A new Run will be created", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01279-9131", + "type": "text", + "x": 3944.0, + "y": 4506.0, + "width": 720.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 118264686, + "version": 1, + "versionNonce": 388223824, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "New record: R-1043 | Source Run: R-1038 | Published \u00b7 v9", + "originalText": "New record: R-1043 | Source Run: R-1038 | Published \u00b7 v9", + "fontSize": 16, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01280-9629", + "type": "text", + "x": 3944.0, + "y": 4546.0, + "width": 790.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 948178954, + "version": 1, + "versionNonce": 1124882286, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "R-1038 remains Failed. R-1043 starts from step 4 after the connection is repaired.", + "originalText": "R-1038 remains Failed. R-1043 starts from step 4 after the connection is repaired.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01281-2407", + "type": "rectangle", + "x": 4868.0, + "y": 4574.0, + "width": 88.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1549559365, + "version": 1, + "versionNonce": 924216296, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01282-1057", + "type": "text", + "x": 4876.0, + "y": 4583.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1897895684, + "version": 1, + "versionNonce": 1994722396, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Cancel", + "originalText": "Cancel", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01283-4296", + "type": "rectangle", + "x": 4968.0, + "y": 4574.0, + "width": 98.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": { + "type": 3 + }, + "seed": 1203614796, + "version": 1, + "versionNonce": 1012437240, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01284-3283", + "type": "text", + "x": 4976.0, + "y": 4583.0, + "width": 82.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-11" + ], + "frameId": "frame-01229-7206", + "roundness": null, + "seed": 1512095931, + "version": 1, + "versionNonce": 1715065281, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry", + "originalText": "Retry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01285-9253", + "type": "frame", + "x": 160.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 837952270, + "version": 1, + "versionNonce": 1147278256, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "13 Workflows and Activity - states" + }, + { + "id": "r-01287-6271", + "type": "rectangle", + "x": 160.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 612078966, + "version": 1, + "versionNonce": 865550241, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01288-3111", + "type": "rectangle", + "x": 160.0, + "y": 5020.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 163594218, + "version": 1, + "versionNonce": 1072478566, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01289-5272", + "type": "text", + "x": 184.0, + "y": 5044.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 2096582180, + "version": 1, + "versionNonce": 1492254283, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01290-6522", + "type": "text", + "x": 184.0, + "y": 5073.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1321854539, + "version": 1, + "versionNonce": 277782784, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01291-1444", + "type": "ellipse", + "x": 188.0, + "y": 5140.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1987425564, + "version": 1, + "versionNonce": 359823391, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01292-1982", + "type": "text", + "x": 208.0, + "y": 5136.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 941169516, + "version": 1, + "versionNonce": 313279294, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01293-8310", + "type": "rectangle", + "x": 172.0, + "y": 5180.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1894726635, + "version": 1, + "versionNonce": 616128182, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01294-9325", + "type": "ellipse", + "x": 188.0, + "y": 5192.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 707660498, + "version": 1, + "versionNonce": 580704925, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01295-8858", + "type": "text", + "x": 208.0, + "y": 5188.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1456576687, + "version": 1, + "versionNonce": 1928480422, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01296-7048", + "type": "ellipse", + "x": 188.0, + "y": 5244.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 2091366638, + "version": 1, + "versionNonce": 1896072899, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01297-9804", + "type": "text", + "x": 208.0, + "y": 5240.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 548929767, + "version": 1, + "versionNonce": 1133028068, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01298-6645", + "type": "text", + "x": 184.0, + "y": 5866.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1587897835, + "version": 1, + "versionNonce": 192682895, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01299-2761", + "type": "text", + "x": 184.0, + "y": 5892.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1414501597, + "version": 1, + "versionNonce": 1100804952, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01300-2027", + "type": "rectangle", + "x": 370.0, + "y": 5020.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1403976860, + "version": 1, + "versionNonce": 17932670, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01301-5163", + "type": "text", + "x": 400.0, + "y": 5038.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 969083565, + "version": 1, + "versionNonce": 913710751, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows + Activity states", + "originalText": "Workflows + Activity states", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01302-2007", + "type": "text", + "x": 400.0, + "y": 5069.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1220007569, + "version": 1, + "versionNonce": 1763047078, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Empty, loading, error, and compact layouts for the two in-scope areas.", + "originalText": "Empty, loading, error, and compact layouts for the two in-scope areas.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01303-5486", + "type": "rectangle", + "x": 400.0, + "y": 5136.0, + "width": 360.0, + "height": 218.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1996195455, + "version": 1, + "versionNonce": 959178900, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01304-9539", + "type": "text", + "x": 420.0, + "y": 5154.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1622829244, + "version": 1, + "versionNonce": 389599467, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "EMPTY WORKFLOWS", + "originalText": "EMPTY WORKFLOWS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01305-1142", + "type": "text", + "x": 420.0, + "y": 5198.0, + "width": 310.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1292911526, + "version": 1, + "versionNonce": 1329390748, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "No workflows yet", + "originalText": "No workflows yet", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01306-8231", + "type": "text", + "x": 420.0, + "y": 5236.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1715546669, + "version": 1, + "versionNonce": 1567239948, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Create your first workflow directly.", + "originalText": "Create your first workflow directly.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01307-2998", + "type": "rectangle", + "x": 420.0, + "y": 5286.0, + "width": 130.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 706173134, + "version": 1, + "versionNonce": 609629391, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01308-8035", + "type": "text", + "x": 428.0, + "y": 5295.0, + "width": 114.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 330386115, + "version": 1, + "versionNonce": 1641836457, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "New workflow", + "originalText": "New workflow", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01309-6060", + "type": "rectangle", + "x": 790.0, + "y": 5136.0, + "width": 360.0, + "height": 218.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1461648455, + "version": 1, + "versionNonce": 851965182, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01310-3106", + "type": "text", + "x": 810.0, + "y": 5154.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1640201482, + "version": 1, + "versionNonce": 1982628517, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "EMPTY ACTIVITY", + "originalText": "EMPTY ACTIVITY", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01311-4705", + "type": "text", + "x": 810.0, + "y": 5198.0, + "width": 310.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 671177584, + "version": 1, + "versionNonce": 1903356667, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "No Runs match", + "originalText": "No Runs match", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01312-3725", + "type": "text", + "x": 810.0, + "y": 5236.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1147559190, + "version": 1, + "versionNonce": 1929783729, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Change filters or run a workflow.", + "originalText": "Change filters or run a workflow.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01313-2162", + "type": "rectangle", + "x": 810.0, + "y": 5286.0, + "width": 130.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 2103813100, + "version": 1, + "versionNonce": 187422521, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01314-4459", + "type": "text", + "x": 818.0, + "y": 5295.0, + "width": 114.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1516062225, + "version": 1, + "versionNonce": 448568281, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Clear filters", + "originalText": "Clear filters", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01315-9227", + "type": "rectangle", + "x": 1180.0, + "y": 5136.0, + "width": 360.0, + "height": 218.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 442257385, + "version": 1, + "versionNonce": 1534173367, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01316-4243", + "type": "text", + "x": 1200.0, + "y": 5154.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 408718578, + "version": 1, + "versionNonce": 1254500644, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "LOADING ACTIVITY", + "originalText": "LOADING ACTIVITY", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01317-5946", + "type": "rectangle", + "x": 1204.0, + "y": 5198.0, + "width": 312.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#dbeafe", + "backgroundColor": "#dbeafe", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 435887527, + "version": 1, + "versionNonce": 1449171098, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01318-8385", + "type": "rectangle", + "x": 1204.0, + "y": 5240.0, + "width": 278.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#dbeafe", + "backgroundColor": "#dbeafe", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1457467303, + "version": 1, + "versionNonce": 1708913146, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01319-4270", + "type": "rectangle", + "x": 1204.0, + "y": 5282.0, + "width": 244.0, + "height": 18.0, + "angle": 0, + "strokeColor": "#dbeafe", + "backgroundColor": "#dbeafe", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1577885323, + "version": 1, + "versionNonce": 2122564949, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01320-9387", + "type": "rectangle", + "x": 400.0, + "y": 5386.0, + "width": 750.0, + "height": 174.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 800522707, + "version": 1, + "versionNonce": 1263772977, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01321-4635", + "type": "text", + "x": 422.0, + "y": 5408.0, + "width": 240.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1469966617, + "version": 1, + "versionNonce": 1803695655, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ACTIVITY UNAVAILABLE", + "originalText": "ACTIVITY UNAVAILABLE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01322-6285", + "type": "text", + "x": 422.0, + "y": 5444.0, + "width": 520.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 857020542, + "version": 1, + "versionNonce": 70855538, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Run history could not be loaded", + "originalText": "Run history could not be loaded", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01323-1472", + "type": "text", + "x": 422.0, + "y": 5482.0, + "width": 620.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 441373054, + "version": 1, + "versionNonce": 675039383, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Your Runs are still retained. Retry loading without losing search or filters.", + "originalText": "Your Runs are still retained. Retry loading without losing search or filters.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01324-3997", + "type": "rectangle", + "x": 978.0, + "y": 5494.0, + "width": 142.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 731156685, + "version": 1, + "versionNonce": 124216181, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01325-3735", + "type": "text", + "x": 986.0, + "y": 5503.0, + "width": 126.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 816806152, + "version": 1, + "versionNonce": 699341990, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry loading", + "originalText": "Retry loading", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01326-1133", + "type": "rectangle", + "x": 1196.0, + "y": 5386.0, + "width": 370.0, + "height": 438.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 271526854, + "version": 1, + "versionNonce": 1581295718, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01327-3243", + "type": "rectangle", + "x": 1196.0, + "y": 5386.0, + "width": 370.0, + "height": 58.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 90066969, + "version": 1, + "versionNonce": 963459001, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01328-8579", + "type": "text", + "x": 1214.0, + "y": 5404.0, + "width": 140.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 436874491, + "version": 1, + "versionNonce": 1339142088, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01329-9336", + "type": "rectangle", + "x": 1460.0, + "y": 5398.0, + "width": 88.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1858823328, + "version": 1, + "versionNonce": 1734939015, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01330-2873", + "type": "text", + "x": 1468.0, + "y": 5407.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 845796224, + "version": 1, + "versionNonce": 897798933, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Filters", + "originalText": "Filters", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01331-3684", + "type": "rectangle", + "x": 1210.0, + "y": 5462.0, + "width": 342.0, + "height": 92.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 639009042, + "version": 1, + "versionNonce": 1426534409, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01332-8818", + "type": "rectangle", + "x": 1224.0, + "y": 5476.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 1467512687, + "version": 1, + "versionNonce": 2142026268, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01333-2508", + "type": "text", + "x": 1234.0, + "y": 5482.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 899296608, + "version": 1, + "versionNonce": 1460826554, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Running", + "originalText": "Running", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01334-9491", + "type": "text", + "x": 1224.0, + "y": 5510.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1616168190, + "version": 1, + "versionNonce": 881955040, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Customer escalation triage", + "originalText": "Customer escalation triage", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01335-7083", + "type": "text", + "x": 1426.0, + "y": 5510.0, + "width": 100.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 212521355, + "version": 1, + "versionNonce": 299752724, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Draft \u00b7 r12", + "originalText": "Draft \u00b7 r12", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "right", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01336-2726", + "type": "rectangle", + "x": 1210.0, + "y": 5570.0, + "width": 342.0, + "height": 92.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 712657376, + "version": 1, + "versionNonce": 966260601, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01337-6447", + "type": "rectangle", + "x": 1224.0, + "y": 5584.0, + "width": 94.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 501507969, + "version": 1, + "versionNonce": 174670523, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01338-4506", + "type": "text", + "x": 1234.0, + "y": 5590.0, + "width": 74.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 927414737, + "version": 1, + "versionNonce": 456851652, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Needs you", + "originalText": "Needs you", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01339-3827", + "type": "text", + "x": 1224.0, + "y": 5618.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 713187849, + "version": 1, + "versionNonce": 780352816, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Invoice follow-up", + "originalText": "Invoice follow-up", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01340-9862", + "type": "text", + "x": 1426.0, + "y": 5618.0, + "width": 100.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1542864462, + "version": 1, + "versionNonce": 1876495651, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published \u00b7 v4", + "originalText": "Published \u00b7 v4", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "right", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01341-7236", + "type": "rectangle", + "x": 1210.0, + "y": 5678.0, + "width": 342.0, + "height": 92.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 2021087663, + "version": 1, + "versionNonce": 57507750, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01342-6115", + "type": "rectangle", + "x": 1224.0, + "y": 5692.0, + "width": 70.8, + "height": 28.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": { + "type": 3 + }, + "seed": 122460687, + "version": 1, + "versionNonce": 1327505561, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01343-9096", + "type": "text", + "x": 1234.0, + "y": 5698.0, + "width": 50.8, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1285392862, + "version": 1, + "versionNonce": 1639697066, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Failed", + "originalText": "Failed", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01344-6566", + "type": "text", + "x": 1224.0, + "y": 5726.0, + "width": 210.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 652403333, + "version": 1, + "versionNonce": 623668003, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Nightly order sync", + "originalText": "Nightly order sync", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01345-9119", + "type": "text", + "x": 1426.0, + "y": 5726.0, + "width": 100.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1201163218, + "version": 1, + "versionNonce": 1666422737, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Published \u00b7 v9", + "originalText": "Published \u00b7 v9", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "right", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01346-6639", + "type": "text", + "x": 400.0, + "y": 5886.0, + "width": 820.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-12" + ], + "frameId": "frame-01285-9253", + "roundness": null, + "seed": 1448887915, + "version": 1, + "versionNonce": 325478459, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Compact layouts preserve status, workflow, revision, and recovery actions before secondary metrics.", + "originalText": "Compact layouts preserve status, workflow, revision, and recovery actions before secondary metrics.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01347-2804", + "type": "frame", + "x": 1920.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1533077920, + "version": 1, + "versionNonce": 854519233, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "14 Settings - AI defaults" + }, + { + "id": "r-01349-4279", + "type": "rectangle", + "x": 1920.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 235997616, + "version": 1, + "versionNonce": 1108047664, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01350-4146", + "type": "rectangle", + "x": 1920.0, + "y": 5020.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 235051775, + "version": 1, + "versionNonce": 1958461662, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01351-6493", + "type": "text", + "x": 1944.0, + "y": 5044.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1569738610, + "version": 1, + "versionNonce": 995855571, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01352-3617", + "type": "text", + "x": 1944.0, + "y": 5073.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1196677308, + "version": 1, + "versionNonce": 809040478, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01353-4144", + "type": "ellipse", + "x": 1948.0, + "y": 5140.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1234709496, + "version": 1, + "versionNonce": 892732285, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01354-1233", + "type": "text", + "x": 1968.0, + "y": 5136.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 564233452, + "version": 1, + "versionNonce": 1039279757, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01355-1456", + "type": "ellipse", + "x": 1948.0, + "y": 5192.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1752785355, + "version": 1, + "versionNonce": 273852382, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01356-6305", + "type": "text", + "x": 1968.0, + "y": 5188.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 195088846, + "version": 1, + "versionNonce": 303644954, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01357-8161", + "type": "rectangle", + "x": 1932.0, + "y": 5232.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": { + "type": 3 + }, + "seed": 1154511956, + "version": 1, + "versionNonce": 1687869133, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01358-9303", + "type": "ellipse", + "x": 1948.0, + "y": 5244.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 907222365, + "version": 1, + "versionNonce": 1144244503, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01359-9807", + "type": "text", + "x": 1968.0, + "y": 5240.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 238830539, + "version": 1, + "versionNonce": 1339332096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01360-3862", + "type": "text", + "x": 1944.0, + "y": 5866.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1851320176, + "version": 1, + "versionNonce": 1364796261, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01361-9750", + "type": "text", + "x": 1944.0, + "y": 5892.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1040103525, + "version": 1, + "versionNonce": 1563104544, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01362-6774", + "type": "rectangle", + "x": 2130.0, + "y": 5020.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1375633141, + "version": 1, + "versionNonce": 808956731, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01363-4783", + "type": "text", + "x": 2160.0, + "y": 5038.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1140975957, + "version": 1, + "versionNonce": 1864936054, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01364-7700", + "type": "text", + "x": 2160.0, + "y": 5069.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1660488457, + "version": 1, + "versionNonce": 2132496547, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Personal defaults and access.", + "originalText": "Personal defaults and access.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01365-6831", + "type": "rectangle", + "x": 2156.0, + "y": 5136.0, + "width": 176.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#eff8ff", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": { + "type": 3 + }, + "seed": 1228429804, + "version": 1, + "versionNonce": 628430253, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01366-7358", + "type": "text", + "x": 2168.0, + "y": 5148.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1770242956, + "version": 1, + "versionNonce": 547850179, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01367-1494", + "type": "text", + "x": 2204.0, + "y": 5147.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1473453840, + "version": 1, + "versionNonce": 594590762, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01368-1586", + "type": "text", + "x": 2168.0, + "y": 5196.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1162335614, + "version": 1, + "versionNonce": 1537169262, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ID", + "originalText": "ID", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01369-7164", + "type": "text", + "x": 2204.0, + "y": 5195.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1330148973, + "version": 1, + "versionNonce": 2099461800, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01370-5509", + "type": "text", + "x": 2168.0, + "y": 5244.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1059882503, + "version": 1, + "versionNonce": 404781872, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "", + "originalText": "", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01371-3657", + "type": "text", + "x": 2204.0, + "y": 5243.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1821916848, + "version": 1, + "versionNonce": 253145521, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01372-2410", + "type": "text", + "x": 2366.0, + "y": 5136.0, + "width": 360.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1546359889, + "version": 1, + "versionNonce": 1697836586, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01373-9691", + "type": "text", + "x": 2366.0, + "y": 5172.0, + "width": 650.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 578933374, + "version": 1, + "versionNonce": 2042262429, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Choose the service and model used by new Chat, Studio, and global tool sessions without an override.", + "originalText": "Choose the service and model used by new Chat, Studio, and global tool sessions without an override.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01374-3766", + "type": "line", + "x": 2366.0, + "y": 5210.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 845071362, + "version": 1, + "versionNonce": 1616552981, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01375-1947", + "type": "text", + "x": 2366.0, + "y": 5258.0, + "width": 220.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 438466788, + "version": 1, + "versionNonce": 1220833202, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Preferred service", + "originalText": "Preferred service", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01376-5009", + "type": "text", + "x": 2366.0, + "y": 5286.0, + "width": 230.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1341085039, + "version": 1, + "versionNonce": 1323915280, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Gateway or exact connected service.", + "originalText": "Gateway or exact connected service.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01377-6547", + "type": "rectangle", + "x": 2636.0, + "y": 5248.0, + "width": 694.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": { + "type": 3 + }, + "seed": 503970770, + "version": 1, + "versionNonce": 1379977860, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01378-8546", + "type": "text", + "x": 2652.0, + "y": 5263.0, + "width": 654.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 239559871, + "version": 1, + "versionNonce": 1730836008, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NyxID Gateway", + "originalText": "NyxID Gateway", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01379-2664", + "type": "line", + "x": 2366.0, + "y": 5330.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 28262857, + "version": 1, + "versionNonce": 1757342685, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01380-5854", + "type": "text", + "x": 2366.0, + "y": 5362.0, + "width": 220.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 967889422, + "version": 1, + "versionNonce": 1381037751, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Default model", + "originalText": "Default model", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01381-4349", + "type": "text", + "x": 2366.0, + "y": 5390.0, + "width": 230.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1604850960, + "version": 1, + "versionNonce": 447205049, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Leave unset to use the service default.", + "originalText": "Leave unset to use the service default.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01382-4793", + "type": "rectangle", + "x": 2636.0, + "y": 5352.0, + "width": 694.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": { + "type": 3 + }, + "seed": 884669658, + "version": 1, + "versionNonce": 227123271, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01383-9722", + "type": "text", + "x": 2652.0, + "y": 5367.0, + "width": 654.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 762863685, + "version": 1, + "versionNonce": 669710784, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "gpt-4o-mini", + "originalText": "gpt-4o-mini", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01384-5014", + "type": "line", + "x": 2366.0, + "y": 5434.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-13" + ], + "frameId": "frame-01347-2804", + "roundness": null, + "seed": 1893589099, + "version": 1, + "versionNonce": 392155568, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "frame-01385-8489", + "type": "frame", + "x": 3680.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1983501480, + "version": 1, + "versionNonce": 1920796021, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "15 Settings - save and recovery states" + }, + { + "id": "r-01387-2030", + "type": "rectangle", + "x": 3680.0, + "y": 5020.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 63740086, + "version": 1, + "versionNonce": 1382093752, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01388-5164", + "type": "rectangle", + "x": 3680.0, + "y": 5020.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1519159249, + "version": 1, + "versionNonce": 1596157500, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01389-6193", + "type": "text", + "x": 3704.0, + "y": 5044.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 63969348, + "version": 1, + "versionNonce": 1234395593, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01390-9162", + "type": "text", + "x": 3704.0, + "y": 5073.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 820445153, + "version": 1, + "versionNonce": 1582892083, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01391-6741", + "type": "ellipse", + "x": 3708.0, + "y": 5140.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1618358921, + "version": 1, + "versionNonce": 2084837080, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01392-6050", + "type": "text", + "x": 3728.0, + "y": 5136.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 2097232794, + "version": 1, + "versionNonce": 835295425, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01393-8627", + "type": "ellipse", + "x": 3708.0, + "y": 5192.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1981694405, + "version": 1, + "versionNonce": 284850049, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01394-5635", + "type": "text", + "x": 3728.0, + "y": 5188.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1324982721, + "version": 1, + "versionNonce": 669024849, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01395-6551", + "type": "rectangle", + "x": 3692.0, + "y": 5232.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 288158373, + "version": 1, + "versionNonce": 505424483, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01396-1871", + "type": "ellipse", + "x": 3708.0, + "y": 5244.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1289805619, + "version": 1, + "versionNonce": 1968801829, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01397-8122", + "type": "text", + "x": 3728.0, + "y": 5240.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1439073919, + "version": 1, + "versionNonce": 460066283, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01398-4057", + "type": "text", + "x": 3704.0, + "y": 5866.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 855073383, + "version": 1, + "versionNonce": 316717962, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01399-7525", + "type": "text", + "x": 3704.0, + "y": 5892.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 641187854, + "version": 1, + "versionNonce": 1821823180, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01400-2312", + "type": "rectangle", + "x": 3890.0, + "y": 5020.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1025748692, + "version": 1, + "versionNonce": 382541464, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01401-8313", + "type": "text", + "x": 3920.0, + "y": 5038.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1136551013, + "version": 1, + "versionNonce": 265510148, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings states", + "originalText": "Settings states", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01402-8945", + "type": "text", + "x": 3920.0, + "y": 5069.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1240674555, + "version": 1, + "versionNonce": 1634944198, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Dirty, accepted, fallback, and catalog recovery without dashboard noise.", + "originalText": "Dirty, accepted, fallback, and catalog recovery without dashboard noise.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01403-7394", + "type": "rectangle", + "x": 3916.0, + "y": 5136.0, + "width": 176.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#eff8ff", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 337155779, + "version": 1, + "versionNonce": 33651577, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01404-6764", + "type": "text", + "x": 3928.0, + "y": 5148.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1188764318, + "version": 1, + "versionNonce": 449414398, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01405-2664", + "type": "text", + "x": 3964.0, + "y": 5147.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 767626005, + "version": 1, + "versionNonce": 765290798, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01406-4555", + "type": "text", + "x": 3928.0, + "y": 5196.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1304795943, + "version": 1, + "versionNonce": 238392434, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ID", + "originalText": "ID", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01407-9252", + "type": "text", + "x": 3964.0, + "y": 5195.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 669924908, + "version": 1, + "versionNonce": 1321580823, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01408-3484", + "type": "text", + "x": 3928.0, + "y": 5244.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 296116468, + "version": 1, + "versionNonce": 1587498787, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "", + "originalText": "", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01409-4114", + "type": "text", + "x": 3964.0, + "y": 5243.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1121012824, + "version": 1, + "versionNonce": 1848762673, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01410-7008", + "type": "text", + "x": 4126.0, + "y": 5136.0, + "width": 340.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1754221974, + "version": 1, + "versionNonce": 143322266, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01411-5221", + "type": "rectangle", + "x": 4964.0, + "y": 5136.0, + "width": 78.6, + "height": 28.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1055506233, + "version": 1, + "versionNonce": 910456120, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01412-4723", + "type": "text", + "x": 4974.0, + "y": 5142.0, + "width": 58.599999999999994, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 41334996, + "version": 1, + "versionNonce": 542713502, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Unsaved", + "originalText": "Unsaved", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01413-6885", + "type": "line", + "x": 4126.0, + "y": 5194.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 123653659, + "version": 1, + "versionNonce": 848274341, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "r-01414-4180", + "type": "rectangle", + "x": 4126.0, + "y": 5228.0, + "width": 309.3333333333333, + "height": 218.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1144335061, + "version": 1, + "versionNonce": 180028715, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01415-3873", + "type": "text", + "x": 4142.0, + "y": 5244.0, + "width": 277.3333333333333, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 2114741770, + "version": 1, + "versionNonce": 794121747, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "UNSAVED", + "originalText": "UNSAVED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01416-6071", + "type": "text", + "x": 4142.0, + "y": 5280.0, + "width": 277.3333333333333, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1022692486, + "version": 1, + "versionNonce": 1684613620, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Sticky save bar appears", + "originalText": "Sticky save bar appears", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01417-3736", + "type": "text", + "x": 4142.0, + "y": 5316.0, + "width": 277.3333333333333, + "height": 20.3, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1600234081, + "version": 1, + "versionNonce": 259607857, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Discard and Save changes stay close to the edited defaults.", + "originalText": "Discard and Save changes stay close to the edited defaults.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.45 + }, + { + "id": "r-01418-2599", + "type": "rectangle", + "x": 4142.0, + "y": 5394.0, + "width": 92.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 814948283, + "version": 1, + "versionNonce": 273070971, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01419-2195", + "type": "text", + "x": 4150.0, + "y": 5403.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 531244682, + "version": 1, + "versionNonce": 1069602640, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Discard", + "originalText": "Discard", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01420-8868", + "type": "rectangle", + "x": 4244.0, + "y": 5394.0, + "width": 122.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 530062556, + "version": 1, + "versionNonce": 574008512, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01421-3479", + "type": "text", + "x": 4252.0, + "y": 5403.0, + "width": 106.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1770321324, + "version": 1, + "versionNonce": 1319312186, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save changes", + "originalText": "Save changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01422-9696", + "type": "rectangle", + "x": 4453.333333333333, + "y": 5228.0, + "width": 309.3333333333333, + "height": 218.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1613802427, + "version": 1, + "versionNonce": 48210767, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01423-5279", + "type": "text", + "x": 4469.333333333333, + "y": 5244.0, + "width": 277.3333333333333, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1783192645, + "version": 1, + "versionNonce": 768662865, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ACCEPTED", + "originalText": "ACCEPTED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01424-3417", + "type": "text", + "x": 4469.333333333333, + "y": 5280.0, + "width": 277.3333333333333, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 139661205, + "version": 1, + "versionNonce": 2106079533, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Confirming saved values", + "originalText": "Confirming saved values", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01425-1979", + "type": "text", + "x": 4469.333333333333, + "y": 5316.0, + "width": 277.3333333333333, + "height": 20.3, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 303950440, + "version": 1, + "versionNonce": 1979590044, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Keep the submitted exact service visible until the committed values are observed.", + "originalText": "Keep the submitted exact service visible until the committed values are observed.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.45 + }, + { + "id": "r-01426-2879", + "type": "rectangle", + "x": 4469.333333333333, + "y": 5394.0, + "width": 172.2, + "height": 28.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 912800381, + "version": 1, + "versionNonce": 671836540, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01427-3949", + "type": "text", + "x": 4479.333333333333, + "y": 5400.0, + "width": 152.2, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 148947539, + "version": 1, + "versionNonce": 729742939, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Observation pending", + "originalText": "Observation pending", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01428-3241", + "type": "rectangle", + "x": 4780.666666666667, + "y": 5228.0, + "width": 309.3333333333333, + "height": 218.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1295900234, + "version": 1, + "versionNonce": 1592416572, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01429-1435", + "type": "text", + "x": 4796.666666666667, + "y": 5244.0, + "width": 277.3333333333333, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 736887116, + "version": 1, + "versionNonce": 384582791, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "FALLBACK", + "originalText": "FALLBACK", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01430-8537", + "type": "text", + "x": 4796.666666666667, + "y": 5280.0, + "width": 277.3333333333333, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1735073812, + "version": 1, + "versionNonce": 87612028, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Effective route changed", + "originalText": "Effective route changed", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01431-3379", + "type": "text", + "x": 4796.666666666667, + "y": 5316.0, + "width": 277.3333333333333, + "height": 20.3, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1321285048, + "version": 1, + "versionNonce": 655495687, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Show fallback only when the selected service is unavailable.", + "originalText": "Show fallback only when the selected service is unavailable.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.45 + }, + { + "id": "r-01432-4607", + "type": "rectangle", + "x": 4796.666666666667, + "y": 5394.0, + "width": 164.4, + "height": 28.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 2085603833, + "version": 1, + "versionNonce": 424923271, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01433-1166", + "type": "text", + "x": 4806.666666666667, + "y": 5400.0, + "width": 144.4, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1842049247, + "version": 1, + "versionNonce": 1023428090, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Effective: Gateway", + "originalText": "Effective: Gateway", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01434-6080", + "type": "rectangle", + "x": 4126.0, + "y": 5480.0, + "width": 473.0, + "height": 220.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1930724197, + "version": 1, + "versionNonce": 881209757, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01435-9281", + "type": "text", + "x": 4144.0, + "y": 5498.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 916507118, + "version": 1, + "versionNonce": 254043194, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "CATALOG UNAVAILABLE", + "originalText": "CATALOG UNAVAILABLE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01436-3967", + "type": "text", + "x": 4144.0, + "y": 5536.0, + "width": 380.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 874831448, + "version": 1, + "versionNonce": 800399372, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Keep stored defaults visible", + "originalText": "Keep stored defaults visible", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01437-9725", + "type": "text", + "x": 4144.0, + "y": 5572.0, + "width": 400.0, + "height": 20.3, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 306108310, + "version": 1, + "versionNonce": 435057985, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Disable service and model editing until provider inventory can be loaded.", + "originalText": "Disable service and model editing until provider inventory can be loaded.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.45 + }, + { + "id": "r-01438-4276", + "type": "rectangle", + "x": 4144.0, + "y": 5640.0, + "width": 88.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "#fffaeb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1297614898, + "version": 1, + "versionNonce": 7619594, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01439-9008", + "type": "text", + "x": 4152.0, + "y": 5649.0, + "width": 72.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b54708", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 755528004, + "version": 1, + "versionNonce": 1694202759, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Retry", + "originalText": "Retry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01440-7984", + "type": "rectangle", + "x": 4617.0, + "y": 5480.0, + "width": 473.0, + "height": 220.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 171127586, + "version": 1, + "versionNonce": 1317110207, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01441-9882", + "type": "text", + "x": 4635.0, + "y": 5498.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1636343763, + "version": 1, + "versionNonce": 311141415, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SAVE FAILED", + "originalText": "SAVE FAILED", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01442-5545", + "type": "text", + "x": 4635.0, + "y": 5536.0, + "width": 380.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1457862153, + "version": 1, + "versionNonce": 1490482846, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Edits remain intact", + "originalText": "Edits remain intact", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01443-9386", + "type": "text", + "x": 4635.0, + "y": 5572.0, + "width": 400.0, + "height": 20.3, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1225023547, + "version": 1, + "versionNonce": 986366319, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "The exact service and model stay editable. Retry without rebuilding the draft.", + "originalText": "The exact service and model stay editable. Retry without rebuilding the draft.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.45 + }, + { + "id": "r-01444-1322", + "type": "rectangle", + "x": 4635.0, + "y": 5640.0, + "width": 126.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 2126538852, + "version": 1, + "versionNonce": 1485013824, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01445-9399", + "type": "text", + "x": 4643.0, + "y": 5649.0, + "width": 110.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 733711489, + "version": 1, + "versionNonce": 2090793514, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save changes", + "originalText": "Save changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01446-5383", + "type": "rectangle", + "x": 4126.0, + "y": 5782.0, + "width": 964.0, + "height": 72.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#1d2939", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1577761729, + "version": 1, + "versionNonce": 940431788, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01447-1234", + "type": "text", + "x": 4144.0, + "y": 5796.0, + "width": 280.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1086021772, + "version": 1, + "versionNonce": 1922292976, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Unsaved changes", + "originalText": "Unsaved changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01448-1371", + "type": "text", + "x": 4144.0, + "y": 5822.0, + "width": 420.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 1665378024, + "version": 1, + "versionNonce": 1178959511, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Your AI defaults have not been saved.", + "originalText": "Your AI defaults have not been saved.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01449-2999", + "type": "rectangle", + "x": 4846.0, + "y": 5799.0, + "width": 92.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#1d2939", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 1813035084, + "version": 1, + "versionNonce": 691994728, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01450-3232", + "type": "text", + "x": 4854.0, + "y": 5808.0, + "width": 76.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 418608765, + "version": 1, + "versionNonce": 1531214311, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Discard", + "originalText": "Discard", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01451-8437", + "type": "rectangle", + "x": 4950.0, + "y": 5799.0, + "width": 122.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": { + "type": 3 + }, + "seed": 36465954, + "version": 1, + "versionNonce": 30130200, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01452-6912", + "type": "text", + "x": 4958.0, + "y": 5808.0, + "width": 106.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-14" + ], + "frameId": "frame-01385-8489", + "roundness": null, + "seed": 893299798, + "version": 1, + "versionNonce": 2130008099, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save changes", + "originalText": "Save changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01453-1672", + "type": "frame", + "x": 160.0, + "y": 6200.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 484681397, + "version": 1, + "versionNonce": 1133747951, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "16 Settings - Account" + }, + { + "id": "r-01455-5780", + "type": "rectangle", + "x": 160.0, + "y": 6200.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 468592318, + "version": 1, + "versionNonce": 1992747004, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01456-2657", + "type": "rectangle", + "x": 160.0, + "y": 6200.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1305307619, + "version": 1, + "versionNonce": 1395522119, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01457-8301", + "type": "text", + "x": 184.0, + "y": 6224.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1383550855, + "version": 1, + "versionNonce": 1263821023, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01458-8067", + "type": "text", + "x": 184.0, + "y": 6253.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1087579687, + "version": 1, + "versionNonce": 1002889266, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01459-5254", + "type": "ellipse", + "x": 188.0, + "y": 6320.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 367854330, + "version": 1, + "versionNonce": 18848022, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01460-8125", + "type": "text", + "x": 208.0, + "y": 6316.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 533488267, + "version": 1, + "versionNonce": 976581283, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01461-3945", + "type": "ellipse", + "x": 188.0, + "y": 6372.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 667538718, + "version": 1, + "versionNonce": 769404180, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01462-3246", + "type": "text", + "x": 208.0, + "y": 6368.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1748481628, + "version": 1, + "versionNonce": 1445292877, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01463-8163", + "type": "rectangle", + "x": 172.0, + "y": 6412.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": { + "type": 3 + }, + "seed": 57045393, + "version": 1, + "versionNonce": 493195431, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01464-4474", + "type": "ellipse", + "x": 188.0, + "y": 6424.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1744942403, + "version": 1, + "versionNonce": 937451252, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01465-7694", + "type": "text", + "x": 208.0, + "y": 6420.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 934483270, + "version": 1, + "versionNonce": 7146933, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01466-1695", + "type": "text", + "x": 184.0, + "y": 7046.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 778162051, + "version": 1, + "versionNonce": 2026150080, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01467-2649", + "type": "text", + "x": 184.0, + "y": 7072.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 990348876, + "version": 1, + "versionNonce": 1088532350, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01468-2809", + "type": "rectangle", + "x": 370.0, + "y": 6200.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 2089855605, + "version": 1, + "versionNonce": 1748557073, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01469-4990", + "type": "text", + "x": 400.0, + "y": 6218.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1871338950, + "version": 1, + "versionNonce": 808166133, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01470-7455", + "type": "text", + "x": 400.0, + "y": 6249.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1762342695, + "version": 1, + "versionNonce": 2109791759, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Identity and service access for this browser.", + "originalText": "Identity and service access for this browser.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01471-8175", + "type": "text", + "x": 408.0, + "y": 6328.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 273259037, + "version": 1, + "versionNonce": 676321177, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01472-2816", + "type": "text", + "x": 444.0, + "y": 6327.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 775824419, + "version": 1, + "versionNonce": 1884574902, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01473-3031", + "type": "rectangle", + "x": 396.0, + "y": 6364.0, + "width": 176.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#eff8ff", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": { + "type": 3 + }, + "seed": 462537833, + "version": 1, + "versionNonce": 1898455527, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01474-8577", + "type": "text", + "x": 408.0, + "y": 6376.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 666037192, + "version": 1, + "versionNonce": 1400673992, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ID", + "originalText": "ID", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01475-4196", + "type": "text", + "x": 444.0, + "y": 6375.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1983586580, + "version": 1, + "versionNonce": 929691373, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01476-6525", + "type": "text", + "x": 408.0, + "y": 6424.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 300003326, + "version": 1, + "versionNonce": 263921816, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "", + "originalText": "", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01477-6679", + "type": "text", + "x": 444.0, + "y": 6423.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1980031760, + "version": 1, + "versionNonce": 467220791, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01478-3999", + "type": "text", + "x": 606.0, + "y": 6316.0, + "width": 340.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 2059380956, + "version": 1, + "versionNonce": 1599607209, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01479-1540", + "type": "text", + "x": 606.0, + "y": 6352.0, + "width": 560.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 205415923, + "version": 1, + "versionNonce": 1162286098, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Identity and service access for this browser session.", + "originalText": "Identity and service access for this browser session.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01480-6336", + "type": "line", + "x": 606.0, + "y": 6390.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1396856795, + "version": 1, + "versionNonce": 43484434, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01481-6457", + "type": "text", + "x": 606.0, + "y": 6420.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 139008272, + "version": 1, + "versionNonce": 1546218043, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Profile", + "originalText": "Profile", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01482-5172", + "type": "rectangle", + "x": 1476.0, + "y": 6420.0, + "width": 94.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "#fef3f2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": { + "type": 3 + }, + "seed": 31843376, + "version": 1, + "versionNonce": 1936411226, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01483-1868", + "type": "text", + "x": 1484.0, + "y": 6429.0, + "width": 78.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#b42318", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 454233759, + "version": 1, + "versionNonce": 2131558527, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Sign out", + "originalText": "Sign out", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01484-8843", + "type": "ellipse", + "x": 606.0, + "y": 6468.0, + "width": 50.0, + "height": 50.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1788887638, + "version": 1, + "versionNonce": 847109655, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01485-9362", + "type": "text", + "x": 674.0, + "y": 6468.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 975632672, + "version": 1, + "versionNonce": 930484791, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Calvin Tan", + "originalText": "Calvin Tan", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01486-7924", + "type": "text", + "x": 674.0, + "y": 6498.0, + "width": 300.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1822855844, + "version": 1, + "versionNonce": 749759910, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "calvin@example.com", + "originalText": "calvin@example.com", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01487-7442", + "type": "rectangle", + "x": 606.0, + "y": 6542.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1165781030, + "version": 1, + "versionNonce": 822102335, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01488-3329", + "type": "text", + "x": 620.0, + "y": 6556.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1336095133, + "version": 1, + "versionNonce": 1850146034, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "USER ID", + "originalText": "USER ID", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01489-3098", + "type": "text", + "x": 620.0, + "y": 6586.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 230197460, + "version": 1, + "versionNonce": 198137655, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "usr_7c9142...e8a13f", + "originalText": "usr_7c9142...e8a13f", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01490-7475", + "type": "rectangle", + "x": 1088.0, + "y": 6542.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1008377440, + "version": 1, + "versionNonce": 1457404723, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01491-9349", + "type": "text", + "x": 1102.0, + "y": 6556.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 564475850, + "version": 1, + "versionNonce": 903770595, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ROLES", + "originalText": "ROLES", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01492-1494", + "type": "text", + "x": 1102.0, + "y": 6586.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 718840216, + "version": 1, + "versionNonce": 939179069, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "admin, operator", + "originalText": "admin, operator", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01493-5140", + "type": "rectangle", + "x": 606.0, + "y": 6618.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 722768783, + "version": 1, + "versionNonce": 357790906, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01494-7569", + "type": "text", + "x": 620.0, + "y": 6632.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 188536010, + "version": 1, + "versionNonce": 1513245803, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "GROUPS", + "originalText": "GROUPS", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01495-7832", + "type": "text", + "x": 620.0, + "y": 6662.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 371963551, + "version": 1, + "versionNonce": 88451668, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "platform", + "originalText": "platform", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01496-4927", + "type": "rectangle", + "x": 1088.0, + "y": 6618.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 779227246, + "version": 1, + "versionNonce": 1901134174, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01497-5762", + "type": "text", + "x": 1102.0, + "y": 6632.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1901101936, + "version": 1, + "versionNonce": 1321551567, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "EMAIL", + "originalText": "EMAIL", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01498-4732", + "type": "text", + "x": 1102.0, + "y": 6662.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 587120480, + "version": 1, + "versionNonce": 754463524, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Verified", + "originalText": "Verified", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01499-9048", + "type": "line", + "x": 606.0, + "y": 6698.0, + "width": 964.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 748748423, + "version": 1, + "versionNonce": 189893986, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 964.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01500-8307", + "type": "text", + "x": 606.0, + "y": 6720.0, + "width": 280.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1570820449, + "version": 1, + "versionNonce": 98170457, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Authentication", + "originalText": "Authentication", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01501-6892", + "type": "rectangle", + "x": 1362.0, + "y": 6720.0, + "width": 208.0, + "height": 38.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": { + "type": 3 + }, + "seed": 1727722122, + "version": 1, + "versionNonce": 1841634925, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01502-6647", + "type": "text", + "x": 1370.0, + "y": 6729.0, + "width": 192.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 151115467, + "version": 1, + "versionNonce": 1822014334, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Manage service access", + "originalText": "Manage service access", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01503-8579", + "type": "rectangle", + "x": 606.0, + "y": 6776.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 808373594, + "version": 1, + "versionNonce": 2074224923, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01504-4044", + "type": "text", + "x": 620.0, + "y": 6790.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 689745965, + "version": 1, + "versionNonce": 96319711, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SESSION EXPIRES", + "originalText": "SESSION EXPIRES", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01505-4745", + "type": "text", + "x": 620.0, + "y": 6820.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1019550595, + "version": 1, + "versionNonce": 1212116739, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "2026/8/4 18:30", + "originalText": "2026/8/4 18:30", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01506-2700", + "type": "rectangle", + "x": 1088.0, + "y": 6776.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 695952407, + "version": 1, + "versionNonce": 834356280, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01507-7459", + "type": "text", + "x": 1102.0, + "y": 6790.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 112978896, + "version": 1, + "versionNonce": 803436872, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "PROVIDER", + "originalText": "PROVIDER", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01508-6799", + "type": "text", + "x": 1102.0, + "y": 6820.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1251456328, + "version": 1, + "versionNonce": 1836881257, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NyxID", + "originalText": "NyxID", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01509-5755", + "type": "rectangle", + "x": 606.0, + "y": 6852.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 770153418, + "version": 1, + "versionNonce": 374692706, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01510-9613", + "type": "text", + "x": 620.0, + "y": 6866.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 1559801653, + "version": 1, + "versionNonce": 2102578667, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SCOPE", + "originalText": "SCOPE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01511-8521", + "type": "text", + "x": 620.0, + "y": 6896.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 188367656, + "version": 1, + "versionNonce": 682049254, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "openid profile email", + "originalText": "openid profile email", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01512-6563", + "type": "rectangle", + "x": 1088.0, + "y": 6852.0, + "width": 482.0, + "height": 76.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 751958461, + "version": 1, + "versionNonce": 187382147, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01513-8179", + "type": "text", + "x": 1102.0, + "y": 6866.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 637511676, + "version": 1, + "versionNonce": 1671783096, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "BROWSER TOKEN REFRESH", + "originalText": "BROWSER TOKEN REFRESH", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01514-3520", + "type": "text", + "x": 1102.0, + "y": 6896.0, + "width": 454.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 2144028933, + "version": 1, + "versionNonce": 929492645, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Disabled", + "originalText": "Disabled", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01515-3655", + "type": "rectangle", + "x": 606.0, + "y": 6986.0, + "width": 964.0, + "height": 66.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#f8fafc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": { + "type": 3 + }, + "seed": 1208874976, + "version": 1, + "versionNonce": 717741904, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01516-4321", + "type": "text", + "x": 622.0, + "y": 7000.0, + "width": 180.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 512864919, + "version": 1, + "versionNonce": 281932279, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SIGNED-OUT STATE", + "originalText": "SIGNED-OUT STATE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01517-1664", + "type": "text", + "x": 820.0, + "y": 7000.0, + "width": 560.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-15" + ], + "frameId": "frame-01453-1672", + "roundness": null, + "seed": 144813878, + "version": 1, + "versionNonce": 380319714, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Replace identity claims with No active session and a Sign in action.", + "originalText": "Replace identity claims with No active session and a Sign in action.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "frame-01518-1658", + "type": "frame", + "x": 1920.0, + "y": 6200.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1843727536, + "version": 1, + "versionNonce": 759803996, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "name": "17 Settings - Advanced and responsive" + }, + { + "id": "r-01520-7648", + "type": "rectangle", + "x": 1920.0, + "y": 6200.0, + "width": 1440.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1350669967, + "version": 1, + "versionNonce": 266805915, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01521-9270", + "type": "rectangle", + "x": 1920.0, + "y": 6200.0, + "width": 210.0, + "height": 920.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1823625113, + "version": 1, + "versionNonce": 2015076333, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01522-4974", + "type": "text", + "x": 1944.0, + "y": 6224.0, + "width": 160.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1716522767, + "version": 1, + "versionNonce": 1440354968, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AEVATAR", + "originalText": "AEVATAR", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01523-7816", + "type": "text", + "x": 1944.0, + "y": 6253.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1696312985, + "version": 1, + "versionNonce": 1863203555, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AUTOMATION", + "originalText": "AUTOMATION", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01524-4006", + "type": "ellipse", + "x": 1948.0, + "y": 6320.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 383968405, + "version": 1, + "versionNonce": 2054788935, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01525-8830", + "type": "text", + "x": 1968.0, + "y": 6316.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 459213693, + "version": 1, + "versionNonce": 1885640492, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workflows", + "originalText": "Workflows", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "e-01526-5152", + "type": "ellipse", + "x": 1948.0, + "y": 6372.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "#98a2b3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 565876042, + "version": 1, + "versionNonce": 1981344992, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01527-2700", + "type": "text", + "x": 1968.0, + "y": 6368.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1210681185, + "version": 1, + "versionNonce": 799725216, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Activity", + "originalText": "Activity", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01528-3555", + "type": "rectangle", + "x": 1932.0, + "y": 6412.0, + "width": 186.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#344054", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 1063361436, + "version": 1, + "versionNonce": 71939005, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "e-01529-3756", + "type": "ellipse", + "x": 1948.0, + "y": 6424.0, + "width": 10.0, + "height": 10.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1501286945, + "version": 1, + "versionNonce": 1028853732, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01530-2973", + "type": "text", + "x": 1968.0, + "y": 6420.0, + "width": 130.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 2116555255, + "version": 1, + "versionNonce": 1646361413, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01531-1346", + "type": "text", + "x": 1944.0, + "y": 7046.0, + "width": 150.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 38798154, + "version": 1, + "versionNonce": 1453992633, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Workspace", + "originalText": "Workspace", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01532-8354", + "type": "text", + "x": 1944.0, + "y": 7072.0, + "width": 160.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1134749211, + "version": 1, + "versionNonce": 904670745, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Acme Operations", + "originalText": "Acme Operations", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01533-1921", + "type": "rectangle", + "x": 2130.0, + "y": 6200.0, + "width": 1230.0, + "height": 82.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1638152854, + "version": 1, + "versionNonce": 1168336161, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01534-6748", + "type": "text", + "x": 2160.0, + "y": 6218.0, + "width": 620.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1893680968, + "version": 1, + "versionNonce": 1342045319, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01535-9963", + "type": "text", + "x": 2160.0, + "y": 6249.0, + "width": 700.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 910910478, + "version": 1, + "versionNonce": 2087184812, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced diagnostics stay separate from everyday defaults.", + "originalText": "Advanced diagnostics stay separate from everyday defaults.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01536-6888", + "type": "text", + "x": 2162.0, + "y": 6324.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 97109917, + "version": 1, + "versionNonce": 1412748522, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI", + "originalText": "AI", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01537-8732", + "type": "text", + "x": 2198.0, + "y": 6323.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1891843358, + "version": 1, + "versionNonce": 762639347, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01538-6759", + "type": "text", + "x": 2162.0, + "y": 6372.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1313256864, + "version": 1, + "versionNonce": 1160475485, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "ID", + "originalText": "ID", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01539-1478", + "type": "text", + "x": 2198.0, + "y": 6371.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 2064236470, + "version": 1, + "versionNonce": 1587712918, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01540-9764", + "type": "rectangle", + "x": 2150.0, + "y": 6408.0, + "width": 176.0, + "height": 40.0, + "angle": 0, + "strokeColor": "#eff8ff", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 1741366048, + "version": 1, + "versionNonce": 1717583814, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01541-1219", + "type": "text", + "x": 2162.0, + "y": 6420.0, + "width": 34.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1398392972, + "version": 1, + "versionNonce": 1127071780, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "", + "originalText": "", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01542-8026", + "type": "text", + "x": 2198.0, + "y": 6419.0, + "width": 118.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1018181984, + "version": 1, + "versionNonce": 1544846885, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01543-7376", + "type": "text", + "x": 2348.0, + "y": 6312.0, + "width": 280.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 946535221, + "version": 1, + "versionNonce": 707024409, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01544-6839", + "type": "text", + "x": 2348.0, + "y": 6348.0, + "width": 480.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 2107910375, + "version": 1, + "versionNonce": 1885975148, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Read-only runtime and routing details for troubleshooting.", + "originalText": "Read-only runtime and routing details for troubleshooting.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01545-7405", + "type": "line", + "x": 2348.0, + "y": 6386.0, + "width": 610.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1196059152, + "version": 1, + "versionNonce": 295144296, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 610.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01546-7331", + "type": "text", + "x": 2348.0, + "y": 6414.0, + "width": 260.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1921294946, + "version": 1, + "versionNonce": 1806287196, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Request values", + "originalText": "Request values", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01547-1312", + "type": "text", + "x": 2348.0, + "y": 6444.0, + "width": 460.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 777922115, + "version": 1, + "versionNonce": 766323898, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Effective values supplied to new requests.", + "originalText": "Effective values supplied to new requests.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01548-9906", + "type": "rectangle", + "x": 2348.0, + "y": 6486.0, + "width": 610.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1906280475, + "version": 1, + "versionNonce": 116070713, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01549-5902", + "type": "text", + "x": 2362.0, + "y": 6501.0, + "width": 270.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 745636606, + "version": 1, + "versionNonce": 1305923974, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x-aevatar-llm-route", + "originalText": "x-aevatar-llm-route", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01550-2030", + "type": "text", + "x": 2640.0, + "y": 6501.0, + "width": 304.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 955885848, + "version": 1, + "versionNonce": 888134969, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "nyxid_gateway", + "originalText": "nyxid_gateway", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01551-8316", + "type": "rectangle", + "x": 2348.0, + "y": 6534.0, + "width": 610.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1835155286, + "version": 1, + "versionNonce": 1871458236, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01552-7476", + "type": "text", + "x": 2362.0, + "y": 6549.0, + "width": 270.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1905761765, + "version": 1, + "versionNonce": 819486045, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "x-aevatar-llm-model", + "originalText": "x-aevatar-llm-model", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01553-8436", + "type": "text", + "x": 2640.0, + "y": 6549.0, + "width": 304.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 304076748, + "version": 1, + "versionNonce": 1897790974, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "gpt-4o-mini", + "originalText": "gpt-4o-mini", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01554-6364", + "type": "rectangle", + "x": 2348.0, + "y": 6582.0, + "width": 610.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 613684243, + "version": 1, + "versionNonce": 269946591, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01555-5481", + "type": "text", + "x": 2362.0, + "y": 6597.0, + "width": 270.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 24063485, + "version": 1, + "versionNonce": 565957481, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "studio.runtime_base_url", + "originalText": "studio.runtime_base_url", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01556-4814", + "type": "text", + "x": 2640.0, + "y": 6597.0, + "width": 304.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 895531112, + "version": 1, + "versionNonce": 1867121743, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "http://127.0.0.1:5080", + "originalText": "http://127.0.0.1:5080", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01557-9525", + "type": "rectangle", + "x": 2348.0, + "y": 6630.0, + "width": 610.0, + "height": 48.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1310909508, + "version": 1, + "versionNonce": 1476657748, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01558-4634", + "type": "text", + "x": 2362.0, + "y": 6645.0, + "width": 270.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 446097990, + "version": 1, + "versionNonce": 1221844558, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "aevatar.runtime_mode", + "originalText": "aevatar.runtime_mode", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01559-1277", + "type": "text", + "x": 2640.0, + "y": 6645.0, + "width": 304.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#98a2b3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1919714662, + "version": 1, + "versionNonce": 2098068699, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "local", + "originalText": "local", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01560-5106", + "type": "rectangle", + "x": 2988.0, + "y": 6310.0, + "width": 344.0, + "height": 774.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 513121372, + "version": 1, + "versionNonce": 592051364, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "r-01561-5394", + "type": "rectangle", + "x": 2988.0, + "y": 6310.0, + "width": 344.0, + "height": 58.0, + "angle": 0, + "strokeColor": "#101828", + "backgroundColor": "#101828", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 274844987, + "version": 1, + "versionNonce": 1874463630, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01562-3774", + "type": "text", + "x": 3002.0, + "y": 6328.0, + "width": 120.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1476828694, + "version": 1, + "versionNonce": 723617535, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01563-2340", + "type": "text", + "x": 3002.0, + "y": 6390.0, + "width": 180.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1429033534, + "version": 1, + "versionNonce": 998465552, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Settings", + "originalText": "Settings", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01564-3419", + "type": "text", + "x": 3002.0, + "y": 6426.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1246125999, + "version": 1, + "versionNonce": 394236100, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Personal defaults and access.", + "originalText": "Personal defaults and access.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01565-1944", + "type": "rectangle", + "x": 2998.0, + "y": 6476.0, + "width": 108.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#eff8ff", + "backgroundColor": "#eff8ff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 956673298, + "version": 1, + "versionNonce": 2071922083, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01566-5125", + "type": "text", + "x": 3004.0, + "y": 6488.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1146301641, + "version": 1, + "versionNonce": 756589554, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01567-6474", + "type": "text", + "x": 3112.0, + "y": 6488.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1201000195, + "version": 1, + "versionNonce": 208000512, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Account", + "originalText": "Account", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01568-6162", + "type": "text", + "x": 3220.0, + "y": 6488.0, + "width": 96.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1743977963, + "version": 1, + "versionNonce": 1664003700, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Advanced", + "originalText": "Advanced", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01569-2036", + "type": "line", + "x": 2998.0, + "y": 6524.0, + "width": 324.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1756494985, + "version": 1, + "versionNonce": 560266594, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 324.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01570-3045", + "type": "text", + "x": 3002.0, + "y": 6546.0, + "width": 220.0, + "height": 27.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1007623017, + "version": 1, + "versionNonce": 194347506, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "AI defaults", + "originalText": "AI defaults", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01571-3229", + "type": "text", + "x": 3002.0, + "y": 6584.0, + "width": 310.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 218801819, + "version": 1, + "versionNonce": 1410949115, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Service and model for new sessions without an override.", + "originalText": "Service and model for new sessions without an override.", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "l-01572-3519", + "type": "line", + "x": 3002.0, + "y": 6632.0, + "width": 316.0, + "height": 0.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 517778096, + "version": 1, + "versionNonce": 2082037072, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 316.0, + 0.0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "t-01573-6021", + "type": "text", + "x": 3002.0, + "y": 6658.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 935979578, + "version": 1, + "versionNonce": 365146095, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Preferred service", + "originalText": "Preferred service", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01574-4627", + "type": "text", + "x": 3002.0, + "y": 6686.0, + "width": 55.85999999999999, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1426330259, + "version": 1, + "versionNonce": 1606464868, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "SERVICE", + "originalText": "SERVICE", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01575-9329", + "type": "rectangle", + "x": 3002.0, + "y": 6708.0, + "width": 316.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 608742821, + "version": 1, + "versionNonce": 1999006717, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01576-3339", + "type": "text", + "x": 3014.0, + "y": 6719.0, + "width": 292.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1377267368, + "version": 1, + "versionNonce": 837200287, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "NyxID Gateway", + "originalText": "NyxID Gateway", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01577-6783", + "type": "text", + "x": 3002.0, + "y": 6782.0, + "width": 260.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 581428195, + "version": 1, + "versionNonce": 1516287839, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Default model", + "originalText": "Default model", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "t-01578-6884", + "type": "text", + "x": 3002.0, + "y": 6810.0, + "width": 39.9, + "height": 17.5, + "angle": 0, + "strokeColor": "#667085", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 969757150, + "version": 1, + "versionNonce": 1570372960, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "MODEL", + "originalText": "MODEL", + "fontSize": 14, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01579-6755", + "type": "rectangle", + "x": 3002.0, + "y": 6832.0, + "width": 316.0, + "height": 42.0, + "angle": 0, + "strokeColor": "#d0d5dd", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 1721867008, + "version": 1, + "versionNonce": 265302696, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01580-3643", + "type": "text", + "x": 3014.0, + "y": 6843.0, + "width": 292.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#17202a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 972630293, + "version": 1, + "versionNonce": 744269190, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "gpt-4o-mini", + "originalText": "gpt-4o-mini", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01581-7188", + "type": "rectangle", + "x": 3002.0, + "y": 6980.0, + "width": 316.0, + "height": 70.0, + "angle": 0, + "strokeColor": "#344054", + "backgroundColor": "#1d2939", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 485345586, + "version": 1, + "versionNonce": 106392578, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01582-2585", + "type": "text", + "x": 3014.0, + "y": 6992.0, + "width": 180.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 653522696, + "version": 1, + "versionNonce": 1024544547, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Unsaved changes", + "originalText": "Unsaved changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + }, + { + "id": "r-01583-1459", + "type": "rectangle", + "x": 3190.0, + "y": 6996.0, + "width": 116.0, + "height": 34.0, + "angle": 0, + "strokeColor": "#175cd3", + "backgroundColor": "#175cd3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": { + "type": 3 + }, + "seed": 139191895, + "version": 1, + "versionNonce": 1791253054, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false + }, + { + "id": "t-01584-5947", + "type": "text", + "x": 3198.0, + "y": 7005.0, + "width": 100.0, + "height": 17.5, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "group-16" + ], + "frameId": "frame-01518-1658", + "roundness": null, + "seed": 1962603503, + "version": 1, + "versionNonce": 2037366862, + "isDeleted": false, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "text": "Save changes", + "originalText": "Save changes", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "autoResize": false, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#eef2f6" + }, + "files": {} +} diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.gen.py b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.gen.py new file mode 100644 index 0000000000..b857bffa53 --- /dev/null +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.gen.py @@ -0,0 +1,1212 @@ +#!/usr/bin/env python3 +"""Generate the Workflow + Activity + Settings vNext Excalidraw board. + +The board implements the product decisions from the 2026-08-03 meeting: +- Workflow is the only user-authored resource in scope. +- Run is the only execution action. +- Every Run is retained in Activity, including draft revisions. +- Retry and Run again create linked records instead of rewriting history. +- Settings follows the current Aevatar Console LLM and Account behavior. +- The application navigation contains Workflows, Activity, and Settings. +""" + +from __future__ import annotations + +import json +import random +import re +from pathlib import Path + + +random.seed(20260803) + +OUT = Path(__file__).with_suffix("").with_suffix(".excalidraw") + +INK = "#17202a" +MUTED = "#667085" +FAINT = "#98a2b3" +LINE = "#d0d5dd" +SURFACE = "#ffffff" +SUBTLE = "#f8fafc" +SIDEBAR = "#101828" +BLUE = "#175cd3" +BLUE_BG = "#eff8ff" +GREEN = "#067647" +GREEN_BG = "#ecfdf3" +AMBER = "#b54708" +AMBER_BG = "#fffaeb" +RED = "#b42318" +RED_BG = "#fef3f2" +PURPLE = "#6941c6" +PURPLE_BG = "#f4f3ff" + +FONT_SANS = 1 +FONT_MONO = 3 +FS_SMALL = 14 +FS_BODY = 16 +FS_HEAD = 22 +FS_TITLE = 32 + +FRAME_W = 1440 +FRAME_H = 920 +GAP_X = 320 +GAP_Y = 260 +COLS = 3 +ORIGIN_X = 160 +ORIGIN_Y = 300 + +elements: list[dict] = [] +current_frame: str | None = None +current_group: str | None = None +sequence = 0 + + +def eid(prefix: str = "e") -> str: + global sequence + sequence += 1 + return f"{prefix}-{sequence:05d}-{random.randint(1000, 9999)}" + + +def base(kind: str, x: float, y: float, w: float, h: float, **kwargs) -> dict: + item = { + "id": kwargs.pop("id", eid(kind[:1])), + "type": kind, + "x": float(x), + "y": float(y), + "width": float(w), + "height": float(h), + "angle": 0, + "strokeColor": kwargs.pop("stroke", INK), + "backgroundColor": kwargs.pop("bg", "transparent"), + "fillStyle": "solid", + "strokeWidth": kwargs.pop("sw", 1), + "strokeStyle": kwargs.pop("style", "solid"), + "roughness": 0, + "opacity": 100, + "groupIds": [current_group] if current_group else [], + "frameId": kwargs.pop("frameId", current_frame), + "roundness": kwargs.pop("roundness", None), + "seed": random.randint(1, 2**31 - 1), + "version": 1, + "versionNonce": random.randint(1, 2**31 - 1), + "isDeleted": False, + "boundElements": None, + "updated": 1, + "link": None, + "locked": False, + } + item.update(kwargs) + return item + + +def rect(x: float, y: float, w: float, h: float, *, bg: str = "transparent", + stroke: str = LINE, radius: bool = True, style: str = "solid", sw: int = 1) -> dict: + item = base("rectangle", x, y, w, h, bg=bg, stroke=stroke, style=style, sw=sw) + if radius: + item["roundness"] = {"type": 3} + elements.append(item) + return item + + +def text(x: float, y: float, value: str, size: int = FS_BODY, color: str = INK, + *, font: int = FONT_SANS, width: float | None = None, + align: str = "left", line_height: float = 1.25) -> dict: + lines = value.split("\n") + if width is None: + width = max(18, max(len(line) for line in lines) * size * 0.57) + height = len(lines) * size * line_height + item = base("text", x, y, width, height, stroke=color) + item.update({ + "text": value, + "originalText": value, + "fontSize": size, + "fontFamily": font, + "textAlign": align, + "verticalAlign": "top", + "containerId": None, + "autoResize": False, + "lineHeight": line_height, + }) + elements.append(item) + return item + + +def line(x: float, y: float, w: float, h: float, *, color: str = LINE, + style: str = "solid", arrowhead: str | None = None, sw: int = 1) -> dict: + kind = "arrow" if arrowhead else "line" + item = base(kind, x, y, abs(w), abs(h), stroke=color, style=style, sw=sw) + item.update({ + "points": [[0, 0], [float(w), float(h)]], + "lastCommittedPoint": None, + "startBinding": None, + "endBinding": None, + "startArrowhead": None, + "endArrowhead": arrowhead, + }) + if arrowhead: + item["elbowed"] = False + elements.append(item) + return item + + +def dot(x: float, y: float, diameter: float, color: str, *, bg: str | None = None) -> dict: + item = base("ellipse", x, y, diameter, diameter, stroke=color, bg=bg or color) + elements.append(item) + return item + + +def button(x: float, y: float, w: float, label: str, *, primary: bool = False, + color: str = INK, bg: str = SURFACE, h: float = 38) -> None: + stroke = color + fill = color if primary else bg + rect(x, y, w, h, bg=fill, stroke=stroke) + text(x + 8, y + 9, label, FS_SMALL, SURFACE if primary else color, + width=w - 16, align="center") + + +def chip(x: float, y: float, label: str, *, color: str = MUTED, + bg: str = SURFACE, selected: bool = False) -> float: + w = max(62, len(label) * 8.2 + 24) + rect(x, y, w, 30, bg=(BLUE_BG if selected else bg), + stroke=(BLUE if selected else LINE)) + text(x + 10, y + 7, label, FS_SMALL, BLUE if selected else color, width=w - 20) + return x + w + 8 + + +def badge(x: float, y: float, label: str, status: str) -> float: + palette = { + "ok": (GREEN, GREEN_BG), + "run": (BLUE, BLUE_BG), + "wait": (AMBER, AMBER_BG), + "fail": (RED, RED_BG), + "info": (PURPLE, PURPLE_BG), + "muted": (MUTED, SUBTLE), + } + color, bg = palette[status] + w = max(68, len(label) * 7.8 + 24) + rect(x, y, w, 28, bg=bg, stroke=color) + text(x + 10, y + 6, label, FS_SMALL, color, width=w - 20) + return x + w + 8 + + +def field(x: float, y: float, w: float, label: str, value: str, + *, mono: bool = False, h: float = 42) -> None: + text(x, y, label.upper(), FS_SMALL, MUTED, font=FONT_MONO) + rect(x, y + 22, w, h, bg=SURFACE, stroke=LINE) + text(x + 12, y + 33, value, FS_SMALL, INK, + font=FONT_MONO if mono else FONT_SANS, width=w - 24) + + +def frame_position(index: int) -> tuple[int, int]: + return ( + ORIGIN_X + (index % COLS) * (FRAME_W + GAP_X), + ORIGIN_Y + (index // COLS) * (FRAME_H + GAP_Y), + ) + + +def begin_frame(index: int, name: str) -> tuple[int, int]: + global current_frame, current_group + x, y = frame_position(index) + fid = eid("frame") + frame_item = base("frame", x, y, FRAME_W, FRAME_H, id=fid, + stroke="#344054", bg="transparent", frameId=None) + frame_item["name"] = name + elements.append(frame_item) + current_frame = fid + current_group = f"group-{index:02d}" + rect(x, y, FRAME_W, FRAME_H, bg=SURFACE, stroke="#344054", radius=False, sw=2) + return x, y + + +def end_frame() -> None: + global current_frame, current_group + current_frame = None + current_group = None + + +def app_shell(fx: float, fy: float, active: str, *, title: str, + subtitle: str = "") -> tuple[float, float, float]: + sidebar_w = 210 + top_h = 82 + rect(fx, fy, sidebar_w, FRAME_H, bg=SIDEBAR, stroke=SIDEBAR, radius=False) + text(fx + 24, fy + 24, "AEVATAR", FS_HEAD, SURFACE, width=160) + text(fx + 24, fy + 53, "AUTOMATION", FS_SMALL, "#98a2b3", font=FONT_MONO, width=160) + nav_y = fy + 116 + for idx, item in enumerate(("Workflows", "Activity", "Settings")): + row_y = nav_y + idx * 52 + if item == active: + rect(fx + 12, row_y - 8, sidebar_w - 24, 42, bg="#344054", stroke="#344054") + dot(fx + 28, row_y + 4, 10, BLUE if item == active else FAINT) + text(fx + 48, row_y, item, FS_BODY, SURFACE if item == active else "#d0d5dd", width=130) + text(fx + 24, fy + FRAME_H - 74, "Workspace", FS_SMALL, "#98a2b3", width=150) + text(fx + 24, fy + FRAME_H - 48, "Acme Operations", FS_SMALL, SURFACE, width=160) + + content_x = fx + sidebar_w + content_w = FRAME_W - sidebar_w + rect(content_x, fy, content_w, top_h, bg=SURFACE, stroke=LINE, radius=False) + text(content_x + 30, fy + 18, title, FS_HEAD, INK, width=620) + if subtitle: + text(content_x + 30, fy + 49, subtitle, FS_SMALL, MUTED, width=700) + return content_x, fy + top_h, content_w + + +def table_header(x: float, y: float, widths: list[float], labels: list[str]) -> None: + rect(x, y, sum(widths), 42, bg=SUBTLE, stroke=LINE, radius=False) + cursor = x + for width, label in zip(widths, labels): + text(cursor + 14, y + 13, label.upper(), FS_SMALL, MUTED, + font=FONT_MONO, width=width - 28) + cursor += width + + +def workflow_row(x: float, y: float, name: str, description: str, revision: str, + last_run: str, run_status: str, state: str) -> None: + widths = [410, 170, 220, 150, 190] + rect(x, y, sum(widths), 82, bg=SURFACE, stroke=LINE, radius=False) + text(x + 16, y + 15, name, FS_BODY, INK, width=360) + text(x + 16, y + 43, description, FS_SMALL, MUTED, width=375) + cursor = x + widths[0] + text(cursor + 14, y + 28, revision, FS_SMALL, MUTED, font=FONT_MONO, width=140) + cursor += widths[1] + status_label = {"ok": "Succeeded", "run": "Running", "fail": "Failed", "muted": "Never run"}[run_status] + badge(cursor + 14, y + 26, status_label, run_status) + text(cursor + 115, y + 31, last_run, FS_SMALL, MUTED, width=95) + cursor += widths[2] + badge(cursor + 14, y + 26, state, "ok" if state == "Published" else "muted") + cursor += widths[3] + button(cursor + 12, y + 22, 72, "Run") + button(cursor + 94, y + 22, 76, "Open", color=BLUE, bg=BLUE_BG) + + +def activity_row(x: float, y: float, status: str, name: str, origin: str, + revision: str, when: str, took: str, usage: str, outcome: str) -> None: + widths = [105, 235, 120, 175, 115, 75, 90, 255] + rect(x, y, sum(widths), 70, bg=SURFACE, stroke=LINE, radius=False) + palette_key = {"Succeeded": "ok", "Running": "run", "Needs you": "wait", "Failed": "fail"}[status] + cursor = x + badge(cursor + 12, y + 21, status, palette_key) + cursor += widths[0] + text(cursor + 12, y + 14, name, FS_SMALL, INK, width=210) + text(cursor + 12, y + 39, origin, FS_SMALL, MUTED, width=210) + cursor += widths[1] + text(cursor + 12, y + 25, origin.split(" · ")[0], FS_SMALL, MUTED, width=96) + cursor += widths[2] + text(cursor + 12, y + 15, revision.split(" · ")[0], FS_SMALL, INK, width=150) + text(cursor + 12, y + 40, " · ".join(revision.split(" · ")[1:]), FS_SMALL, MUTED, font=FONT_MONO, width=150) + cursor += widths[3] + text(cursor + 12, y + 25, when, FS_SMALL, MUTED, width=92) + cursor += widths[4] + text(cursor + 8, y + 25, took, FS_SMALL, MUTED, font=FONT_MONO, width=60) + cursor += widths[5] + text(cursor + 8, y + 25, usage, FS_SMALL, MUTED, font=FONT_MONO, width=72) + cursor += widths[6] + text(cursor + 12, y + 15, outcome, FS_SMALL, INK, width=228) + text(cursor + 12, y + 40, "Open details", FS_SMALL, BLUE, width=228) + + +def node_appearance(kind: str) -> tuple[str, str]: + normalized = kind.casefold() + if "ai" in normalized: + return BLUE, "AI" + if "schedule" in normalized: + return PURPLE, "T" + if "condition" in normalized: + return "#9333ea", "C" + if "approval" in normalized: + return AMBER, "H" + if "validation" in normalized: + return RED, "V" + if any(token in normalized for token in ("data", "query", "xero", "warehouse")): + return "#0891b2", "D" + if any(token in normalized for token in ("message", "lark", "gmail")): + return "#16a34a", "I" + return MUTED, "{}" + + +def studio_node(x: float, y: float, name: str, kind: str, summary: str, + *, selected: bool = False, status: str | None = None) -> None: + color, icon = node_appearance(kind) + node_w = 214 + node_h = 126 + rect(x, y, node_w, node_h, bg=SURFACE, + stroke=color if selected else "#dbe3ee", sw=2 if selected else 1) + rect(x, y, 4, node_h, bg=color, stroke=color, radius=False) + dot(x - 6, y + 54, 12, color, bg=color) + dot(x + node_w - 6, y + 54, 12, color, bg=color) + rect(x + 14, y + 14, 34, 34, bg=BLUE_BG if color == BLUE else SUBTLE, + stroke=color) + text(x + 14, y + 23, icon, FS_SMALL, color, width=34, align="center") + text(x + 60, y + 13, name, FS_SMALL, INK, width=138) + text(x + 60, y + 39, kind, 12, MUTED, width=138) + line(x + 12, y + 62, node_w - 24, 0, color="#edf2f7") + text(x + 14, y + 76, summary, 12, MUTED, width=node_w - 28) + if status: + status_palette = "ok" if status == "Completed" else "run" if status == "Running" else "muted" + badge(x + 124, y + 88, status, status_palette) + + +def studio_canvas(cx: float, cy: float, cw: float, ch: float, + steps: list[tuple[str, str]], *, selected: int | None = None, + fields: list[tuple[str, str]] | None = None, + empty: bool = False, source: str = "", + statuses: list[str] | None = None) -> None: + canvas_x = cx + 10 + canvas_y = cy + 10 + canvas_w = cw - 20 + canvas_h = ch - 20 + rect(canvas_x, canvas_y, canvas_w, canvas_h, bg="#f7f9fc", stroke="#d8e0ea") + for grid_x in range(80, int(canvas_w), 120): + line(canvas_x + grid_x, canvas_y, 0, canvas_h, color="#edf2f7") + for grid_y in range(80, int(canvas_h), 120): + line(canvas_x, canvas_y + grid_y, canvas_w, 0, color="#edf2f7") + badge(canvas_x + 16, canvas_y + 16, f"Created from {source}", "info") + + if empty: + center_x = canvas_x + canvas_w / 2 + center_y = canvas_y + canvas_h / 2 + rect(center_x - 56, center_y - 86, 112, 112, + bg=SURFACE, stroke=FAINT, style="dashed") + text(center_x - 56, center_y - 58, "+", FS_TITLE, MUTED, + width=112, align="center") + text(center_x - 150, center_y + 44, "Add first step", FS_HEAD, INK, + width=300, align="center") + text(center_x - 220, center_y + 82, + "Start this workflow by adding the first step.", + FS_SMALL, MUTED, width=440, align="center") + button(canvas_x + 16, canvas_y + canvas_h - 128, 34, "+", color=MUTED, h=32) + button(canvas_x + 16, canvas_y + canvas_h - 92, 34, "-", color=MUTED, h=32) + button(canvas_x + 16, canvas_y + canvas_h - 56, 34, "Fit", color=MUTED, h=32) + return + + inspector_open = selected is not None and 0 <= selected < len(steps) + graph_w = canvas_w - (400 if inspector_open else 0) + node_w = 214 + positions: list[tuple[float, float]] = [] + if len(steps) <= 5 and not inspector_open: + gap = min(48, (graph_w - 56 - len(steps) * node_w) / max(1, len(steps) - 1)) + total_w = len(steps) * node_w + max(0, len(steps) - 1) * gap + start_x = canvas_x + (graph_w - total_w) / 2 + positions = [(start_x + idx * (node_w + gap), canvas_y + 260) + for idx in range(len(steps))] + else: + positions = [] + for idx in range(len(steps)): + if idx < 3: + positions.append((canvas_x + 44 + idx * 248, canvas_y + 112)) + else: + positions.append((canvas_x + 540, canvas_y + 112 + (idx - 2) * 236)) + + for idx in range(len(positions) - 1): + sx, sy = positions[idx] + tx, ty = positions[idx + 1] + line(sx + node_w, sy + 60, tx - (sx + node_w), ty - sy, + color="#94a3b8", arrowhead="arrow", sw=2) + + for idx, ((name, kind), (nx, ny)) in enumerate(zip(steps, positions)): + summary = fields[0][1] if idx == selected and fields else "Configuration ready" + studio_node(nx, ny, name, kind, summary, selected=idx == selected, + status=statuses[idx] if statuses and idx < len(statuses) else None) + + button(canvas_x + 16, canvas_y + canvas_h - 128, 34, "+", color=MUTED, h=32) + button(canvas_x + 16, canvas_y + canvas_h - 92, 34, "-", color=MUTED, h=32) + button(canvas_x + 16, canvas_y + canvas_h - 56, 34, "Fit", color=MUTED, h=32) + minimap_x = canvas_x + graph_w - 154 + minimap_y = canvas_y + canvas_h - 104 + rect(minimap_x, minimap_y, 138, 86, bg=SURFACE, stroke="#d8e0ea") + for idx, _ in enumerate(steps[:6]): + rect(minimap_x + 10 + (idx % 3) * 40, minimap_y + 16 + (idx // 3) * 28, + 28, 14, bg=SURFACE, stroke=BLUE) + + if not inspector_open: + return + selected_name, selected_kind = steps[selected] + panel_x = canvas_x + canvas_w - 390 + panel_w = 390 + rect(panel_x, canvas_y, panel_w, canvas_h, bg=SURFACE, stroke="#e5e7eb") + text(panel_x + 20, canvas_y + 18, "Node configuration", FS_BODY, INK, width=260) + text(panel_x + 20, canvas_y + 48, selected_name, FS_SMALL, MUTED, width=300) + text(panel_x + 344, canvas_y + 17, "x", FS_HEAD, MUTED, width=24, align="center") + line(panel_x, canvas_y + 76, panel_w, 0, color="#eef2f7") + field(panel_x + 20, canvas_y + 100, panel_w - 40, "Node name", selected_name) + field(panel_x + 20, canvas_y + 188, panel_w - 40, "Type", selected_kind) + for field_index, (label, value) in enumerate(fields or []): + field(panel_x + 20, canvas_y + 276 + field_index * 88, + panel_w - 40, label, value, h=58 if len(value) > 64 else 42) + line(panel_x, canvas_y + canvas_h - 62, panel_w, 0, color="#eef2f7") + button(panel_x + 194, canvas_y + canvas_h - 48, 78, "Delete", color=RED, h=34) + button(panel_x + 282, canvas_y + canvas_h - 48, 88, "Apply", + primary=True, color=BLUE, h=34) + + +def workflow_editor_frame(index: int, frame_name: str, *, title: str, + subtitle: str, source: str, + steps: list[tuple[str, str]], selected: int | None = None, + fields: list[tuple[str, str]] | None = None, + empty: bool = False) -> None: + fx, fy = begin_frame(index, frame_name) + cx, cy, cw = app_shell(fx, fy, "Workflows", title=title, subtitle=subtitle) + button(cx + cw - 510, fy + 24, 72, "Run", color=MUTED if empty else INK, h=32) + button(cx + cw - 428, fy + 24, 92, "Add node", color=INK, h=32) + button(cx + cw - 326, fy + 24, 92, "Edit YAML", color=INK, h=32) + button(cx + cw - 224, fy + 24, 82, "Save", primary=True, color=BLUE, h=32) + button(cx + cw - 132, fy + 24, 102, "Publish", color=MUTED if empty else INK, h=32) + studio_canvas(cx, cy, cw, FRAME_H - (cy - fy), steps, + selected=selected, fields=fields, empty=empty, source=source) + end_frame() + + +def settings_nav(x: float, y: float, active: str, *, mobile: bool = False) -> None: + labels = ("AI defaults", "Account", "Advanced") + if mobile: + tab_w = 108 + for idx, label in enumerate(labels): + tab_x = x + idx * tab_w + selected = label == active + if selected: + rect(tab_x, y, tab_w, 42, bg=BLUE_BG, stroke=BLUE_BG) + text(tab_x + 6, y + 12, label, FS_SMALL, + BLUE if selected else MUTED, width=tab_w - 12, align="center") + line(x, y + 48, tab_w * len(labels), 0, color=LINE) + return + + for idx, label in enumerate(labels): + row_y = y + idx * 48 + selected = label == active + if selected: + rect(x, row_y, 176, 40, bg=BLUE_BG, stroke=BLUE_BG) + icon = ("AI", "ID", "")[idx] + text(x + 12, row_y + 12, icon, FS_SMALL, + BLUE if selected else MUTED, font=FONT_MONO, width=34) + text(x + 48, row_y + 11, label, FS_SMALL, + BLUE if selected else MUTED, width=118) + + +def settings_panel_heading(x: float, y: float, w: float, title_value: str, + description: str = "") -> None: + text(x + 18, y + 16, title_value, FS_BODY, INK, width=w - 36) + if description: + text(x + 18, y + 42, description, FS_SMALL, MUTED, width=w - 36) + line(x, y + 72, w, 0, color=LINE) + + +# Board title and semantic contract +text(ORIGIN_X, 80, "Aevatar Workflow + Activity + Settings vNext", FS_TITLE, INK, width=1040) +text(ORIGIN_X, 132, + "One authoring resource. One Run action. One immutable Activity history.", + FS_HEAD, BLUE, width=980) +text(ORIGIN_X, 180, + "Primary path: Workflows -> direct draft -> Run -> Activity. Settings owns LLM defaults and browser identity.", + FS_BODY, MUTED, width=1180) + + +# 01 Workflows catalogue +fx, fy = begin_frame(0, "01 Workflows - catalogue") +cx, cy, cw = app_shell(fx, fy, "Workflows", title="Workflows", + subtitle="Create, edit, publish, and run automations.") +button(cx + cw - 174, fy + 22, 144, "New workflow", primary=True, color=BLUE) +field(cx + 30, cy + 26, 370, "Search", "Search workflows") +px = cx + 426 +for label, selected in (("All 5", True), ("Published 3", False), ("Drafts 2", False), ("Failing 1", False)): + px = chip(px, cy + 48, label, selected=selected) +table_x = cx + 30 +table_y = cy + 112 +table_header(table_x, table_y, [410, 170, 220, 150, 190], + ["Workflow", "Version", "Last run", "State", "Actions"]) +rows = [ + ("Weekly feedback digest", "Group feedback and post a weekly summary", "v7", "12 min ago", "ok", "Published"), + ("Customer escalation triage", "Classify urgent conversations and prepare replies", "draft r12", "4 min ago", "run", "Draft"), + ("Invoice follow-up", "Prepare overdue invoice reminders for approval", "v4", "42 min ago", "ok", "Published"), + ("Nightly order sync", "Move completed orders into the warehouse", "v9", "3:04 AM", "fail", "Published"), + ("Contract review", "Flag unusual clauses and prepare a review note", "draft r3", "-", "muted", "Draft"), +] +for idx, row in enumerate(rows): + workflow_row(table_x, table_y + 42 + idx * 82, *row) +text(table_x, fy + FRAME_H - 54, + "5 workflows | Every Run, including a draft revision, is retained in Activity.", + FS_SMALL, MUTED, width=760) +end_frame() + + +# 02 Direct Workflow creation +fx, fy = begin_frame(1, "02 New workflow - direct creation") +cx, cy, cw = app_shell(fx, fy, "Workflows", title="New workflow", + subtitle="Choose a starting point. Each option creates a workflow draft directly.") +button(cx + cw - 118, fy + 22, 88, "Cancel") +card_y = cy + 42 +cards = [ + ("Describe what you need", "Turn a plain-language goal into a reviewable first draft.", BLUE, BLUE_BG, "Describe"), + ("Start blank", "Open an empty workflow canvas and add the first node.", INK, SURFACE, "Start blank"), + ("Import YAML", "Create a draft from an existing workflow definition.", PURPLE, PURPLE_BG, "Import"), +] +for idx, (title, body, color, bg, action) in enumerate(cards): + x = cx + 30 + idx * 390 + rect(x, card_y, 362, 180, bg=bg, stroke=color if idx != 1 else LINE) + text(x + 22, card_y + 24, title, FS_HEAD, color, width=318) + text(x + 22, card_y + 64, body, FS_BODY, MUTED, width=310) + button(x + 22, card_y + 124, 130, action, primary=(idx == 0), color=color) +text(cx + 30, card_y + 226, "TEMPLATES", FS_SMALL, MUTED, font=FONT_MONO, width=180) +templates = [ + ("Weekly feedback digest", "6 steps", "Lark", "Used 240 times"), + ("Escalation triage", "9 steps", "Lark + Linear", "Used 180 times"), + ("Invoice follow-up", "11 steps", "Xero + Gmail", "Used 96 times"), + ("Standup digest", "5 steps", "Lark", "Used 88 times"), + ("On-call summary", "5 steps", "PagerDuty", "Used 61 times"), + ("Contract review", "7 steps", "Drive", "Used 44 times"), +] +for idx, item in enumerate(templates): + col = idx % 3 + row = idx // 3 + x = cx + 30 + col * 390 + y = card_y + 264 + row * 172 + rect(x, y, 362, 148, bg=SURFACE, stroke=LINE) + text(x + 18, y + 18, item[0], FS_BODY, INK, width=320) + text(x + 18, y + 48, f"{item[1]} | {item[2]}", FS_SMALL, MUTED, width=320) + text(x + 18, y + 78, item[3], FS_SMALL, MUTED, width=160) + button(x + 218, y + 92, 126, "Use template", color=BLUE, bg=BLUE_BG) +end_frame() + + +# 03-06 The four creation paths produce different Workflow documents. +workflow_editor_frame( + 2, "03 Describe - generated Workflow draft", + title="Monday feedback summary", + subtitle="Current draft · revision 1 · Saved just now", + source="Description", + steps=[ + ("Schedule every Monday", "Schedule"), + ("Collect recent feedback", "Lark messages"), + ("Group feedback themes", "AI task"), + ("Draft concise summary", "AI task"), + ("Post feedback summary", "Lark message"), + ], + selected=0, + fields=[("Frequency", "Every Monday at 09:00"), + ("Timezone", "Asia/Shanghai")], +) + +workflow_editor_frame( + 3, "04 Start blank - empty Workflow draft", + title="Untitled workflow", + subtitle="Current draft · revision 1 · Saved just now", + source="Blank", + steps=[], + empty=True, +) + +workflow_editor_frame( + 4, "05 Import YAML - imported Workflow draft", + title="Daily order exception report", + subtitle="Current draft · revision 1 · Imported just now", + source="Import", + steps=[ + ("Schedule every weekday", "Schedule"), + ("Find order exceptions", "Data query"), + ("Summarize exceptions", "AI task"), + ("Send operations summary", "Lark message"), + ], + selected=1, + fields=[("Source", "Orders"), + ("Filter", "status = exception")], +) + +workflow_editor_frame( + 5, "06 Template - populated Workflow draft", + title="Standup digest copy", + subtitle="Current draft · revision 1 · Saved just now", + source="Template", + steps=[ + ("Schedule every weekday", "Schedule"), + ("Collect contributor updates", "Lark messages"), + ("Draft standup digest", "AI task"), + ("Post standup digest", "Lark message"), + ], + selected=2, + fields=[("Instruction", "Group updates into completed, planned, and blocked."), + ("Tone", "Concise")], +) + + +# 07 Unified Run dialog +fx, fy = begin_frame(6, "07 Run - unified execution dialog") +cx, cy, cw = app_shell(fx, fy, "Workflows", title="Customer escalation triage", + subtitle="Draft revision 12 | Saved just now") +button(cx + cw - 510, fy + 24, 72, "Run", color=INK, h=32) +button(cx + cw - 428, fy + 24, 92, "Add node", color=INK, h=32) +button(cx + cw - 326, fy + 24, 92, "Edit YAML", color=INK, h=32) +button(cx + cw - 224, fy + 24, 82, "Save", primary=True, color=BLUE, h=32) +button(cx + cw - 132, fy + 24, 102, "Publish", color=INK, h=32) +studio_canvas(cx, cy, cw, FRAME_H - (cy - fy), [ + ("Receive conversation", "Lark message"), + ("Classify severity", "AI task"), + ("Route escalation", "Condition"), + ("Prepare reply", "AI task"), + ("Post response", "Lark message"), +], source="Description") +modal_x = cx + 260 +modal_y = cy + 38 +modal_w = 710 +rect(modal_x, modal_y, modal_w, 720, bg=SURFACE, stroke="#344054", sw=2) +text(modal_x + 28, modal_y + 26, "Run workflow", FS_HEAD, INK, width=450) +text(modal_x + 28, modal_y + 62, + "Confirm the revision, inputs, and external effects.", FS_SMALL, MUTED, width=520) +badge(modal_x + 28, modal_y + 102, "Current draft", "info") +text(modal_x + 154, modal_y + 108, "revision 12", FS_SMALL, PURPLE, + font=FONT_MONO, width=130) +line(modal_x + 28, modal_y + 144, modal_w - 56, 0, color=LINE) +field(modal_x + 28, modal_y + 168, modal_w - 56, "Workflow", "Customer escalation triage") +field(modal_x + 28, modal_y + 252, modal_w - 56, "Conversation", "support-thread-4821", mono=True) +field(modal_x + 28, modal_y + 336, (modal_w - 70) / 2, "Environment", "Production") +field(modal_x + 42 + (modal_w - 70) / 2, modal_y + 336, + (modal_w - 70) / 2, "Connections", "2 ready") +rect(modal_x + 28, modal_y + 430, modal_w - 56, 86, bg=AMBER_BG, stroke=AMBER) +text(modal_x + 46, modal_y + 448, "External effects", FS_BODY, AMBER, width=180) +text(modal_x + 46, modal_y + 477, + "May post a Lark reply and create a Linear issue.", FS_SMALL, INK, width=560) +rect(modal_x + 28, modal_y + 536, 18, 18, bg=BLUE, stroke=BLUE) +text(modal_x + 32, modal_y + 537, "x", FS_SMALL, SURFACE, width=12) +text(modal_x + 58, modal_y + 536, + "I understand this workflow may change external systems.", FS_SMALL, INK, width=590) +rect(modal_x + 28, modal_y + 574, modal_w - 56, 50, bg=BLUE_BG, stroke=BLUE) +text(modal_x + 46, modal_y + 590, + "This Run will be saved in Activity.", FS_SMALL, BLUE, width=500) +button(modal_x + modal_w - 232, modal_y + 652, 88, "Cancel") +button(modal_x + modal_w - 132, modal_y + 652, 104, "Run", primary=True, color=BLUE) +end_frame() + + +# 08 Running in the same Workflow Studio canvas +fx, fy = begin_frame(7, "08 Running draft - Studio canvas and Run console") +cx, cy, cw = app_shell(fx, fy, "Workflows", title="Customer escalation triage", + subtitle="Current draft · revision 12 · Run R-1042") +button(cx + cw - 510, fy + 24, 72, "Run", color=INK, h=32) +button(cx + cw - 428, fy + 24, 92, "Add node", color=INK, h=32) +button(cx + cw - 326, fy + 24, 92, "Edit YAML", color=INK, h=32) +button(cx + cw - 224, fy + 24, 82, "Save", primary=True, color=BLUE, h=32) +button(cx + cw - 132, fy + 24, 102, "Publish", color=INK, h=32) +running_steps = [ + ("Receive conversation", "Lark message"), + ("Classify severity", "AI task"), + ("Route escalation", "Condition"), + ("Prepare reply", "AI task"), + ("Post response", "Lark message"), +] +studio_canvas(cx, cy, cw, 570, running_steps, source="Description", + statuses=["Completed", "Completed", "Completed", "Running", "Queued"]) +console_y = cy + 570 +rect(cx, console_y, cw, FRAME_H - (console_y - fy), bg=SURFACE, stroke="#d8e0ea", radius=False) +rect(cx, console_y - 6, cw, 6, bg="#cbd5e1", stroke="#cbd5e1", radius=False) +text(cx + 22, console_y + 18, "Run console", FS_BODY, INK, width=180) +badge(cx + 142, console_y + 14, "Running", "run") +text(cx + 248, console_y + 21, + "R-1042 · Current draft · revision 12 · Activity record created", + FS_SMALL, BLUE, font=FONT_MONO, width=620) +button(cx + cw - 132, console_y + 12, 102, "View Run", color=BLUE, bg=BLUE_BG, h=34) +line(cx, console_y + 58, cw, 0, color="#eef2f7") +log_rows = [ + ("14:32:18.102", "Receive conversation", "completed", GREEN), + ("14:32:22.364", "Classify severity", "completed", GREEN), + ("14:32:22.481", "Route escalation", "completed", GREEN), + ("14:32:23.019", "Prepare reply", "running", BLUE), +] +for idx, (stamp, name, state, color) in enumerate(log_rows): + row_y = console_y + 78 + idx * 37 + text(cx + 24, row_y, stamp, 12, MUTED, font=FONT_MONO, width=120) + dot(cx + 152, row_y + 2, 10, color, bg=color) + text(cx + 176, row_y, name, FS_SMALL, INK, width=260) + text(cx + 450, row_y, state, FS_SMALL, color, font=FONT_MONO, width=130) +rect(cx + 690, console_y + 74, cw - 720, 134, bg=SIDEBAR, stroke=SIDEBAR) +text(cx + 708, console_y + 90, "OUTPUT", 12, FAINT, font=FONT_MONO, width=100) +text(cx + 708, console_y + 120, + "Preparing a concise response for\nthe support conversation...", + FS_SMALL, "#d0d5dd", font=FONT_MONO, width=cw - 760) +end_frame() + + +# 09 Activity with a Workflow filter +fx, fy = begin_frame(8, "09 Activity - filtered by Workflow") +cx, cy, cw = app_shell(fx, fy, "Activity", title="Activity", + subtitle="Every Run, newest first. Filtered to Weekly feedback digest.") +field(cx + 30, cy + 24, 330, "Search", "Search Run or outcome") +px = cx + 386 +px = chip(px, cy + 46, "Workflow: Weekly feedback digest", selected=True) +for label, selected in (("All 24", True), ("Needs you 1", False), ("Running 1", False), ("Failed 2", False)): + px = chip(px, cy + 46, label, selected=selected) +table_x = cx + 30 +table_y = cy + 112 +table_header(table_x, table_y, [105, 235, 120, 175, 115, 75, 90, 255], + ["Status", "Workflow", "Origin", "Revision", "When", "Took", "Usage", "Outcome"]) +runs = [ + ("Running", "Weekly feedback digest", "Manual · you", "Current draft · revision 12", "18s ago", "00:18", "$0.03", "Preparing summary"), + ("Succeeded", "Weekly feedback digest", "Schedule · weekly", "Published · v7", "2h ago", "01:42", "$0.18", "Posted 1 summary"), + ("Needs you", "Weekly feedback digest", "Schedule · weekly", "Published · v7", "1w ago", "00:56", "$0.11", "Approval requested"), + ("Failed", "Weekly feedback digest", "Manual · Calvin", "Draft · revision 9", "8d ago", "00:12", "$0.02", "Lark connection expired"), + ("Succeeded", "Weekly feedback digest", "Run again · R-1001", "Published · v6", "15d ago", "01:37", "$0.17", "Posted 1 summary"), +] +for idx, run in enumerate(runs): + activity_row(table_x, table_y + 42 + idx * 70, *run) +text(table_x, fy + FRAME_H - 52, + "Activity remains the authoritative Run history. Run again and Retry append linked records.", + FS_SMALL, BLUE, width=720) +end_frame() + + +# 10 Global Activity +fx, fy = begin_frame(9, "10 Activity - all retained Runs") +cx, cy, cw = app_shell(fx, fy, "Activity", title="Activity", + subtitle="Every Run, newest first. Search, inspect, and recover from one place.") +field(cx + 30, cy + 28, 380, "Search", "Search workflow, Run, or outcome") +px = cx + 438 +for label, selected in (("All 128", True), ("Needs you 2", False), ("Running 3", False), ("Failed 7", False), ("Mine 18", False)): + px = chip(px, cy + 50, label, selected=selected) +table_x = cx + 30 +table_y = cy + 116 +table_header(table_x, table_y, [105, 235, 120, 175, 115, 75, 90, 255], + ["Status", "Workflow", "Origin", "Revision", "When", "Took", "Usage", "Outcome"]) +runs = [ + ("Running", "Customer escalation triage", "Manual · you", "Current draft · revision 12", "18s ago", "00:18", "$0.03", "Preparing reply"), + ("Needs you", "Invoice follow-up", "Schedule · weekday", "Published · v4", "42m ago", "02:18", "$0.22", "Approve 4 messages"), + ("Succeeded", "Weekly feedback digest", "Schedule · weekly", "Published · v7", "2h ago", "01:42", "$0.18", "Posted 1 summary"), + ("Failed", "Nightly order sync", "Schedule · nightly", "Published · v9", "3:04 AM", "00:21", "$0.04", "Warehouse unavailable"), + ("Succeeded", "Customer escalation triage", "Retry · R-1031", "Draft · revision 11", "Yesterday", "00:49", "$0.09", "Posted reply"), + ("Failed", "Customer escalation triage", "Manual · Calvin", "Draft · revision 11", "Yesterday", "00:13", "$0.02", "Linear permission denied"), + ("Succeeded", "Invoice follow-up", "Manual · you", "Published · v4", "2d ago", "02:04", "$0.20", "Sent 6 reminders"), +] +for idx, run in enumerate(runs): + activity_row(table_x, table_y + 42 + idx * 70, *run) +text(table_x, fy + FRAME_H - 50, + "Showing 7 of 128 Runs | New records appear here as soon as execution starts.", + FS_SMALL, MUTED, width=680) +end_frame() + + +# 11 Run detail +fx, fy = begin_frame(10, "11 Run detail - immutable record") +cx, cy, cw = app_shell(fx, fy, "Activity", title="Run #R-1042", + subtitle="Customer escalation triage | Started 18 seconds ago") +button(cx + cw - 328, fy + 22, 126, "Open workflow", color=BLUE, bg=BLUE_BG) +button(cx + cw - 190, fy + 22, 160, "Run again", primary=True, color=BLUE) +left_w = 780 +rect(cx + 30, cy + 28, left_w, 748, bg=SURFACE, stroke=LINE) +badge(cx + 54, cy + 52, "Running", "run") +text(cx + 54, cy + 100, "Customer escalation triage", FS_HEAD, INK, width=520) +text(cx + 54, cy + 138, + "Current draft · revision 12", FS_BODY, PURPLE, font=FONT_MONO, width=360) +summary_fields = [ + ("ORIGIN", "Manual · you"), + ("STARTED", "Aug 3, 2026 at 14:32:18"), + ("ELAPSED", "00:18"), + ("USAGE", "$0.03 · 5.8k tokens"), +] +for idx, (label, value) in enumerate(summary_fields): + sx = cx + 54 + (idx % 2) * 360 + sy = cy + 196 + (idx // 2) * 84 + text(sx, sy, label, FS_SMALL, MUTED, font=FONT_MONO, width=130) + text(sx, sy + 26, value, FS_SMALL, INK, width=310) +line(cx + 54, cy + 362, left_w - 48, 0, color=LINE) +text(cx + 54, cy + 390, "STEP TIMELINE", FS_SMALL, MUTED, font=FONT_MONO, width=170) +timeline = [ + ("Receive conversation", "0.8s", "ok"), + ("Classify severity", "3.4s", "ok"), + ("Route escalation", "0.1s", "ok"), + ("Prepare reply", "running 13s", "run"), + ("Post response", "queued", "muted"), +] +for idx, (name, duration, status) in enumerate(timeline): + ty = cy + 432 + idx * 58 + color = GREEN if status == "ok" else BLUE if status == "run" else FAINT + dot(cx + 58, ty + 4, 18, color, bg=color if status != "muted" else SURFACE) + if idx < len(timeline) - 1: + line(cx + 67, ty + 22, 0, 38, color=LINE) + text(cx + 94, ty + 2, name, FS_SMALL, INK, width=320) + text(cx + 460, ty + 2, duration, FS_SMALL, color, font=FONT_MONO, width=160) + +side_x = cx + 834 +side_w = cw - 864 +rect(side_x, cy + 28, side_w, 748, bg=SUBTLE, stroke=LINE) +text(side_x + 24, cy + 52, "RECORD", FS_SMALL, MUTED, font=FONT_MONO, width=100) +field(side_x + 24, cy + 88, side_w - 48, "Run", "R-1042", mono=True) +field(side_x + 24, cy + 172, side_w - 48, "Revision", "draft-r12", mono=True) +field(side_x + 24, cy + 256, side_w - 48, "Environment", "Production") +text(side_x + 24, cy + 346, "LINKED RUNS", FS_SMALL, MUTED, font=FONT_MONO, width=140) +rect(side_x + 24, cy + 376, side_w - 48, 76, bg=SURFACE, stroke=LINE) +text(side_x + 40, cy + 392, "No linked Runs yet", FS_SMALL, INK, width=240) +text(side_x + 40, cy + 419, + "Run again will create one.", FS_SMALL, MUTED, width=270) +rect(side_x + 24, cy + 480, side_w - 48, 124, bg=BLUE_BG, stroke=BLUE) +text(side_x + 40, cy + 500, "History is immutable", FS_BODY, BLUE, width=250) +text(side_x + 40, cy + 534, + "A new attempt keeps this record and links back to R-1042.", + FS_SMALL, INK, width=300) +button(side_x + 24, cy + 634, side_w - 48, "Run again", primary=True, color=BLUE) +end_frame() + + +# 12 Failure / Needs you recovery +fx, fy = begin_frame(11, "12 Failed Run - recovery creates a new record") +cx, cy, cw = app_shell(fx, fy, "Activity", title="Run #R-1038", + subtitle="Nightly order sync | Failed at step 4") +button(cx + cw - 328, fy + 22, 126, "Edit workflow", color=BLUE, bg=BLUE_BG) +button(cx + cw - 190, fy + 22, 160, "Retry", primary=True, color=BLUE) +rect(cx + 30, cy + 28, cw - 60, 110, bg=RED_BG, stroke=RED) +badge(cx + 52, cy + 52, "Failed", "fail") +text(cx + 160, cy + 56, "Warehouse connection is unavailable", FS_HEAD, RED, width=560) +text(cx + 160, cy + 90, + "Step 4 · Write completed orders | The connection rejected the request.", + FS_SMALL, INK, width=700) +text(cx + 52, cy + 168, "WHAT HAPPENED", FS_SMALL, MUTED, font=FONT_MONO, width=180) +rect(cx + 30, cy + 198, 720, 246, bg=SURFACE, stroke=LINE) +field(cx + 54, cy + 222, 672, "Revision", "Published · v9", mono=True) +field(cx + 54, cy + 306, 320, "Failed after", "00:21", mono=True) +field(cx + 390, cy + 306, 336, "Usage", "$0.04 · 8.1k tokens", mono=True) +text(cx + 54, cy + 400, + "The previous three completed steps remain visible in this Run record.", + FS_SMALL, MUTED, width=620) +side_x = cx + 780 +side_w = cw - 810 +text(side_x + 22, cy + 168, "RECOVER", FS_SMALL, MUTED, font=FONT_MONO, width=130) +rect(side_x, cy + 198, side_w, 246, bg=SUBTLE, stroke=LINE) +text(side_x + 24, cy + 222, "1. Repair the connection", FS_BODY, INK, width=330) +button(side_x + 24, cy + 254, side_w - 48, "Open connections", color=BLUE, bg=BLUE_BG) +text(side_x + 24, cy + 318, "2. Retry this Run", FS_BODY, INK, width=330) +button(side_x + 24, cy + 350, side_w - 48, "Retry", primary=True, color=BLUE) +text(cx + 30, cy + 486, "RETRY PREVIEW", FS_SMALL, MUTED, font=FONT_MONO, width=160) +rect(cx + 30, cy + 516, cw - 60, 204, bg=BLUE_BG, stroke=BLUE) +text(cx + 54, cy + 542, "A new Run will be created", FS_HEAD, BLUE, width=420) +text(cx + 54, cy + 584, + "New record: R-1043 | Source Run: R-1038 | Published · v9", + FS_BODY, INK, font=FONT_MONO, width=720) +text(cx + 54, cy + 624, + "R-1038 remains Failed. R-1043 starts from step 4 after the connection is repaired.", + FS_SMALL, MUTED, width=790) +button(cx + cw - 252, cy + 652, 88, "Cancel") +button(cx + cw - 152, cy + 652, 98, "Retry", primary=True, color=BLUE) +end_frame() + + +# 13 State sheet and mobile behavior +fx, fy = begin_frame(12, "13 Workflows and Activity - states") +cx, cy, cw = app_shell(fx, fy, "Activity", title="Workflows + Activity states", + subtitle="Empty, loading, error, and compact layouts for the two in-scope areas.") +state_w = 360 +state_y = cy + 34 +states = [ + ("Empty Workflows", "No workflows yet", "Create your first workflow directly.", "New workflow", BLUE, BLUE_BG), + ("Empty Activity", "No Runs match", "Change filters or run a workflow.", "Clear filters", MUTED, SUBTLE), + ("Loading Activity", "Loading Run history", "Keeping the current filters in place.", "", BLUE, BLUE_BG), +] +for idx, (label, title, body, action, color, bg) in enumerate(states): + x = cx + 30 + idx * 390 + rect(x, state_y, state_w, 218, bg=bg, stroke=color if idx != 1 else LINE) + text(x + 20, state_y + 18, label.upper(), FS_SMALL, color, font=FONT_MONO, width=260) + if idx == 2: + for bar in range(3): + rect(x + 24, state_y + 62 + bar * 42, state_w - 48 - bar * 34, 18, + bg="#dbeafe", stroke="#dbeafe") + else: + text(x + 20, state_y + 62, title, FS_HEAD, INK, width=310) + text(x + 20, state_y + 100, body, FS_SMALL, MUTED, width=310) + button(x + 20, state_y + 150, 130, action, + primary=(idx == 0), color=color, bg=SURFACE) + +error_y = state_y + 250 +rect(cx + 30, error_y, 750, 174, bg=RED_BG, stroke=RED) +text(cx + 52, error_y + 22, "ACTIVITY UNAVAILABLE", FS_SMALL, RED, font=FONT_MONO, width=240) +text(cx + 52, error_y + 58, "Run history could not be loaded", FS_HEAD, INK, width=520) +text(cx + 52, error_y + 96, + "Your Runs are still retained. Retry loading without losing search or filters.", + FS_SMALL, MUTED, width=620) +button(cx + 608, error_y + 108, 142, "Retry loading", color=RED, bg=RED_BG) + +mobile_x = cx + 826 +mobile_y = error_y +mobile_w = 370 +rect(mobile_x, mobile_y, mobile_w, 438, bg=SURFACE, stroke="#344054", sw=2) +rect(mobile_x, mobile_y, mobile_w, 58, bg=SIDEBAR, stroke=SIDEBAR, radius=False) +text(mobile_x + 18, mobile_y + 18, "Activity", FS_BODY, SURFACE, width=140) +button(mobile_x + 264, mobile_y + 12, 88, "Filters", color=SURFACE, bg=SIDEBAR, h=34) +mobile_runs = [ + ("Running", "Customer escalation triage", "Draft · r12", "run"), + ("Needs you", "Invoice follow-up", "Published · v4", "wait"), + ("Failed", "Nightly order sync", "Published · v9", "fail"), +] +for idx, (status, name, revision, palette) in enumerate(mobile_runs): + y = mobile_y + 76 + idx * 108 + rect(mobile_x + 14, y, mobile_w - 28, 92, bg=SURFACE, stroke=LINE) + badge(mobile_x + 28, y + 14, status, palette) + text(mobile_x + 28, y + 48, name, FS_SMALL, INK, width=210) + text(mobile_x + 230, y + 48, revision, FS_SMALL, MUTED, + font=FONT_MONO, width=100, align="right") +text(cx + 30, fy + FRAME_H - 54, + "Compact layouts preserve status, workflow, revision, and recovery actions before secondary metrics.", + FS_SMALL, MUTED, width=820) +end_frame() + + +# 14 Settings - AI defaults +fx, fy = begin_frame(13, "14 Settings - AI defaults") +cx, cy, cw = app_shell(fx, fy, "Settings", title="Settings", + subtitle="Personal defaults and access.") +settings_nav(cx + 26, cy + 34, "AI defaults") +main_x = cx + 236 +main_w = cw - 266 +text(main_x, cy + 34, "AI defaults", FS_HEAD, INK, width=360) +text(main_x, cy + 70, + "Choose the service and model used by new Chat, Studio, and global tool sessions without an override.", + FS_SMALL, MUTED, width=650) +line(main_x, cy + 108, main_w, 0, color=LINE) + +rows = [ + ("Preferred service", "Gateway or exact connected service.", "NyxID Gateway"), + ("Default model", "Leave unset to use the service default.", "gpt-4o-mini"), +] +for idx, (label, body, value) in enumerate(rows): + y = cy + 146 + idx * 104 + text(main_x, y + 10, label, FS_SMALL, INK, width=220) + text(main_x, y + 38, body, FS_SMALL, MUTED, width=230) + rect(main_x + 270, y, main_w - 270, 48, bg=SURFACE, stroke=LINE) + text(main_x + 286, y + 15, value, FS_SMALL, INK, width=main_w - 310) + line(main_x, y + 82, main_w, 0, color=LINE) +end_frame() + + +# 15 Settings - save and recovery states +fx, fy = begin_frame(14, "15 Settings - save and recovery states") +cx, cy, cw = app_shell(fx, fy, "Settings", title="Settings states", + subtitle="Dirty, accepted, fallback, and catalog recovery without dashboard noise.") +settings_nav(cx + 26, cy + 34, "AI defaults") +main_x = cx + 236 +main_w = cw - 266 +text(main_x, cy + 34, "AI defaults", FS_HEAD, INK, width=340) +badge(main_x + main_w - 126, cy + 34, "Unsaved", "wait") +line(main_x, cy + 92, main_w, 0, color=LINE) + +state_y = cy + 126 +state_gap = 18 +state_w = (main_w - state_gap * 2) / 3 +state_specs = [ + ("UNSAVED", "Sticky save bar appears", "Discard and Save changes stay close to the edited defaults.", AMBER, AMBER_BG), + ("ACCEPTED", "Confirming saved values", "Keep the submitted exact service visible until the committed values are observed.", BLUE, BLUE_BG), + ("FALLBACK", "Effective route changed", "Show fallback only when the selected service is unavailable.", AMBER, AMBER_BG), +] +for idx, (label, title_value, body, color, bg) in enumerate(state_specs): + x = main_x + idx * (state_w + state_gap) + rect(x, state_y, state_w, 218, bg=bg, stroke=color) + text(x + 16, state_y + 16, label, FS_SMALL, color, font=FONT_MONO, width=state_w - 32) + text(x + 16, state_y + 52, title_value, FS_BODY, INK, width=state_w - 32) + text(x + 16, state_y + 88, body, FS_SMALL, MUTED, + width=state_w - 32, line_height=1.45) + if idx == 0: + button(x + 16, state_y + 166, 92, "Discard") + button(x + 118, state_y + 166, 122, "Save changes", primary=True, color=BLUE) + elif idx == 1: + badge(x + 16, state_y + 166, "Observation pending", "run") + else: + badge(x + 16, state_y + 166, "Effective: Gateway", "wait") + +recovery_y = state_y + 252 +rect(main_x, recovery_y, (main_w - 18) / 2, 220, bg=SUBTLE, stroke=LINE) +text(main_x + 18, recovery_y + 18, "CATALOG UNAVAILABLE", FS_SMALL, AMBER, + font=FONT_MONO, width=300) +text(main_x + 18, recovery_y + 56, "Keep stored defaults visible", FS_BODY, INK, width=380) +text(main_x + 18, recovery_y + 92, + "Disable service and model editing until provider inventory can be loaded.", + FS_SMALL, MUTED, width=400, line_height=1.45) +button(main_x + 18, recovery_y + 160, 88, "Retry", color=AMBER, bg=AMBER_BG) + +error_x = main_x + (main_w - 18) / 2 + 18 +rect(error_x, recovery_y, (main_w - 18) / 2, 220, bg=RED_BG, stroke=RED) +text(error_x + 18, recovery_y + 18, "SAVE FAILED", FS_SMALL, RED, + font=FONT_MONO, width=300) +text(error_x + 18, recovery_y + 56, "Edits remain intact", FS_BODY, INK, width=380) +text(error_x + 18, recovery_y + 92, + "The exact service and model stay editable. Retry without rebuilding the draft.", + FS_SMALL, MUTED, width=400, line_height=1.45) +button(error_x + 18, recovery_y + 160, 126, "Save changes", color=RED, bg=RED_BG) + +savebar_y = cy + 680 +rect(main_x, savebar_y, main_w, 72, bg="#1d2939", stroke="#344054") +text(main_x + 18, savebar_y + 14, "Unsaved changes", FS_SMALL, SURFACE, width=280) +text(main_x + 18, savebar_y + 40, "Your AI defaults have not been saved.", FS_SMALL, "#d0d5dd", width=420) +button(main_x + main_w - 244, savebar_y + 17, 92, "Discard", color=SURFACE, bg="#1d2939") +button(main_x + main_w - 140, savebar_y + 17, 122, "Save changes", primary=True, color=BLUE) +end_frame() + + +# 16 Settings - Account +fx, fy = begin_frame(15, "16 Settings - Account") +cx, cy, cw = app_shell(fx, fy, "Settings", title="Settings", + subtitle="Identity and service access for this browser.") +settings_nav(cx + 26, cy + 34, "Account") +main_x = cx + 236 +main_w = cw - 266 +text(main_x, cy + 34, "Account", FS_HEAD, INK, width=340) +text(main_x, cy + 70, "Identity and service access for this browser session.", + FS_SMALL, MUTED, width=560) +line(main_x, cy + 108, main_w, 0, color=LINE) + +profile_y = cy + 138 +text(main_x, profile_y, "Profile", FS_BODY, INK, width=260) +button(main_x + main_w - 94, profile_y, 94, "Sign out", color=RED, bg=RED_BG) +dot(main_x, profile_y + 48, 50, BLUE) +text(main_x + 68, profile_y + 48, "Calvin Tan", FS_BODY, INK, width=260) +text(main_x + 68, profile_y + 78, "calvin@example.com", FS_SMALL, MUTED, width=300) + +detail_y = profile_y + 122 +detail_w = main_w / 2 +account_fields = [ + ("USER ID", "usr_7c9142...e8a13f"), + ("ROLES", "admin, operator"), + ("GROUPS", "platform"), + ("EMAIL", "Verified"), +] +for idx, (label, value) in enumerate(account_fields): + col = idx % 2 + row = idx // 2 + x = main_x + col * detail_w + y = detail_y + row * 76 + rect(x, y, detail_w, 76, bg=SURFACE, stroke=LINE, radius=False) + text(x + 14, y + 14, label, FS_SMALL, MUTED, font=FONT_MONO, width=detail_w - 28) + text(x + 14, y + 44, value, FS_SMALL, INK, width=detail_w - 28) + +auth_y = profile_y + 300 +line(main_x, auth_y - 22, main_w, 0, color=LINE) +text(main_x, auth_y, "Authentication", FS_BODY, INK, width=280) +button(main_x + main_w - 208, auth_y, 208, "Manage service access") +auth_fields = [ + ("SESSION EXPIRES", "2026/8/4 18:30"), + ("PROVIDER", "NyxID"), + ("SCOPE", "openid profile email"), + ("BROWSER TOKEN REFRESH", "Disabled"), +] +for idx, (label, value) in enumerate(auth_fields): + col = idx % 2 + row = idx // 2 + x = main_x + col * detail_w + y = auth_y + 56 + row * 76 + rect(x, y, detail_w, 76, bg=SURFACE, stroke=LINE, radius=False) + text(x + 14, y + 14, label, FS_SMALL, MUTED, font=FONT_MONO, width=detail_w - 28) + text(x + 14, y + 44, value, FS_SMALL, INK, width=detail_w - 28) + +signedout_y = cy + 704 +rect(main_x, signedout_y, main_w, 66, bg=SUBTLE, stroke=LINE) +text(main_x + 16, signedout_y + 14, "SIGNED-OUT STATE", FS_SMALL, MUTED, + font=FONT_MONO, width=180) +text(main_x + 214, signedout_y + 14, + "Replace identity claims with No active session and a Sign in action.", + FS_SMALL, INK, width=560) +end_frame() + + +# 17 Settings - Advanced and responsive behavior +fx, fy = begin_frame(16, "17 Settings - Advanced and responsive") +cx, cy, cw = app_shell(fx, fy, "Settings", title="Settings", + subtitle="Advanced diagnostics stay separate from everyday defaults.") +settings_nav(cx + 20, cy + 30, "Advanced") +advanced_x = cx + 218 +advanced_w = 610 +text(advanced_x, cy + 30, "Advanced", FS_HEAD, INK, width=280) +text(advanced_x, cy + 66, "Read-only runtime and routing details for troubleshooting.", + FS_SMALL, MUTED, width=480) +line(advanced_x, cy + 104, advanced_w, 0, color=LINE) +text(advanced_x, cy + 132, "Request values", FS_BODY, INK, width=260) +text(advanced_x, cy + 162, "Effective values supplied to new requests.", + FS_SMALL, MUTED, width=460) +technical_rows = [ + ("x-aevatar-llm-route", "nyxid_gateway"), + ("x-aevatar-llm-model", "gpt-4o-mini"), + ("studio.runtime_base_url", "http://127.0.0.1:5080"), + ("aevatar.runtime_mode", "local"), +] +for idx, (key, value) in enumerate(technical_rows): + y = cy + 204 + idx * 48 + rect(advanced_x, y, advanced_w, 48, bg=SIDEBAR, stroke="#344054", radius=False) + text(advanced_x + 14, y + 15, key, FS_SMALL, "#d0d5dd", + font=FONT_MONO, width=270) + text(advanced_x + 292, y + 15, value, FS_SMALL, "#98a2b3", + font=FONT_MONO, width=advanced_w - 306) + +mobile_x = cx + 858 +mobile_y = cy + 28 +mobile_w = 344 +rect(mobile_x, mobile_y, mobile_w, 774, bg=SURFACE, stroke="#344054", sw=2) +rect(mobile_x, mobile_y, mobile_w, 58, bg=SIDEBAR, stroke=SIDEBAR, radius=False) +text(mobile_x + 14, mobile_y + 18, "Settings", FS_BODY, SURFACE, width=120) +text(mobile_x + 14, mobile_y + 80, "Settings", FS_HEAD, INK, width=180) +text(mobile_x + 14, mobile_y + 116, + "Personal defaults and access.", + FS_SMALL, MUTED, width=310) +settings_nav(mobile_x + 10, mobile_y + 166, "AI defaults", mobile=True) +text(mobile_x + 14, mobile_y + 236, "AI defaults", FS_HEAD, INK, width=220) +text(mobile_x + 14, mobile_y + 274, + "Service and model for new sessions without an override.", + FS_SMALL, MUTED, width=310) +line(mobile_x + 14, mobile_y + 322, mobile_w - 28, 0, color=LINE) +text(mobile_x + 14, mobile_y + 348, "Preferred service", FS_SMALL, INK, width=260) +field(mobile_x + 14, mobile_y + 376, mobile_w - 28, "Service", "NyxID Gateway") +text(mobile_x + 14, mobile_y + 472, "Default model", FS_SMALL, INK, width=260) +field(mobile_x + 14, mobile_y + 500, mobile_w - 28, "Model", "gpt-4o-mini") +rect(mobile_x + 14, mobile_y + 670, mobile_w - 28, 70, bg="#1d2939", stroke="#344054") +text(mobile_x + 26, mobile_y + 682, "Unsaved changes", FS_SMALL, SURFACE, width=180) +button(mobile_x + 202, mobile_y + 686, 116, "Save changes", primary=True, color=BLUE, h=34) +end_frame() + + +document = { + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": elements, + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": False, + "viewBackgroundColor": "#eef2f6", + }, + "files": {}, +} + +OUT.write_text(json.dumps(document, indent=2, ensure_ascii=True) + "\n", encoding="utf-8") + + +# Structural and semantic checks +ids = [item["id"] for item in elements] +assert len(ids) == len(set(ids)), "duplicate Excalidraw element IDs" +frames = {item["id"]: item for item in elements if item["type"] == "frame"} +assert len(frames) == 17, f"expected 17 frames, got {len(frames)}" + +escaped = [] +for item in elements: + frame_id = item.get("frameId") + if not frame_id: + continue + owner = frames[frame_id] + if (item["x"] < owner["x"] - 2 or item["y"] < owner["y"] - 2 + or item["x"] + item["width"] > owner["x"] + owner["width"] + 2 + or item["y"] + item["height"] > owner["y"] + owner["height"] + 2): + escaped.append((owner["name"], item["type"], item.get("text", "")[:60])) +assert not escaped, f"elements escaped frames: {escaped[:8]}" + +visible_text = "\n".join(item["text"] for item in elements if item["type"] == "text") +for forbidden in ("Invoke", "Draft Run", "Test Run", "Create Team", "Create Member", "Open Flows", "Fix flow"): + assert forbidden.casefold() not in visible_text.casefold(), f"stale term: {forbidden}" + +standalone_flow = re.compile(r"(?= 10 +assert visible_text.count("Activity") >= 12 +assert visible_text.count("Settings") >= 8 +assert "This Run will be saved in Activity." in visible_text +assert "Current draft · revision 12" in visible_text +assert "Run again will create one." in visible_text +assert "Preferred service" in visible_text +assert "Manage service access" in visible_text +assert "AI defaults" in visible_text +assert "studio.runtime_base_url" in visible_text +assert "aevatar.runtime_mode" in visible_text + +print(f"wrote {OUT}") +print(f"elements: {len(elements)}") +print(f"frames: {len(frames)}") +print("semantic checks: passed") +print("frame bounds: passed") diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-activity.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-activity.png new file mode 100644 index 0000000000000000000000000000000000000000..83139c8ad755a6d24985457d9b2d9324c9586559 GIT binary patch literal 143323 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

V*}H|kD=Ipgg>`gC(my69rX{s>wIu%f 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�m(vx9eM>SjbWRo;%>bD*SI*io76&$+OyO{DTk^v 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 TA~)N^{%gf@QMq0ZbR{ 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 JigyM9{|7W?O2_~J literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-blank.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-blank.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8500734770e601f4e276635337091f6eca5de0 GIT binary patch literal 49457 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-describe.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-describe.png new file mode 100644 index 0000000000000000000000000000000000000000..8ebe09b5f8d103eb9e18a6c975fea17499f62299 GIT binary patch literal 81550 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-import.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-import.png new file mode 100644 index 0000000000000000000000000000000000000000..146a32f65395b4e8d677dc3411813298111aba58 GIT binary patch literal 67719 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 XctSWXzz6u`$U_~&``_=rc=LY%!IK{@ literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-mobile.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-create-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1e43e420a0b9462a4dfe81b27fe3e3d0ddf882 GIT binary patch literal 27974 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%0D5h8T&IwH2E!#2^>_?za< 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-node.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-node.png new file mode 100644 index 0000000000000000000000000000000000000000..389569aa25813ad590d3464a7f52eb8e62ddf115 GIT binary patch literal 103470 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˺J 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-yaml.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-editor-yaml.png new file mode 100644 index 0000000000000000000000000000000000000000..2c2a25fb2fa140bacbcf6df070a211e48f9f605e GIT binary patch literal 106924 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 uz& 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-account.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-account.png new file mode 100644 index 0000000000000000000000000000000000000000..d3a69b5d6a43b6562f7f530e8eca91b97e88ca2f GIT binary patch literal 61391 zcmd43cT`i`+bxWGl!FKy6;Y`wO}e1;b`a^kcaYu*9YTwWfQa-aU3v{j384o>dXpLm z3B4FfLhmHs#^3wi@qXiuasT?p_%8pjM@X{Qe%4xZJ1_%z2wY^GeEjR?)u?x_yWb+6qeg5`z)NFDnadhw^VH_2DH^nO%XUZS8Tuk_3d6l7#%BOv9te-Am6O@y2a{ycBC@O8?T zyY;x~Nzj7*=qUdgZ6uwIaiiVk++{=?-Dy~1nA}mINo~k}!fJWJ!opi=N3_P-*?>V+ z@Oh^pOyZ~qgvOW_zIC;=b#*OtL-mcC|A}J@$zSyTms!p3yW4(JzebeNZ|$Aw{yF(K zD+}R}R-3>4{V9_@qLU8p2HKzJx%tq(xbD!>a?z>vQzSU(!I$LZ$vjaAuLZ2q1)DHg zrVFef;D@iB{BdZ$0-Q4U+QSLQr3U;M4@HBAHsaN`_wo-JqqChOM<3tmxc81o+Vf=t zr_0vOp2Oq;8rAVC^K$tIu)XDLc)MvtNSSW#VC{DC1U(v7=@E*$eW!m7N;uoQFw&{Y zlJs0^k6Av48iCIdDeCx)-`cYHvBF z3RPIB@X(805Z~noOnyGwYp0siBJb`d?iGCb@+I3ggMux|f0 z;)k0n=56&qEt|Lyv1<{dqo#jWD}ZSLH(L$KX9czu&pU+DY!`%_mih2kS3A+A`Qjy? zMEP!^@~g-w$cZ++J3m;tlFRj_L8D`s00cz(iROId8^&e!%h5Wu^}NC|>gW16C?&|E z!5{qV*WHb#jbDsqhZ*(CFE#K9W3hD=Gv8MIq+_jBb*@4Jidlz5er{|`%c+VDnHnAK zfE&hpJ>-?1*;`m#-V3b_IXa6r*%R>^&fBw9QOf<~Cle{L;-J%SXIEsN&FECiF;37X zzo*n&e^9>J8PlntffILs!;`whWoPfq9QLj~!il&vciCQy8tEFC>Ww|U=bFT&qn)^s zn$u?7=G1!qMlK?5rMLQik9sDjK~a*}rq0j6<0(NbE%BA_kB{@kSbNqVhV`CMRft-~ z#P^V_pFiJ}D$SZ_si`Wr1h2cH&VK)XlxAgZty9NwrnjU$0{s*fg+v|@{YzFD&S3SY zcsC^MxN@jmO-;?#_XI9|Aqa^K!R9jeWWYDmg=15j0{q{6G?qR?DlLa(B=~NoR+%R{ z;t8??4+m@N>;EkB1)t-K8!=L?O=as=Xe$4>*-o|Fp;7_(tdJb3nHsfXKP&6;92Gvt zA0WgR0e*gd0Rbmn#8eSfys#!XgT^UmFZ5_?JGMu}9|59*eRETM9k-@NS26qDXQWM5 zpfpOKJKelkUyy^_xM^dSDI~c5^1`N9(MNh9g||95ALlSWH>d8I_^J2#&G2u^g||0; zJxUOHa5mX)E$n&=q7uM!%Uux2h^9oem93y*JVNJl-xyZ610NZ(SZmA+72l zM~+^;jZOr?2pUAh;FRknJ3|J#M2LYhB)dG6W z$5=%0I#J`>S4mL3@$a$RApoE#Pi@-EKp9oKVgs~?4)T>^y|C(AJm^C84+Fh4y)$fGW+-T zC_g_b6%)c^zuvf%3HcHlUB2cz``u952ahs`<{ToeV45&bFif*vl??Xc5WlH|f;(1b zcS80K8ugroOPSv&uf)$hi6PobAF}+bC7hm=UwQRMg5!SMs$o*W|6TzNTJcUhDl7NTcs(q1v9cP=btPW~&xI@{WiQB}1Z_y7A$DHm-F=}hw`z!d?qPSabQ;_6Znapz2v$z8(#%tCxEJn2(lLX`#hiF-#6)s&zJ8JO#qPV~TOm8R)pDdpjwl?QK|E74 znUq5i`4ivWxLSf43t;m+ft^slgViJi)>$eD0Z(uxr>WA_z&$r^csnQ2^z_!fd-sqa zR3-2BWUG!HUBaC_dpKpk7iOfRQ$AS0W~B_4F)=y4f*hu4nxFr6*PZlPPN@b6>Wsqw!dG4fIb1qVwIy4E`pMC)z|nCXwIX&ki4TJ941{Gg3^w*L_UQhtU*w@QOI zxM7(95FDYq5?DWHe3h8%>{MWy4<1LO2jPZyVO!eyVqmMi zde(QFIq%CS`5UKdW1Sz2bRsBDMYd~oa!@!PXXVs`KS}5U#WxT4W36`v{j)vzPekq2 z!cU*Rv;=J1y?*_g5Cmp9>CLlMb=exqfM;dWV@$nlg*HCdMSlA9ieJdr*H?K~9%^tE zcf{a^Badrcp6Md#UQdKb4`Tn7k}{`ON9+&obaxz4IwZTdqlb@&DOCoP`T`kYsmC3B%${UC?) zq0XPgU}eQ7U30_gZ2vj=CH|uSm-0}5zq=giimnMRPC8~3VbMVq-iMryzJy2oDSQ>> z+OMr>vazv2)N)$vF4tHG41jt}i9xvTb)x`lP}Je?sZxi<*MH8Q1<;p{_Xc>|m=4PZ z0MFD^zl>9A{`t$V`KZ7M{ zG0)zAOfMODe6-7P)RV*^>}HxeW7(;YDJ8O$GuznYV*lMG zM+36I|NgqO*^u2Da8Av|R(p)coCog}a88y9IZs)2p7}KUZF3s51)Wne6h5iN3RybZ z3hr%!# zs>+=D6<-VJ64}+ieExjl(yOV&`0BGfDz!>e%QIRznM-A+S8;!Z&C1#;mt zoRQFG=VFMm@@JQS5oA|0LGsoxd!3c@=56X%f-f;xyv0_|x zN5-L?%0owc&vSiv``52uCc>Z;ZX@HA@Q4U;5fLC+S{>egOnvaYR_#r}=h)c930aP*I(LfOz6O-`T!$O+ug-zGW%F2!NfHMNuJdSvfcykF(99X@&K7&QPtypEShK9(6Mb z@S3Nh!^eahU(!U5k1*}rv|FO?Rx=yhMz0iARny@%%kcbSGXz)zT$vHlMmUVTn$dQz zUG%NTP{Uj+QtdC?l&sLgs%10mLXHD|u zqtB0Vn{?-(dGm68*Wioy$dQXex(AqlYm0WtBDJ5J!hE4uz`m$4uYm*o4HyoHkR2C}@SsOscXc2D2oqt&%yNmJ13B zBm;esr8)*3&iy}s8h&_ZJlPKb-?q57)8gJxL~KsE?yv^G&9(P3H}?Z4OSR0w`ipn9 zghBHLSAk!E{W%fLU$C>UPd7|YfBjmKVPtIdnC`KhoE!(=m&nLSDAd)#q2xp6&;L3z z!m!dO_JRwV_R@6xZ6864j9KYnVG6_Ph@(B2iEq6Sg_A*C>?rw(vT4`_`D|nEaVl9OhUu z>aYUq-^BV7#-bx+o_2kvahb2lj%Ze)(O}t}OMs!kDni*JKKxQoWwN`(K?qxWXP#&{ zUaDq@sHtf9vANtUc6g|80gja{hhtj^P|Na=lRPVYNaG-6!#!M&w4l$kuDwE zV}D~Vg?WF5d`bz(8qjqJ{S>b4{ng^ufm99xGVgGVyOW`r>BM29Vs-9jOTp}qoBNE? zK3Y)7BWf|rjn|@Osb?QfTW!WWo-SsggboflzVgNPEWF3Cs?OAHwn?4GyU_*MpuV=7}>eCjfNwvHFl8ZQ;Lj=!aG41gXhHLHaFM6QNR$uzH@& z7}}T23o>yL83QZ)vO3yH4JZECamdJM^D$#tFMsG2`TB7b(H#`?5` zYpPJPs;U(U>5+=&FI+UM*k@J#WsU{VG3KcLUKG0N{`0$$6S|d}HLs1#a?PQ)%Z0c4 zmm+O|wLjJsn=Qgu<&M24XEXYZU^dyIb4)M6ajFuEYC5NHMb-{Qj;G#BOb`fCQ$BqF z-itk~8X*T_+3p15J=~QRpv(Bj`(Pjs-NdarV$@$K(3i(^5Hv?G@UG9=G ztntnG@BDs@od*ySo=fc-9_WwH!32PtZvG{a@S~7RnDyJ^i^;z(L!|B~Lukf;wP!zU z!VHf7cysJuNdD?pxo+;yLz82BKEv|K%6Q#8mGf?JD|%dLx_bS0Iz=-;fKRHG92?W{ zsvkLZ%qaPJ<#A^#PPK+9yit}hSB1TAL8%w|@U30GQ`jh!Ro*b~v}uv^ z5D36Kh6Z@tu_oAH#Le`D2;E2MszskYF?`%jeQ;R zXw;$)eb89WO8e-@k_p@o%> zP0d8$EaUxIVZ7F8lz|Jl+!1;P<4jLW3)vki;BM;Q0wzAAH7oif!+WX^tl@Dp4JRMJ zEofGu-R{ZVl<*HaW+l_Zp~=1de1^9Kff;AT*zL9ePfXIHZr=R}$mEmwVj~*UuwNR> zLuETx{|T7*iq)X}^+cxo8y`f{i%^!B7@T`y{+UJ}UlL1@~?U=;Q^XVfYgm9Uu=Rw1tJr{t$M-us61c4G7yn z)HBA-FL=*2i_@tALP4ds51<|ud zRK$?}{DtoT5>_^5P9&<0NswbsX=@@23T%jy3bH?*WiMVuVym@E<0+YxO%7}BBVjOs zsn$TbwWS^a81aC;4+r)Bt0{O$T3X!Kq(8II)w%A&w~))iFqV6TuQ!c5?zNCRB z^HyP2c|OHvz42gtB@ci)Wijqr%>j*Lte=Z!O@UhwzqVeCFCvVMzdqzhGLZDH(rZO8 zF0XfM7(HM8Y#Ti8reZc!>~M|BFV&)$+hMlG4W?Yk!u)9WlA86<=5t6Zs>qd3kYYpE zxG|(y6)b_t%-Wjd9c$BRTQpEoqN3W=T&9+m?z3>j zg{}|M0&p?PUtKSxkKHI7L-lL-g`b@K1%4$ge2z@v;23lL5)GS7jLOTi=H}t~68%NM zgr8Fk)r~UVA6Z}#;x*UjAGnxdWn)YG`nANFm4$`P8J1>No0yczt-+F^AsWS=-~+m6 z^9hKC6R7&0VS)3-%Aeibw|?ltoP4#sd;9(t+1Y4C{o&F`c4!ywJ-B+AL~GW zyhz;vS*d4<|3I_0Cg`l|I#n0{hvgu-^J||<#BcLvTF_TuO)lZ-P23V=vt3HCfyuX%nhw9X3s6R}*M7Oks`T`D3Oqmbfw{80LAOAv& z2ry1}ckiHZIjm``rzB`j!buclUIlPgDl4OPN;lGgpruGun!!is=G>M(j^f-Z>4aS+ zy?;CK=j1_(!YNbqQ;Ld;qUk23eJ@si$PF9>r>Cb6W}h}ECN9VEwTVF%mp+6w$r)6d zCcbtt{m^Cd-vq(i)>cXLSb-24srHG_f#0pVS>Qolh>Q^L#C-qt%ib_)n9b)=On^`N zE2n?x=mO7=FbsrddIkm;7nj=)INeV_^X5J`8WZN=P>Oy^jp=1DS*cbo{JgP6xIt{N zSI$BS?yu}-UYs99rSjUhVh5Wrjn1*KyfDnZe)ZP>drl1Qz{D z<2eKbl`$4SEnRQhy13s{v_HrFi z`1jWiwP%qPxpYsKcXF*CANd|kN5;<+9Bds$J${TBfrM+_te351U%#G(ySiFg)!0T6 z!=p`rh0veO<#m{jQ>ExrGc_|SCWzL0!TnHv8&9R-^`M@#X_D;N$xY$XFhDl0QD>kA zcWoS=6_Sxf;zM@&N^(MG)(|FEVY7a?D|?Jmxw?tp))}&Jsp_Umz|p1q;#l%EAPM~- zs}Cp}y9q{!1-*kLTO`Qis3NYUQF#EIbzJjo;q1PoU`S}0l}nxo8T1vk=oAA4si2mX*K0g2)oX(iT+q2v(6B9M#EGh7(sqyRIv>n z`}*OneR*chN0sy6XJ(riWNLf%HpzwkE*aT9F~wYOB}5Cj1+WT~6vT)IoZ}>6fG{%7 z+7kCV%+uD6D7rg)S9bD@srw5NS=ghI{djLxSUV2Af*hS!J9Yh{u%RfAP)neOhpu_mFXAe{bvdBImL~B`m~d;K*>q$ zcrU9^XT061AhY+t2x`(IWGiY!itZqH@K(gfpkI(UHyPzy zt&TcXVvqL+K&&b!FwlI!{Cin;A~?L>ya>bDzs4iJF;NL#`ng=NHU$-*o4YAonEgED z;P>vfxbGxX$7=(R=)V#xAPmIj>m4lVx(Wu4;kgdp zr@Z_^y!znEY^ccynvsf0Qdw11JI-}ZDn#KTm6Jq3;O(#V0sYG4AAHgw%O+@c0fDk$ zxSvh^SB<;GXs*P>M9Eekzpn}Imc$qe?9@;|>F>k2_UKZ}p6cM)m1PyB)&f?*k)-{6 z(Zpr5$u{hQQ?W(+XY(%{)OkYhjN-y&5*4j20tw1V%$FUy#WN|wywkmW?W+_Mztd5R zoc45caF$=kbuMJZIxiz0U%KnBQ_pVErz>jgH&q;PNfByVgf(<`8h>CZ7 zXx89=x^YN%y1%AnKQuIyV`?UC;~B1!Jh7`V`5|p)`EGi2G?PWsBZm9=pp>Kw;xGO3 zNvWAwF@aD02lz_h9`91IVP86~k@)ih?y7P=l~3Q)&j9$Y|N8Z-?sEfqr6F-ELJZ3h zlbC?V#=(4%YE)Fzh?S$RuJ>t8;bcGg!>W=nXJNOpn?7y6ly!RHCb5m0*40&R0RZ?~BBkmB7hFi}8 z57fpiu67>|@ixewBPyV%#$7u%ok0-?=}PgEh7*>VclUZSuJN zh{j1UD$VO*Qn97IeKCzb6yr|#!RjM%_iFkZfn5F_J*UL8*R9EsFqmE#LId^>uz3qr z&Nxch^%ZSi06IFL)U($DGY{2lm8lgn`|!aBZgshHDJ3s2Z^Q@HsWncebe*W&5Og)k z4~ddhh#7Ff7z<9!dIHWjIn3`bqWg694#efJyN()9`(ifqxJt{nr2Pxr#)UKjy=Z_@ zJjno(_a_l!7CB=T9yfHxA$MFxEBcntGhEO$n3+D0t{*cz=|W=)RE#*c<(?p9bKpY` z`&M0nn9=!I`V+$PKX1%X`OZC9oe)9cmx(jS^V_9tPs%i;wvgZFmc?@&UeSVI3Gu7L zXKCdUef7^CpDzo8_x($2ySuToSB`;^u1)uc4<0qq`#(&c$bCH7DI6S))5`C2fa z9fB%U-Y~OHOh}wG=_|UQ6X%XMf7fin%T-=19?Z<+%6UH#Y3c3^+$Y{`Ho$pi{`P}n_zSYCMp!&ieU0tHJa%*;WXF3wuX*|V>eX2MMj<; z@F5bC%#Le+=sKuSVF74ye7$mO4wC0osBQEr<(Kv7=OKld91Ue~ya?&F>s7%=mco&82U%2?0aigDa4{L73aSos4f0SYTLb89 zBmNhMlExM7_^MK?)zk5hgfzzj4Edrn`;vX78l1n;Xi5)4xA9rNp@~MipkZG4YQT(( ze+@>u%`x|VGUmf}QXvKTrP+L&EIMwcpkSqLmdB)~Bt9NDmL$L$70@8c zB<1THMFg_eb6?P;in@Asvw!Dh`+v%q7A29;8z+d8-_ubBZpJ1?YXi$#jV<#iwZJ1L zDNI4g(l_}yJUiscm~b&glof`7U0g0oYYl$@l1u=HyF z7Wp9iW2IX2y~N)0v`=k>BMj-@qkkS9V5T3R^#@*yrDxb0s|?xqZO;tbWVtn%d1qF9dM5m%9GWU<1slI?Twdf zl|U(opD&huAH+DmR?*~rn351s967=8x6Z6haI~tYc1?9!JT!XpQ%CUY*UY?Tt&L8X zm5KJec=4bnw~apLt98E<{$zoKG4bQG9n0?k<@Yh?R{62HS@jfTKjuatW#(2=x{gF? zfyH80VCoS4>V*Nfn>=i>k=TU4m?}Y3A0@psE3MzI4*5hq&X|G+C(4h)0vR zgzQ(QyZsF}RsW1b$GJyJvpfc$JZI@tYZehlll#8{h*No=@-09T!*?VCi%*vmZx84z z{Gan%p?@lA(7juaZ!#uIyk;Co=BO;9%hLv106q(g4Eb~YLtxc@j)}y;@C^XB$IKip zc2u04-1RW$z}vURqSoWMW^$H?XHfd|TY2N@a);n6`Pk--sp`_YRH1tsE4$u0T&({% zVk+vX0Uf~avfE4})-)EWO{l)XlQEURy|$7zu+C=&nyg6i>ySwJu8Jyd{{G)wKw9+{ zprDEg_NVS~{mDezGu|vO;POeTlHjrPcZr3cJAmY3tf)W{y|a=BFs}s#EOgH0gHvis zT6xx1*)DNOne-1HY`z8M+bET*@t=wF#_K&}wg$HM%g_N~RAi)%d5R>dI=4xy88{vwlxtkqEmvXReb#dUG8Btg zFWH;tHiEeVbA#n%&bL~rf~=lp*_&MYBwwZi__Lx4v@FDVsb#?~woaWjrYu@KU+&0! z-%DcQJq7=uhh^p!u&_H%?llbsu88P^J8*Ll|HGS1Voq|O_4$MRaFTH^O5Af>q{8;O zH1KfghfJa&<~$aSzULte`RubSY^7E_O7rxxz&bL0`vSO@wb4$SRJICP>Z3)u0hX%@3$RopXzo3?9;ai zkyj&^z3Qw8w>$)j$s>kXfJ@QNEoc)kD$fE`@ap?bIhzB?yi-tX9RM-*Q!7U@;YISv z0wxz!0NaYJ(yR8kEE7LwG->gS($Co2eTb-YH9`(ef;R?Nm)nRosLbRZb@RB%LP8>y zn8w$p20*O@5FkFC3yD|FuD6*;#SgsC35f!f-Xj6LCwnk)#+zX@gLv%QoDQ=PAf6&#a~l8#*+|>6(;>^BC0j1W*REBy25qNg zLDfQ0$s5SZ=gBo>RKlrV4=cmu-h^C)q;kAW2^PM%LFcT(dUz-4>fj+LjaWb|77Qvh zL&cH{JkN2=vlmohALSaF#g2cYO@04`qHC7lzOm8Gy$+tk9_csHQFZ<79x`iLJZR)H zhSn?Q2H}q!phr2D1^`oSS|4zp6-c$U?PC0@@@bW?U10X2mxSlV#n|L5i>IgO_rPYI z@g+I(4L|GI;m!f1TPk9Ly)z^}dvmhvx!|lHLkc@Cc^2w5E5AWm5G+%4u@@F{_>>tE z6%%RJI6MzI9%WLXRK%2*XjvBxxQU3*9B)nvRi!q!xJq5_A!Fz8rcXdP?xQ!X<-~){ zEjl=U-P6MbS(AM>rwBj%^}Tn7+*<=Z_9O50%+g+EgZpWC3z-6BKb}Q!uPxaR4@APj z$;oq}7ds?Q86sH+3ZG3j@WGb3)|6*6Hj33bE$Fa7Xhw9ii&{|YyyhF}l+4E#F zGNpJeemL${2nt~``MFTPa{e(rt$Ca3F56*w-{=~k)UGwhS00`G zp3j)}`)!jSpcfuA(b@c#QFKcpU};zHQ4L*Juy;gH0^ggS4fzM6-UnL=$tIgqWiM>r z`TTply|~M2m?q$5)E@fH+a#5puv}GJP%d-X?Bh7^e@s{g@|~d^ zVR#GKenaA;t}KOw9Y!(G|=gU#d$emO|N#Jv&~_)naQ24S?pqd zTn_t1@(hCe3Y{MTxgp7|g@w`DJ+C_YBIVOiKNQs?0b}Sq z&gog?zw7nUW7~bHIY%i9jtdSTF49Ff?k{EUyx)`qD*YPA+9d|%A1+L*3K;u=r0?iA z`gOk63cJUt60vXn!Nb%iMzU5`+K2sxwh6SNHWXx0Cp)b%zV%bMO7$A5J^F!EUbd)$ z_gmRbR>`+n3(f5cTj{;@tF*;j;enLdN&ghfxME7_@rN$?1WG>9PgNQno`t~$HO}YV z(qA%I6(%ZqW6ofxx)Np`ZBI{CRZ~VjVV|FIcbdh(XIGj&z7o`@lX$~Z^lNI)G3!j9 z`$$eujMMA84EOEb+)%LK`)Omi7Vp(92at_T0YUDFL5v3yaJsK#M2RA$Wbnc*3Bq#f z_&#MvWB?~=WPW~Db4IPPPScxsAX8Jy)xz|9Jp?IcyzRXkNE4XXpW2fT$PwDT^Mzg5 zT7#8VmJG#apVRvv72X8zD)AkD`5O0Yhr;B@$7x*i$b#@YAxd$U{cNIM0Irp=(Xa`qVC5F5_&Wy`|l9teiqn) z@5YZNWZa>f;Ho{&4$-9rN5*7J%tK|z$3mLM_0A5RUOEll2~OEv`Okv6103gXNv>ZH zy$E8o5P$3BlxL2+OH(PXZeHUwTO-)ioag{+Vnzaj9V{&X944P%U0WR!8#S^Pu6*`D zh@W4|XRU_5&>4ohUAib|EPh09R%@Ah@rwr=?xIBuMMNdG8oT>?#yu!}LVCf7<2}=2 zX_3HOzdlA~a2kTI-%oL&KOJXkN~A1H1z}o_THi%zOFIpE!`{2Re)b5KdGst5H964F zZ*QY}(2;My^0(={ypQxkd;s`UTyZ`dpXXPe*ic0JHxSLq6A{;Tk5$xm+WwTHVy=`R zc(9^eoEYR3(sjFU`8{*-OY}Z_PX1rl66m2b4S}1(vSUR)`(h6UPC3)!7=!1LC&2H5f2v^Qd5;YUFw6}P3BB$2-zfT-2@^km;eLt#PDdN4-wV2zVk`Z z2+0L67u#GcQc1^QAQ!mcdWaE_HL?PUJjsnGJ&RO~VI{Vr2cJKEN%#P`0P@5`+=?lq zc1(MpdINjum_ds5S_J*K^o)!`GtT!vmL8a_2>Xhf3%-1Lxs;(~(gV}x-UY6=s z3T%Y{*06*#=Zmbs_qfVoPn(RM0iMo8YF(hJJ{sH>+Zs2o7n%ZgNQ1|wxt$r{ooGx7 z=m_HkDiBz*oLwMc#VaZF6qxWn7Sao6q3vvbkk9Ck(+R{{BHA?Tpy%`%3k#5#oCHN(^51%_-YF0a1ZX+|c#-6Re7@%9;Pe{`(B~ zHxb8Qixqg4gay!UfONmdMznkbVGXpA2C9XqkF@vl0gcBCW|Y+~g$ z-F$-&WG;asv54pKOn;Rtbiv9~lZ61QLb)E)rHOH3o7VBwo=P(@J0IX6j9e-KW5w3w21WCV z2eNH>gN)j-FZLoQ`Vk^zz+9A(H$4DJrK59Sv>?d{%nQ^33EaIewG~wT6X+SM%n>hz zcq@aj2gPZ+&vi_Xh`;;DYp`8h@F3#A;K!c@d$ynPlKwz97MG`Lv+Mfed)c1eF+g%) zXCKfRclfe`Ws(SBi>d@HL-EU}tIocPtGU+p3WO7&bL`q<95{SrcjHhzwC&;$C_&WK zkHYo>l?tn?J?!|bPsh4|!XLoXnH295@f$yiza_AWi*Ew?aABbDuR)dSw&wvLXLc6JFfh)@5^ zO7dbx4@f`&GUPGA>_AdQy6ZD_ob>T_^ZR=pgPCyoXH-Bno3`B-zr4O`Q3zBe=7Pgl zd+t|>`DEbx*rGvGHrt$Mr&;t{6XeeUh!yuhC1_dOJuemQBE{E@!$S7v@=6VKPN;bn z7IAxJ*<&VP2^AAKF{<>xf+K9j!f*~@vpW>TR!C=HV5lHLQ3iK!2^8B;E3 zp4v&yWKOJsVa&&f5VS0%Dd6QW;%p^fa~y4uZ&E8M)92>6E8FGSb|$M6J-V!h1l*{U z$*TszWL7JUz2!F6u0{you5WQKz!a=S@v^3;O469p+ZeG|m)1vn8WBV%M@#bpO>@B~ zz2CXg=CkO{k|28k1aq-`sF0# zbHW*8iV(0E#!Q>@=|FOXqy&53mcJAKn+s5y!Aycn#M&I$h&3!RqCpp?NJ=$T0je^< z1f%l*ZtJ+&j)<2<7p&MsLCl5nrUv-N^z~_Guq^!ieL!39{)j9ri5knIq;}ipzHHkW z6li0GYDWYPz7Ofbu>p&9#hb%zT1<5;0fEgpX$T4s;xWw>Z@ftT?{~!JbLTkyp zP7JeO z(B9(Qm?j^q&qp*2B_h|1ZPB^kfukMexLw4_CWT7{C0Os$3@J^zkjnK4{k9xL*&n*4j?_x3_nwtH;R5 zbYS&=I&+K3D6vidd&J`C1A1ig5;%oO-sSoe?A=eO_Xob4=ks$KPZ;zGF*E1iV-14i zrvbb2ShY1>aI0VL2QPbKqxB{VnxTIwYTE)!F4V2GXr34z=Cx>(&^9XejrO!v898@O z*BE_fK}px*ff2weo559zkH*?x!h7Bkg6&5#6WqGGqc>b<8uk)n&o<#OD{PBJ58+$; z4l7Hd+XA+iPJT`Z7!%2zE@0wt<}JwH-qtbAtt+v3I6ORjvQp2?mHYG)U?u^)H4h%O{Z%R4KECXFC6GLd z%M?#A&zRx?+O1f>7$Q8pDouH_8zl>Bz~k~MG&G$GA;JM$ANkrY>Xa@z_t`2*HDQop(bG$8PGTyo;IOw)Yu3T+5ZL}S(Mb7Azd5w42M_L9HJjJtX&$i06 zF+DD>qRcFi-p8%*47Mv(0g+3cz;6j5sqz zq?9_|5)Nzh$9n48=x$E&+@|6F?o$p4&roSzLix~nHi{hM4t=exme!!+SL!Z6oh-M; z@r$Tui!?cQrf(uE-zX^b0lQVw??_k3fKU79Y)41PWj9YOo>opUP!zV^in8Tf;7# z>ei*_EYx4WF$wiK`hS>v>#(Z#u+5ikp`x(2peU(GNUKPfMRzwycgLcaiim=X?i6Vj zAuTN;T}nzUI;6W4(;f~M4^v3uK~20JYw<0VEaZJ3t}BZL=vKCU1}K~gv^Vr$_M|=@)9}cpM(g( z%RB$pwp*dL&HsRzMS#&ZBmL%}dh{Yo$z4n!(L&c}#Ao}Fnxxk;-~J;aB6c}Q{X>wXifn^VjEK|294+20zZBNJlmw>?Uzl_zhido$T_#=HeB$hLMK8pPP%d@R7 zdBlGHwkrgo{729!E&iJ}1YpSTJ+?VYawTlcaPW3=WZl;A$V#=O^rJJ55Vc$t@Bj1L zw7B|5zn|fhe5%~u+&E?BG4s=5E-JowZ7PM5hQa#Jfj%C@7nuTVnds;hvsKy$ajLW7 zdUtHm{JFabziRI8AUB?N9TwkJ;M}pFI&u>>#}CH@p_gE}c>3h&RgaU8 z{?_^$B`QP4v~L;OY*Z7=2Y5Az^*EI-$((O%u+QJgjm5o9C5Rdi8PN^OQpZK+C9OK<7VdEAVT9jPqv-HX0W zXicgip|a?`Qv5i_UU6`oZ@1dK&Q0Ko_ZT?Sb?nkzX)B`G;4>GeAHd@1mz3~C8 z`~G!9Y(qt?Kb49`Seo*(h&!3O(nW6bDLo7_^?GXR)}3`eaK6aBx^8>Sw&7>d$c%>Z z>$~BsWM!w@GbhER%fFXfI9ept!*?^Sqe`*HFGQr2+e!H9*S#bAy@!X5562aU+gt6D z8JqvihPR2%QGLbb3*Tf6fzw$3Q0;+zQWHtOb|w1d%ybeTa}1kB1`p+rUN!bH&unXz z?;THRe6}>bCpuK5YAwWQWUhZ!9gSpp!SGkha02d{16g;zphRQyx7Oxf$#0R7WNTA@ z9wP-ulyW_1A0v{#&-?TIl~<`!F>B2pS^9UJc71EATpGhRhIL*~ld1^mb(-vT zr=9btp-tp7x89hN`6(vE-pf zsQb!b8EYRk*e?*xts8r!r;ATwYd>f2Kwjkc{_FD<8|)IcLySr(oW7t#k__D&}CLq2D&GI z6}PcuUAYY_rkA{7dtYf7_@$T?HJMjG7JXK{qA0DKc#S4^tTM;v?-!~$@mtfIXBo!H z#3`B@bP@e5q8(iB^bgaegdLxa({HJJz@Jxc!;y!cUF^MTY%I69MZt?WE64k0rLwu} znM&sC$g+SvW=B$=V`^BevW>r=0ytfig8GoM?|xu$Ni$2m=Z-kaYg_sPEuaq(GQxltDp-|14{ za-axRmE|HIO$tLveUACOh`VN?)+j)gn3?G5`_X#FfI?BOgXsG?Dj-bV3A*e>PB#g^ zz`dzJU-@#Kn?ak<*6(C;_@OmLrEMVr&9*=pfP)j(-o?>sbK$w7rPbHw4v( zs>EV?X6h(*OGb2JlChaR$(;U(ahyBwu)t*K>l76J_R7tj${l7bj?c_n)+P@I$Coj8 zMYn%aNup=9o$ImHX$Dn zd8coVRU=FGrt|V1=H*vHtJ*xZ63^=l-1ov-Hr~B+(cwovK{vC6L0d16dtjkkU7sR_1!ve{GmE{H21;NmTe6*(I1 znRV<7v^>A%VOrZBld{XIlk5Jr1S43lR__+2%OMX{w_@RAl3f39T!RNsDhW6ISjsMgF zbPkUK9&G*ktiQUv5PV>-%Zvt#m!1Hg_4kr$?KRguMITfz%-E&0 z$ul8?apHrA2SKzw$@RMBk?XvT7dm5i^e{JsxvZi5(4VVS`{vD?nXGP6{^RbKQ{51` zd;OG*;Qr%!9KQ@&jvu+%3nojG!KUGyFA7T742F6XHua7D1BTuHD`jTCLPG!=;kw>y zc$DV>fGhny69Xbj~~izXL-W* z@t2xKoMCNqYiqf#GXo`Tx@HL*jhT8@1>J>&=-QDzsdcod#M&1b>GZ{=*@#n9q#R$V z&*mD+8F@OdlEbX_&3wWPvpc)0!#!SaYFMSk>@Dzdp?A1mI#oQ%iLji5>e0unJTLc_ z@GjO&8T%!1v>HdX?;@>orTI(^!mBjK41!8gp1c|{*S8tl=S{SQmNl3pyv`HoR{?wr zUFqj7#F50?*x9LCpRL$%Vl}l?wrthV*Qm#y&E%-ft+^riKQL(Y~m2cntbN8+Hee2V}>isjE`gI%;;YVG*Bj1%kVPqY$?O2{u zsxULPN~Z^ z$Q**N9lMxTLveA6lu!OLF*_YAG)0xdLj6xQ-QPr{+0Cv7guI@8P^e+YFsiGYIPk6F z?%VeA^uNi;?F|jrW@oeUcQ8OIFi<)$@Uw%f#s`{|ItQQbE3BNH`<|#sUIpS>xfL&3 zB=>5C64#dU?(a#*s`yt4$5RbBz1g&m>l*)nTMl>XVFnUetDTxZ@w=_A0Z#M{F~R0e98&CN}$!{zIfGl;K?&M`Ga^HEQw zCO%0?Nm*6h0i`vSgfBQnh_kxLYi10b6`clE^7R}9vya@$%WJCf zjUwpA{jG-FtBSae>W7Xor+$obNks_Uo%gYvx;1&fFL2oA;_qGyA8s2FZg=4Optrqm z`zSHaT*7BgwW>z$&bk2Q2})KjDu!;S|2=CxZhrX7p*5w$a~d7IT%;o5&UG6R$rx=$aKZ&)pB(bwC_XC2$UOsKKd)J`)nC znMgIs@uqfNI2^gb>}HR(vQ_>RNwU9eYA{xD@Z+D6H*bPwe5>j`{7mf%$506v{qoQK z*$>Q!i6un5he}KnB&reO6LoPFa)j&q7jDMIWvm{Kbc(L!>rfxx{#q-KolYU`Kn2TdS1K4&FJ~TX~pQm@)+iO?^1h8L^`$sQSAk}Ui(q`cJv;g4!?|=)5+U> zW{o(lsM}~{onBAwkSB>^jXat=PBp;sS)y^iW%I3@CD$D8$2RiI6%#B*XO;}UQ)h2eHQMBp3EQOp1KvQCc)n?spYimTS!34&(_^VxL2S1kc= zo$JKrn+;sPOi;r{sm|7Eqm*&k^sIe4PLcb;gG0O7`}lB@i<{FUInwb8Ys}NkQdD1o z@9IieX;77$6_bjCWOiiIEgx8!-Cl5HQQP}@D$Eq%J3aonNM-o4zvr&nGDA7M{}JD! za!Bnt;uo~h!LG^P`3e+6L`Krn(Oj7V8U;(bsl|1p2}BF} zN2}x^7vzMNc%x6~_Qq@5OK#A&G<9f8Jy5E;p`9N|+W3rGCenpN=X5GrFIi6em&frv zemoZSuFFDVYv1TN4h2sEuSsc#TeZ6RmOc2m`ih6R^Gc)~OS$tjofEDE2tTYPUS7s|wI{WDf*dcbn z0YJ=pURa>QYN$9BKqP_Tp9T{kHw+Lu|Y zHhv;aym4YriVo+yYqCtcZ+->#T*C^y;1rUsSE!4AWAwFJFAK-B4E^6EoHms6WzNcO~`6FZ^sorFjw&X^~3W#JY`)XYQtb`d5t7&q;56N4dSg2X+Uv)zzMP`{6^f(Y^wY6$~;_^LCCz>*if4 zB_&24o(94_tT9$r+?=%BKd5;OIi_Iq)yJdMr6=j{e>nUPUSKR@-{;JgDu{bNnsCu55? zdn?*$du4G*-8i9!suvCLwrP9J6}3kUJ(5LqH>@&jcde|PEzcX`EqCaFy93N0`8sKLwfVe3LBC0Z7TWombFJ6@j{P4MRH6u7aY;c#K>swC}$H{mA zV|n?4@4-<+_v$W|B%WWuG;1{S`*Yi;uZzldMcvpq)Dt3YjEp1d9G!{B@?OT$yX`dNOQvIPS?mYV)5-#3> zo(n4aIf*4q_-}QhK;>A<-#p&V!0VO&k!MIahl4zWGgluI<1Z$GY6YSaO?3Mdh8E$k|?h9uW~ezBMd z2aLDFm-J3<6gk@?Y?f5$ai8XN}ofjEe-{F6MP{S&=b0~juP$T=eKv(MtaBPc^_@==>=f-X^4m~b*v^Y1Ghxx$kV+1 z&qdKEm6w-x&oFpE=R_aJ64abF&I)7;({jLpxViE0 z0NiJ2UC8?+@ll6^0{(nzXg*vf>lL{o%a72W(NXqbPfrxi(A2s$hn#9mGGm>*c~jo=qKMP zNPh8Jf7K~%mdax>p8Zz)qIdS_tBF(Hy!BaNdhGT_UD?MMQNAuWWGi#8Hqd95 z5A^c`4dTJ$Do5UL-y*##uFJ5dT~>=E=d4C3RXXlErP7J0^OQ@ymwLU)7kURJZcHaD z_v!B4S>d#s@tEUawExFY2ag-d7vb8HZ~Pc&Qff!6ozIoA$6V?!RBkW)eC|ezf_~(l z^1VeJO&0S`J!(qwiF$>AGlxQ%U0SY-OZjX(OKWR%A%Bt31U!*Y(7#9&ln1<*=XYS& z-Qs;5=3g0i;w2+5&BOCR#Ou_`?9baiS!NjD8-43PwE)%1s=aw`vEbaNepg*Pm57Ki zUnu>9DCWGRDsp`SN5AV=d;jFu;NT_U6cp4URZ%(b{#4n>$i`-+cC_N@YZ{if^M;BR zla2V%<&x;t!Qb6Qit2g06E!nmlg~0G^cS*BH664cKyA#g+4ddm$tAW=$jtVA< z5xu1h+~{b2-=*owpdko>se^a6bMN1~^Zg$3ju@lKsLDAix`Us0JW1Th zE+7Qi+U9M!S7D@TFN$RVcHV*vg({m1eTB*D)-52enwovFKjlT8@#B+YSN88%i12{h z{D~QB4Cl{Jj#rKErm_7wFoJPS3PJ?!dH0MOpOfGNPPHpfTx9dg)XEExGE!m94|oRE zrOTU|m@LY)!G7n01-IqMh$edBtx)5iS07lqi%^akcNp#=dqgpp(|gs>&@S3glz6Ya z9r=qC|2Dak{_TMO8_HY%SGQxpw}_|_nZBk%Y!E2ozv{#|yQ_A#MEPMgK0|^toXtDm~`#Xbk7l-yrjI^F6Zy&lmE=C|Y@RbPuxxp3;J!ShG*zu*rmhbxU!9S;n;YYS`0ic>Y*^I_>Ot?B+n z7PkFb6=RiN6C`OZgd*;EwPh03ayyOS1;N^RracNIE3=jdzsa*&rm>NS?W29es)rDR ze@#LyTnCKq$1)T1U*z+k~Z8+A*JNaPS7fO^X9v}@R9T#4v|yH zh)1nd`pyh?3m-q!HL)`rEK?E0FX-PwQI)`{s#Vi=-?8HLT?6%+FlnBlnIRqPdKMGQ zOr_+uhQ=0c*Oh`4J=mZN`ECwNoE3G~Nb_hG>3az5;NiTV@w*9VwSrJ=nS6?%sLkyD zEe@b=|B<-w>*Di0eR7zaE^BUyTfIrO*Lf{5X$`izQCWzo>A8a1W9FQkCmorn8X@OG z{ffn{0c+9Vq!-rKkVhw<^Ehzs{W8(Q6c;PhgzMPy^l-Bol4gg>hcr!)J2{I=)f+O< zzVHjzn{5Bf)AVrbK3mR6Xh&EP=WIDMuD!eccWa@@p1OkY3zdn#`~WG<<{uR_ubQK( ze;Im2*n&0P<0$XkuU!F5 z4lV1AE+C6Xe*0~}wEC;E1zZ9@6Ga7UFidoGTdUox)v&2fIL7wGRb7|}IWg=!NUGj% zd;8Y+$grgO>cdeffQM`y0H<37=kigTl;Va65KDxQiKTC-bY@ASa-&3XHXm&?+yD!4 zLB?+?g+%*{NfY!#Sh2CKcC}(!{tqR)^)ILs5;&E8geM{{D2IklC+&;Y`JQIS ze>gz{2~(V1aIDT*BdHdw4zj)P>6xKoWwn46Igwq}Ef+L9nYB`5Q7h1the6{p@mD=n zP#QdMM(+#-Yk0Q|E++WVzF`m^U}?CM3aE$lNu3(a5zkA4b<_`387V23;a)&Pf>b5H zDkkEUFYZoAkT)`3c@~RnLnD>a^78O+Yd(atn-G|^s{2uFnbujZUv0(>se4hHjbwpp z#KaQ@FAjss`MJeAUS(#BV+rITXr+#B%<;Exjk~r0+8Hi(TZAIXz_6SS;$KY=AGux_ zac83wc_8MI0mBRrb&z|m+UsXD0S|+T_u|SdM{^GY{Y#iW9Lj$R1&`2$b4zx+p4j<( zmo5;v18Jn5~szC1rq;8cK8h-0g{ zT}&yw#BW;f(*V?=Zv)o653NZLCZ|588m#^*lQ`rBX!yhJ!t?l!9BLQU1N zpDj4Z%^7wBZ$g8bkCT7fO1qIl!th{S0w-WQKgKo!{RWhY4!7J_Mctp{Z&olAfkA5% zYqr`?3Q>0=7)HHG&*BSv-WzavuKiw)hQs&o&9FyL;v1-aX@95wGI01OutM^^SKDsa z;XHqZSN2&C%Ja<8{u6)|iR0TJq4yNC2w` zr?kjLwpW!|LB@tw#Pex8GzIX;k{H^WlMe9rHy?}w-*DDme#S;0XMJ1R8|u0CNM-$( zDmjhsq;Y;CD;kAvsL3CSe}+Uqa2DPUsG|5Ar@AU?yE;<%J}7@~W>#DI{qX2h@lK-9 zEo}YChex;a!oxqgyND(vb%*Goa&&sd+sgl1-xcv%`!;elAtqm!HKJ~OWF!|L-WiIO zM*QzZ0bhdL+=>G3N`LAzuD(x}X;LcY*|q`*3uBX=r@e>087Gk*fi@XWQE#K8Z$RTC zeD{+Nq{r)&Bdv(`(F)ltn(=A-7bo=|#Cq49y;zs3*7rlY}vp@GkVdv^;y-fU$HQcM) z0e>7s|D!k6WQsNtIrFv2M(^gdiLtkD`wV5V8-9?YHjRMQgGW>NicF_j$5Dpuz-!#%Jt^LFhhwGbH{2mc(<&xb8~eR6 zC0q*a2`y-tCYk!K8k(9$rS)EA*sUP}KvFNh`t8?v^WUGN&~DaY)kGT-T!j0E>p2L0 z`HXRnzwNXezJ{Ri9ITLT3MnkK?8{ctZmldpUINmxs6S7OQ(2GQ1)pip584|Y`?!wB zd7UT|bw;d@ntu2H_~u!~0^-bJn@U#Kv8z9e!)xoDo*ER|PIz?mfBICrm12rChadP- z*=FhqV246R!w=Py`0)ryll)`hwH`lT=2DDSsNHw;G#vbn?zxo zDTBqmxkYb3R7V$v=^AexeGECRK?{*6l>PbAj;#Mel|TPgRP4KjkLZ_BSfQy}^AFz~ zVz%^}H(QxI-fVX^vdXKxUlxmM+V1%drLV>LrIp_=j%PmsW-*P4Q}v$C*!N$W56)=` z;YEy^bBRt@2>2tDqLj*}2L*97j~^vG{S+RvPNI^`I*cL@vwH_whcX*&w6y;<1rB^l z&#Pg?+hYtLd$ZZyc$W?6i9+3a>g(5M*5;M_1r277)bIvpjXOaLe`?V$@%LrHO1WFU z?JxZ{JIHG%2&@IM^`c-i10HYwn4rcVA)X&_noSZTgY zL@Q}krYf%VTLAer(B{M!9JR+EjWn>ASl#54&$l-Cix1ERBk(dJAj z7QzaUMLMyZB>+Jt{L__VH|3Z;6r|-2och<(>ao8&E71^cDk>=x@mZ6GNUT7aT+Pkod zH-z3r9;NxNO1oM|BFV^(u$)|kyX#AfQ%AG{_Woptc4#p4KhDC!vrSV?LCjXIyXO-z%~YIN@8)8Y$K8UBhiqh})GuzAm*t&8Amt zJJF!>UP@|CntXXYC+>aGCS)2QfD;nG6zqQ~Acrn~xO4F29Ar>_tbv{sH$%Zb>o=GG_|pH=W8iaI!WW5C$u>{NPe^K}$5 z*C8pzw?*!3{%Ru%zqqsBcUqfl8V#OYuiuKc`6Y%sDT86 zAQr{;jLr|jUj2nCQiD~F%XKC7PfjIU(2v9*S0O^_--`?dCdKT#x=MYDMCI{&rMuLvTzG4*Pq-mES8?z z@`chT4b`<|w?%-e3%b#U{aAqWl255Ew%gYCsBD%`Oz3D*5@6H4-{W|rxZ^*yfP);O zZ+BgW82tUkXaqer8|$di8XUqtYq_z%zVwK$Ew0TM^KskaXKMlck~^(-=vPzIT2zwl zf|2`C&l+-&yVI zlJ)VqOwI48-+qlZ^4=Odx20g@Fp!FGFh^D>)v_#b>c_-GYW#9Wa?|JaA2}ow%X~J9 zoFu#$Uu`YzUn818mllD?YnEJ%HM(w+EAM1cg##I*NO;U8<;=X) zGSd|->*>^3B0rZG+o)DJfv$*JQV%=YaM0U%VUix^{*-{ieYDgn=Ok2DHX=^N~eI_?`^D}t`KadK)HDfa?$n}OQRa_Bpd z_NJo}tc;~hW-WONG}PKxCvsCbLnKzMO#2uZ;k8$EgcS9xg`2wgS)JJ-Q3(~rT_f!bc?~sr*(Q} z{^9SxzQ|OIr!{w4jC_n2xM6ALWoo^BQ2qt7L?HJm-{%^@(sGIY#Pm0J4~j1hm@mvP zQ61dZtdEj-lDc)!k?wI64>;gn5pv5sTFvi$&k&y7!NL-*oH1cXPRw=%^58O8ZqE>L z)LHKL`N3YSh-mCoMPplJ2;tau{#Vt|P}UEBnW21k_H@EaXXtA{H{*N6HjN!$?alw^YC4L zQ%1TiT#tPD^omKL!R+ts4z0T5Ei7>4Zxyg|iYac)jE=QN$Vp1J&#ipakN5;U8HcX9 zKYM59KX=e2vpIh8?6#*s0t=^-9aCQ%?OcQ$t3u)9x-sp1x5N0=BF9Y_f2j{=XdO2< zVQ~Si_`;v))_89#$6E`iHaCl#%3MzvQw`;c7r;*WPxyf12QhxsY(C7`RL@BnW;xV+H<%_M#BXg zv%^~B)@P?h|D+k%Mm8gL^^ZjqJSx>~e`VC^05z+G`lE6{{hrG z+((6Ds8on9G6`Tp9Y9w&WC z&u|Kxv2uINyl?@BJQOQ{7BaW#Vy#%MJi^1wT-EWI{z+>UI%@=7OQ>Ks;yYQ*IT2%S zZfRN2W>N+LW@BSx*KA@}dxqtooKd9={8|ZiYy&_!VwEAnuONEfK#}R|i=FH&%72Ux z#$JA(C7Wxy&pmk3&$aI?#rwR<<=Kh#V5m9^&Y{^*vtO;JjmtMn00>Kh=EXH)1cCL zIwh{Xvoq2KU;8J-%QlS>yfKS!3yDxGD|Ys~R3gn85&tBO@%pH=cD-7@f#0EJA8-&6 zTxjcjzU`|L(_Kc@lJCi9ZQWXeYBw~@T^#*2=V!)2JlNy@Mqr@!@x(qc55eSKJJ2=o9Vmw_L%4t0^x<>6_m?q>9oA8Ld zd6Pw@7aRIKpQmZK`}^=9Uqwlry*?2Um)znRb#CulVt9eD@Vf7pvoD7N{BtrhQ&6&a z7w5)XD7O9i_`ih349D{S2{yhRAmDmhg+HSj4(aQh*)@{*IsIAfZya2F)sWYX&itcb zWIX$SN=}Bxy#)8~0c;K4D)<+bR9hP~OK4Y_K1)lGd>J+*kBMCmepSki?jyNp^_Y>6 z1+L=mfRcg}EI;A({$W)HDZqNH5T6j8Rcq_S4jL+j$_by6YF{KpCMB&<4&*DNwTGF! zf&+sOVQj#z{8FN&7In33D3%i!j~`pB^mMZfYw45wi$JLMtc1Mi;hh?zeB59XVTNLb zng5>sfP$3NBJb~(pA%O~D@v4xJhvKals)QQXb&zEOiqAa`=Dd@hONtX3}2?A9=_H#1|r z<1Y&azOrV({6Oc5E#c$6+OJot!}bzgjYhQM9j zQuGa{Zr$4K3t0DeV7i96WBLwdhPZTCR|7oEXb%WpvcVWaaPz~px^bAtXil+_fTJ49 z)gFXS3i-4r1MX@4q#X3gp)Iff8#^OL7j;w$uson_}UYbX&Jv-=~ zOMr0p5}lL)WYGb*{@;JWA7%XWeO^jzm&B+W^>_{T>j~Ipkth9kr~V>PT@-l(hDWRz@|p+mpt$hAp^~0r zQDg%as@Dk~NB@z;?s0MTk=aU*XnB;D6j#-PcmYWa0BnYS~LR|IL>9a2uQzt-f0?f%6w8F`Rz7MA6yIDu@0A&9;k8H?edy%&b zS{xKAb12`lhaL&Mb{I;oO-^U7lJDv1%D!S-bDN5Vb8Ec630mP z210*Mj#9>lfCL%orK_rMeX$31pP{H%?uAa?>LJ;FdcLODXOa~*ZfXBj4|pt2|3NX5 zjMAFb6?#gCDK>Tw90ln|3)%MfK>hkC&E~c5YNmf$k`@uY;yI z60|i5<&I8HTQLT>A77qeM!3;A+@Q23R5)nqdg~@ccd=@)Wz|Tg8csnwJ7{mJn|9>7 zG>~Iy6uyX?nHA&n-ua%4B?JVW-0{vnwGbO2jW|VGuGdL*z*Gml) ztUo5Zu2EAvZ~Svdl(`{nv$haU^%oO+hyrBiR1`%DyyEpCxf z6&rO`oec<16hH>tWN1VqiAC8E>yWN$2$l&CwvoH0F|l|Oqw$5 zW>10v9t7gpaFu*m0<9dhg()e){tJ4n!(=CSYHT)8C1;PRyQ}n?Y{Ydje-PSnS3*bu z+dgzpC8I#-t;BQ5xi@V{^}Jz=-u_#~(PSoizJaL7#x6HWOMOf6E z?2+ryih}6TV*_I|GtZ?T-3YH*c2QOJLhZvJKWEyv|4s?r!2ULvTO{8?|3G~G2x)2{ z!kSX?d6zx|Xx3ovoj&lE4>Us;pX|Q$veK2_k_n*9_Dc`xa-`K$YNc7tm14AG6BH=+ zRTlR0t6gkZA1FRgUpqdk^9%6W!`{EW@v25-YpcC5=Q6>!`zLSyQwuP$>>H%M8egFz zDi4o9p*l1*RS?mhdv;vG+_%HdX;8~ZyADuh^-m_2I6K^vtyhJxY3sP6p+|BJTkK zW}D%8oO(b-2I@pqM#OiZJ0pPDe*bW7!EvibK2acz;49HHsIKy>_ShxO;=6QIXe#k< z0H$s;<{R`Qh6UexXBb8WNl8gb4hBZHC@?@T_NJn|eV|siGHZaOIPj&FkqOE>juD!M zR?F8rqQZ))zIKog7H3G?=vCxNR0KQSrzOi13@_BKe2rVHeMLfJH&*-%dNkb$BAydG zVmf;?jNN84g~hyw5OS&)Qo-7fJVdfY^T2k$t&XeDR2Z){mGgc&9gyOCCkj0L20oiw z$9C&A+ovrh3mo2A<{D*1VPR%a)FDLJ?Xc^+9IbfZMk=XLs`-7T5<|frwL+%l?+^`k zNux5UrrXU)BV{a88oGbI?8H>l-pV|Ri^%(_pSjD3^n{YA|> z4;@$DjIFJ8QSfShHW9-qyj`N~z&VD8M{Lh^mDu$S(zbj+ICl*|LC}M4ON!Mt@Wx&= zCK`HfW{m0T1;d~MP14PK3|4*>M8VyDdi8KaL%DD(;cay zPE5`3yE}rKXg_|o&M0w#@;-OBYfw1-TdhztLF`84kH%~2W$K*ovS<6-zej& zy1yZpC%&gu7v$yX-b$B4;=R#Y5gYQ!YZnNmqmQOXX$Ra^kDwh>D6Iz{FttW9R(4->DiW@%*aEkh%Iihw9zs0Vvj}zT9sI5B*c}(|0 zN+8{LHpUMq-zSLm2-krKzBg4p$`>zPjaZmlS-D9{dJNs0!+%O9AUs_#YxU+W1;Tel zXd_1(^-%J&$aB{sTg?}lp1rll6v>DthvAx|tlZ8La1!H1Ba2HpX|I}_nij2L*5rKB z*w$xWI8nn56<@3%&u>3Jh$$4`)Z$7y42nS=dDV2jd2^1C&`XN9f4F;92Yp^eMdcFX z?DBefQne`dg!S+=UdtZF(kQ)lz3evv37mx*S#DMGub({?ti!J9bR}dnrKh{@9-&S= zw$n!q54&yc%@gcC3%r+^Wi5Mte`#fP6hS$NUR|4KG4-&~tS<-26yj+o7nvvOiDr9P zG2L+$+4`##MUIX>A_wljLKQ@AZH!kE#k@ssr)}Aruwo~ceI|c|LBU?;YbwE46)CA( zqK6?(9qNm$0Kv}}tXe*1(fDz-lr2T+nohd#9eLpugydC!jEd;*{+E0V40CO3MCrbh zjrKO7x5Z>@bRTjrfBN;hzvQ3V*j^pCvyX2q`OHS{EIh=|0eA~*(Wb!ATaLnH~1 z(EVAiccA@&P@zzNV1;YAd&JW=vXiq}*}u&lc>Md?)ro%Hndv$765a5P#S1TE{;hGa ztf`o^bPGHE7O`G?>SKxv2>@J$)bIP2V+!FGRKmW>9-a%Fy5Z|(Q7l4r`BG8Ra}W@z zF*#fU%V-;Q)y_)H-a|_%W$};%zJr-lt#qdK-9^Wv4URaG<1aWuAFnd&fu}4C2ERwp z2IE4>c>Bk62#i9@qaEuJ1Iyev_tuTlBuUCcF(uzoS!wBB4d_i9ldoMU8&#!Ipr>JN zrHbC#P*R+EyE}_$g~e5&ZY@U(31Bgc`^V5HUaOBY{t{da!M0UsWop_p7l@V&o zzOZdC$!nVQ8odd>!-2)7kyyXyqV9g|Cp991Q2#mfpj6dQ7=PPzpj+-o^?A3UjK(d) zjP&HoVY_p_T79iZTyqCkg-~vhzc>B3B)jCtRE}Ejx%hAjDw(A_NNkHvY=#7A5~=OrvibaeUVJP?~(i zpx@iz1Lt{zwe#MlKkN5u%o^v*^W!0HkXv#sQucIrfm7T!CQEWrox19nY*)Q-ozG>~ zJ)v6|`0x|+`Sa_0o;tQ{i+%=&6#0U?d`>HGZ0yFr0Z9E~QbDEe4GygbU?YKf!gcb8 zQch&#^M^sOtdUX6#T?`Z!wRr({bfX&U%2O~*hno5P~~8opgp1w`JoYild@yq33FZ$ zX;6vXjq#gXc5jNm8Wdg7TiZhjvtD>jD@)@Aq!9EsU|{JN^4>WZ%{gn{x*eXy?8RCb z1;e$8Kse@JRmv^S_GFxp-Sn4*i+Frik4{Wrh2&G`E0~hsUB*gp%l~)6vS|2zM?^RhUG56pqLnmP&yf zAq9CrU;2)(lsc=ylJ8fYE!br&C6KWqhZ={;6cjh?4DHNJcMpirFc`M?9eg&Ry*q<6 z8M}6_)aoZaAHAy6WuJ_bcl#Y76Z4zxNqb{gnL8A`Z@C>S;p!ePnls$JSmVgi7@e;J zJ)gfo?!jZH zt;CZCOJ~wccK8)^R7RA|a+cEFe(Cr2$zw%MrqXTORkVNqo}IM_6F7QrF;Md!)G!l; z4l@+Im(^~K^RQl5&C%4qrq8fj9BCkdncb8mhx%CGY9Ne4Wk|PJ}+*a9bcI=g<#ZV7(<>SRp7w zAZ)qx!5&G)iv4itn;uVg_N&bQvfl)IoSHisRdUC$zK8FpPoMJh*mloS<^YvN*SjVv zBV+4itj?>@2=%u9WXEN7Bty{Y$d#UOA7t?xl)7k#1@rB->4jOa@R=w!)~IS1O0LAki)j5tP$PM3 z+(l|Dcp<^=UX``D3nDRJt=8(T4A*GliWCUDQq*b!%kWpNvBuk7O9lS0hcHxY_;Z1- z=?IDofxCb5@hvJ&IYoz7m>F2r?ju z`iBWa;FL{{6@|+s-FsEU39jB_*j3;^uKaI4M!YWCIt+Mr73E?p0JJAYM&=1IjYK%WTzNbwIrt5guXG6t-mBY7Ro$o>*au!KA>!V?-F0L;EWS6#iGb@0d@9!rI!=dl2OLy(pcjoDoctedn;KPZW8FaOWh7cnq&dv}L5MwQep4?A8 zdAJ`Ot}Zly+UapI0^Mnl&_=Xf z>F0wKV%kCj>y_*fP&tpT9y#vto?TtFAtQLa_eVL63jZWo^)tAk(M=X|`<*}f*Nkrp zvfSmtqRCL5@dU~G`m6=ht9PJX7Wf`7a+Lu;whB6b0^hrdiJnH-@rhM`h{fR!j4W!( zXa9MBpT0X(?MD7s{HFowGe&lH(cvR(%YFOM4h#>&o>kmGODp{p@YIozHmR-_K=00* zWx86cg|1A4dP)>iD9HcI!*kXN;q2Bm=K?N;PERhLha9S3JN~WYP1I0NzA$H9eA}6WSqFh{3zIk&Z3^DQXH>^`86gg!vbb9{r~lKycgIubx8GWY+#6 zhRB`1#!|u`P&5()|EjIat22i6zlKLt(Ym=#hu5GMomPKnFsC3*T*i8qVV%3B%N|yE75w9OOeNsvHdWj;CqrIRQWpF! zP@P;%ys0%WE-uG+?5Y?derINOdcVjZ9ec`FPgicIUai(Q(pZ<|vUf7zlo7-X9oxS* zvTKZl=%T{r`%W>yeYN8lJWs{DJ_cP_XoL-e!#$s&t$Eiy$Tz@ghr0!on8(Cw1(7`< z>4s2=7N!%E$Ga*0cRiO?n-BT;#gBZ!zrYL>nqx5q&D6@_azX=X=%EbFu8E1=6!uQ; z-`iQk5=t+*vi^Kf1n_paG-52V6MfLEls!ejxzhdTnFw)`sGR4H>rgSqALT0$>uv`) zeXdv?&lv{y&;lnWHa1cpJs~{-HD9_|*ILeg&sh&-a#| z-kz+9e;~!68>!wREtU5UK*bVudkd)Us=~NOf}^+rgRatT@^cvw&yM5YcQM|O@N@># z-l53NremO#VaBQYh2f0HG(zP9#6s!6NT;~&{{h6)XHClv2!`IMoc~Rlh0K0e*UZW( zs;5^~R^4TPr&w>SIHkDi_!bRh+T#CiS+(Q&dYLx-)Pz10MH2?%PU_P8xh?l8sceRC z<6|iN$1eRF>?`uuA+70n>NcvY3P4~TXPn@OqgOm3?Dw-AI6GZ|L~3(Gs~lU?`^{gx z$f6~sX#jlEx*PEz*bx9a*yc+Frj4#FVkvlj^O=wnc1_?4WcmRZxzC;fp7lMb-yxlF zvSGlW znAA#$6`r7{xlpo-FKc6G&R;4f_mfvUxVxM4fR;lheE&;Aj_;tGdHX%`LvSOF|kS@2`(Wx8F*t$*-{PGmTWA z1u1gZDL6l-jI>9bZEgINFiWM^nr=8x2UaA&Oh);Le0{Qczcb(r&l-n7kn>5vcR&Lm z+J#0ntNNLUEurm4*b5qn_6D~TwR%BtaNF8;XD~F%QJ!Luk4Q+n4q_aKmjl#ehj)`{ z_+cL4!SOU5cW#W*HCX6f$8fzUJtXMj*-( z&`6+eV92%#z;rvnFs0tretq^C@5AroVaE3MMT<3(hHD^b?gh5IT2K&F`&~s9F)=-X z?}BQ!e#tZ&2-z6$iZg0Chh0(81I+2Fj_PKdQ&EDoBcad9_WP^2B6w5$ip8%EG-oUq zDH$yW#g;XsE8SNQ*Q@Ua|Hkz=jLvc@H2pb zLnVnHGLmi9y`}~^7S}+>sI@f+v%_WMwqFo8OjM{>0-_wL$V*NPzz!XsNGwUu$S?)I zt1nFmt=@9VdY%Lffaclyku3(Ma$TUpjlz?$6+j54U3**HgwTE7-=>Pqgm|qAlo^`2 z+@I_XKR-|>*jwLsx6a3V?o^)V`9V3l6Xww^^m=#wrqv0Xc`K^y^x{yUo1&};O7=0O zd4v)FvU)zW%r>Q2Cf}sN-t0t>dj$1dMlyB39fmPA>4N%+$aay0Dq=fHl^_uBu(h$c z_D!)vS$TOnQRhiuQsb%w-Jh>9T_;Z&uc6)p1aFKu zbIgi9Zt*XZf^Q-xWM;J9kSHl-^&`*+w30dr-n>TD%Gi@nA8!?s7n||bm^bL*Z@55?WO6 z8~1+r@PP;Vb85%}EKYp+y*y?JxC$8{Z(rjpN7Z>_gDqGDXvB#!0c4Y$oD2tElu8mn z3ZtwF!b5g;1Nu<%ol#za!B<8MkW+oY4RkOTIJY~C zPV+cw@ftA!V8s2%Q{EPrS*aj8p|@OTbZpa7|@^Hl`~c6BAmxlk+_s_C%x%?~ah&yWX3 zzTYPc6M8^A-}oCAE>SnMalzYC!~-IT=g_t}9T);&S~UAfJp)}rfSvau1(gsvL%bLc zty+~JLCh8JS?h0&+OWrYKVQZU&m4NYuanWyJ#zd_=9{gI_-!l-lLyE$f>Htj?7t-H z-DyGfYl!w|-R~>6WsB$z0cRhJ65GWP+qq(^FjZC6xE3j3$6+r;FK#jz1~8$PnVJO5 z)204otqc<+z9wFkNAa-$t%(_D3-y8FNY@>;RLKVW5hE-#;35RZ{QuBDovnffBIpsV zkJjOSF*JvN_)uO|)%n@b-Xys!stlkF_1^n!AipK761*t~ytkcj1TiF5;maJps!Cww zzdt!R_=L`6wdFnF$El6xoIqs&in?cS@m>8RWLg7Wbp>)M(y;#Cf{%0HK2nZ`lkg;A zPnRAN882$I>t_up_R`tez}+1705o(vGC;dPvfQIEtf~r6YxxHU!_vyCU^)-4-)e2a z6OZtp|3EYy{N?_4xjg?v|LAUzHyLdSC=jpX>sOYJMf=kKg7X8YO{D(@eQ~g~|Bqar z|9P1Q#elAf9Zfr%^4rI;SE=7zzDhDVMHeE&!L7wk#-(@tO33;ZqbxEmGn0^phASRd zNnXe*J=yb74@^x;ddt6eDe?6=I#s}~KUKnKDYoGNDQ^DfmwTCJ|NIF_{3nXR9|7cu z_|MCK*6su1|G4vXuYUjyk9Yrh;YE~owYGo#!c&Nm1Yh--V87W9 z|Kt7#@x_=jMrbZ(;$mDFiiZ_zy9df&*@KW z@I-MgHlZrcHJU$J0!zfKMke*tV~qe8rpTde@G(KlPs!s zOL#gc&Q8#NyuQhyaLJZ_NA-{g5H#GnlN7MI(QXDgvCFUNXn~} zM2Sp7eoTL8^G_^5US5t1O;rPGSb5-JMuY@R1tqB7{)Skd+Pq%USpgE;UCJls)z%N5 zm!kCGt^8zhoVBlEmG#V*)r>{E_Jm%dPy@!A?-DI%sT7( z#Z&Bs#I&MX&V(EtT83Q0%(CQFC!g8@i6wh`xlf`T$XagDRG*62FZ|M+&&gYtmzD48 z{{FRV*FZk8{yNvFJ-hT%&!cV~F?{H6%NHrv6Dxn$>=Leq9k~dY zrJSMfcu^hTgJ@AvP_WG?>Y@Br@-`ap7aQfS9+>)oEhC)+H1Nbd=FM5o%Nb@PRkG#MlEh9j#zu+mQPPP)TN~S zR@ctHDMY)jjBze$*6JVf=$B4uxR4^1lOhKmocllKproSW2+oiJCAa-VnX&0QzH|!? z4s)2P|M6)?KaBE`bTRN!i-I>YNSW@QyosfwrlbU?X-OWl96KTVRk4?~^}kWO6pJoE z1?N?f({%>L)N{t+2yPKyh2Oh^?H{bt_d^>efI|$P*951oeRB!i`qYUlg+T1yeZ2=Q zNl!`bh_aeO?6yqw`{=7HDiuyWIa|D8Z9

6^(iGqnm6hQ`Aw#l@GDE9VEp2lyWyR8>oUS<}TyH3h zT$M&CNV_dUi=^Ts(DVYX@c6F{RJB@jxv0;tlZ>0 z61w^-^&j%_Ie+%UX&bXVth;*a#t?MtHYHi5dB5cTYYGY; z@6{R8-x~`@ZezU4UgYT5kJ`zSKGHdZJzpnHu9K55l?PjlDVsDnspNoJ@AtE;*jzB^ zwhZAJpSc#A9{QuLO)X8>YU22gK%wqVy|e0!E{EF69kzQJSr1%*;lN&^No z9sID=v3{Ydvc3X8!L>5d+-96(ZNCe2(P6U51Ilf2ZpVOG?)Cv0@!0-Pu$W$HibYCF z$_0;ZpU!n#IJ^ z*z@*Lx9*Xzh5A2{d`M9-G0trY5dqd>dk~gqit=!Pkup#u5|EObDkFd+?5|(HEJ8!b+!I#g?1XsCni)aEcq71DFlC9-VJ6pdm{5R#KMVs@j)MnKWwsh5Y`GyRNzRD3B7&w>?JIoEs)X0pJhG8k86maP`!4&BSs z$oz5&iZpSI;6rSZexy#Z&fGpxY~HJl@A+Awobg=R9ZmjZ)ci30YTKsgOzp~b+9#uR z&Q|&~mG(2)N`-0xev~nNL=^^x`RY=1LSBJzlxUb6#b>gPWxXb47BIiy-qq;wT;5V6 z5NP)N`STJunUxxq$70Xi)0~{^(|FLsLIY$fYu$+BiXYO~15HA1V2qa6KW54m&zso? zHkH+irLgR0FJGpQx^51Zz5G3L>%wc^-=x29-E`e=Yda3*GZTvOzTvWaCv<7FL{pcK zNaO-zT(J>9Qot>_o`01GhfdZib-#rwi9Y%~xbl(m(qc;}*xdAZak{2!xTD~wKJXDQ zjTRWFB=vcafa1tiXwu?kcNBScJf(=rIIjH*H<%_*v^kocSyU+5B4cz`b}9sx1SNM~ z)bts8>moS6brgx*7=EJoa%FV0VSgVu5j?W$kJ;sNq_-4xUB&h+*XcN(xmbfHHwCU) zD@S*o-?ZU*=(y)eNs|C|h;IzB9&J&fZq{EnqB-_LbTgr zLkDNM;+b8e?s7h&;YLCu*pzwKm4n3G(b{|jm+aU)t{mwvx*Q7oyh^z^%Mdm5@z=*+ zLJ}_g_o5^@6q9KG5#(8&R_FIPJ;uD+;$)kVT{u+Ns#9#b zT83q3oLtN}simp)GB|yAFRmYkQuHOc(}+}d7^chi+UgR~N_yL$r(rSmB}NTSfFWb4 z?3MVD5@yn*^EI5_m%x4N7i&Ys?suDtME;_~iDas1x|`IJ~u>sN*p)Wb`i#)kM7>SUP)V{*malYxWFX{Yzn5BdZUu3o!oW_!0N;HC~rq= z^%+bEoLiii1DlsoJqW?vVmc zRKSuAY|X;UV?Pqq=wbs{u8A>QZi~9qqSNQZ4mT1c#Qk=hGvn!$$#!3A)Qk?^v9lnC znTb1r<;h!hLZ{e7y6+}#c^OMYbZ}Aw8(?^;lpcdo>8y0-o&6qU47rSq-*OJz7{{8gB^{lmoj0GWi(8OEDtkrkM)~8Z znY9KLeh1Q`QsQb7vf|PM30b zj~Oy8{egTZ$9hn%oArHfjhJ+HV;fyL3#$Y^h4TCOW7oF4kw z({RxL+nD;{#_oN?B5I+$p?W#({g82fJygA8owxW<9>Nq@c%=w)b9{?3XFJ*3NZd;v z*lMb0E8@nwRm(mCrW}(#v#(}D(@lNch%*=@;GZ|VUcKrVM!L{TdMA(V?-#B!KUKnJ zJI*w5A^T3#as^ULJi7$`vhddYWn0 z6cQYkBJ6vx}FIGA@TkNg#zf>*(DeUSHallIlpY(?$4vO&)pLHm9-tCIT43DWNi6{()soEi0 zyjEqw5E+)5)$Qc518kQu0C6kar1Mm)j3ddd$|#?B5GpAIs;MFlut2$KFUt zsyb_QkCs#ZZ9FZmtY7E0?6~tu?mjm+pqie$-)U;QwXQty*%MVD$eqL*`m8`_P{I^A zDBW&2pg$_GZsGNY+r6+iDGu2xK|UnSH9e>o^gLQ=e5Z0EeyDPya!0*K6$rDNfeHw} z}Bk8(8gA)j}ah4Ueg|V$$5cSgG+sy@Jm!{fJJAtVVb2C#g}G_ui3G z=@wL(JzbDqJpIr`s)a#*oyRQlvc*;phf>2DNF4%p{>`WLrLm#p9Q-d<{K30ZA7Ac5 zemmKT0c;&sW1T|pIEEGme%}KVl#q|u#A4`@k%7T*k)GuqaRA_hRWR;QF-WPWecYq6 z%|~#w+Y^B$mzE!1d6{a^s>5FrEbz*{qGIaU&8%;80Vm!16P0caIz($T6Pd39J$rJcD2;a0KsuVHuYiKR3qVGlB9G`D3pc7|uuS26hS--c|v4W%S68LJ67i)=0TvhD8X?>43}e8T>M&f4?(5S=G*lkWLH_|`wA#%p5TAqd zp?VtD!sW9^&rV0WfJhKeb z7T?=T`3JytdIA)_cn4behXttKnQz?Thre;IZFEIdA1w)W$*egUEs3LD%C!E(srlbP zm%m?{n;w^pj10pJ1;8=-;gSkl5qE$f-_ANy)RFN9P0ES6Q5dfyC%9 zh?1_YB1=A|ZD(JGSf@Xi4b*q9mcZ1B?rEA6I+` z`v5KwIO-KuG#=UsnZf=Z!gs%Y?n}g8n(XtBApSoa5bH?ifwq+5Z;v7L_q$zAH0hs% zEcyhT4A-&j{E zvuJNc*qa_mb9d*gkFrZ4jz4u~o>-NYi+hdQ4W$LKMx42?tfY+yUfErAoGD1H$iy9* zBfM_Y7L<%sX;+*&)&+)P4ZD`Ckjg&fTf;fhU8#$ONcV^3^SOFwAu(c6LI$-_YUUOb#mh&_R@G=^RlTV!#J(y&^X~rOm zb*t>*@UJO8)BU)MDdnh6r60ikO2 znrrQN5-cRxnA!d8Cr`W@q_z=O&mD6h{_Z_U!M&)O{ttCrrSa^{m+XlW>}8I9 zofeMX3D9`x^FSHzi!-9`ZfFVv)sX&(|NMCG35)pkEZTZf5)$S$hq3alc)B3q|9X%X zJd;16e4H7%1}Xd}=n&w&vT2_3L)rN{M*_F5EEHyI%c)S0iQ4t#n8>P+=NA;@pL?ls^V@<$fnR>PgFeyn@Z?15lMF66x*rsiR&DeovP2G;hU(ooSM(&br^Ra znC>$Q?-&u3mk;FQvgT+inbLG^pKJqy-arC^HOcjf+U5S3DUpf&q(kik+A}`jp&sXR zHmSv{uuOCycpdUG=tHeziHP|1I@5Y*5EkE=fr0g&M&VA{)m}qJO7Gsjofx;$?RJhVo~9Z6td@PH1!h0gs5b7i zzaAY1>|V59YPBfGT~96d9tmMNq!-1_M>-x0f@A2}1!+)~^d$%_Cmnmu;x;<9XL2PG zbVeiURo2%`5R_v2YEq*mdVOu<$`>G;@593pYx92VeVKZ_ww*=H)rSm{5`Ne~i@!uF z#HQo>o#w{{`S|xED4+RvJ)o@f) z&CPGriDKt}y9#9)7#rW=iawt$bFo2>SgYDCxs3cEH!L?O5a{>4WJ!I*sMAMYAmBn? z@<^zgSyQEgU}So@9~kA}PNA^}_wQRXX>WQNobxECnFu?z+0A4;k~wydcL9>bm$K(` zLlp*=@$akQtDkcFp|`FRft+NQ+0aNZdnswCvPG{_aKs+A6$27^ApN^toWnU_lb1P_ zGH4|3Dcs{f&5j<2+<>HA#Up;TMSRAUFLj!{zO8-pJ6>GdZ&8!%!C2w?Ww1+p2j z0-g1}FD&!vFNc2KzKYcC1~xIN53RFDpcVPR8_eDP7@aRyYUcqWrH^&nbGk=QIi81j zcTBhFpFFu@62~7#H~r}D1J8xGUh1j3J#*3Ami^AL&`f0;?0w@3+^^Mc@SyoEwkpvv zM8iC4L`;MpF@Gt2_Miv;MKxG~P)U0`+hu}xACr)r zkaVX-WiQ1y@RcL14&mI_E4#{O@WU_jU$?x5R%8ReS0x<+sFla$K{2gJ7=8e~Fae}f zDjiSH@55;cmV5$X!C~B6ns+_atoNT&Nf4@O&{P5rV6On!R=`MA)gwLQ!!+DJ*VGqr zUmL6}!&RUrbV^NZzkXoR(gyp;U{VwC4(79-tLHzkU1}V$l@2dyDh7J)UoOZwN>uRWuzb|9BV*XjmRRm7Dmn0Soxp<{`;U<2A4 zBw2Uzujy7|TBNRLOWatm$!2(FNu~>#9eVa`bD;!sUI5>swjEH%(t)6RdwgHKaW)>vWyqgLLwp(MnIvf+<@G0*LcMV z3@>-(G(P|Fk)XmKhfLVi()QU+vt_Z=TP&QTu+dR~t{U+M%cQgwTWA6yiO(JtP=dv{ zpRlR>0-y=t09Oo+%B%p>b1QG3guP=8U@Hk2U?FRn55G>v#p?i&)lV~FaehVUN3RTI zzpp=#`$~kzOtJm_yV_e7%&Y)#vseYHR|{|?a9`^Y;c=9+EXC9&Yz$=QrujYYs&bre z=Awz4;#n~KS@EQImx8x&KROuxbKV%6_)bMH8n%F20Di$#1{ON8H~sn2S1+yXXrD0& z0I4B7$uSm>%~ip1r~8)sOiUTBM>Sp#A@IaMm~3?BLi}d^<;%^KxNoxn>Hw&csLioG z%R|1SobyPoF-Q1FoeZE+Fl&4iv6dd+^dY8np8KS~9PR+99E@{{5t1`3%*-91xb$Hi zm9f@41D`+hwrp{&gRa2R$roI9@7N5{Ai5LW8^A6x3w`;_W(0h$3+lGMa5%i&mtM@K zo|%;wDz7NwbOU6n<>vM$CXERkzJXc+eaJ%VT;`BfuO>}Il6a6w$0lh)QxpLBcI zXESEF6F7HWPtA;PX*Ck$go272Sf<}Wd12*eh-h=F3H?7P(V1F4P7-#|8>DtiMC|OG zo2qb+m1tJI!1D2toQcDppMR0cqJ0fWG5|v0%L58zMV^7W0G>D?EQ`D=BXPkK+a(~< z8pkws%^VT=>}$mnRi(cO_D@*D4s!d=B40i4s@3E(1{fSDzi!`-iZ%q~TwU7`3UHsD zk2v^k3`zl1qh(NM)pR(|=6w9~>!i4-sQhu%`!53;3Bld3s}?Ajf_&Kw<-2?(0ZM9b zswPsP|L#3uM5E>Sd|_X7T`c!z9b%{&4CR8*`1??9(<{wa`5txa)M~s2dhCO>X}L8I zw3sJg{dHPqi30?$JnhDE-{;StBY8c452MzFU=-Y1iWmGpY}WkfULOVMxt5o2Zz?WG zJ|bQcD~vezW27F%nTpJ=*;>K8A7lyT3SMURL$>4rB;QgwPSGvRT0QA*(=E_xg$|`V za(6PxW6(_MJE6F_3P-1 zjH$3zKHLx(VQLgBvs@DfAR^F2JSO$tr-1_q_ZSt;64J?DZ#BhwP1xdN+=XJ>#p|eY zRE_=UN_$z17Ne%>lO9XFTW3Y+x<@IUBhTBcfC zXMyuAONbZP&y+ws4UAV|V$F@}z`fcze8&xDR$nNtOm-N#`Z0LOLe%vcgMhc9(qNbc zi1%z5WY+R* zhtKUtb((-mgUi@t9hvFxQw_jV=S{`$qT~BCHAdBF#&=cS6kX6WEscN=>9b?7JDN8@ z-V4kYW^P^#48c67>*GQ>_1W2S|3WLt?kuc!C)~DF_UZZrz->3Q#OKG7HJdd0#aLfy z3mu&py_AK^nfL2ob*kk22qBYn+4>52rJ^48q$Jcif%p4UPbAfP?hZb1>AR>TWW?{_ z`mDVze_p|~c4cH~w`9$9(E}}2`)ai%g0C0(Z4n6OTX>H|wiS}d=(zos;Cd?ALuB-b=r%*FDK>9z54CIG1amG z){8IwVwF*(A4y8{&{tkg=eT?$-@JJ7B5b-@>{-Wq{G4OdE>-J2cC+moe6-BFq;~UV zAGDVFbtsuy>R`%tPr}jug}VWd`1le<+~DP1-c@e%6M%VrxFKaw@>B^RVz#(Mx1`~( z(UyfnTY`7De*j`~mPS@&1O5?AegZpFtl#6u-R`EtLech9%_@>wM>Y!szki5_kaJYJ z^q)D!SmRw@sQ1o}xkTm$bSuYNlRF{~2#u@Ii5;rjz8(RZgF=&%x3>>Tj?48|#z4gYUp%mPV)&ebS zlh1d`b$tnLb|aZizy`ow>mycgU*?y2nqBKR0_mFf!5`!O>$)9_5v~@KRDQ1&J|?d^j&1J)hjHwT|Lcm00$fv=>e%RZN6AA zp6q98mHobTOOb*!Q)^a$Jdf1WG%7uQ{0#o&&9PZr){}2v8JRNo#+)y;HY_qw{(hFA z>vi6kCXREPLq|&s6+o?jHX?y0CFQp`CFEt?S?B5Mz1#~fRvth5r=P;p1HG-42L(Hi zL3eE5$0F{W;hO3&TWU`4^fb@N@qIl#j3(sqy!RfxC!iFVjZHEI-O_M+_1+yqO2Yo1C1Sk~$Y)FwG9}_Xy(x z(g~fu zhK}{O0SAb-HVz16Q?Y*_Wlg6Y2zV~B;)^z|+0+z&dj@M?4byIxFv9|4oeZ^>@VTMt5w3eEvtKVoAkkzxQ zF$mqQ$gUffZ3@{EvL8>*mjS?6+8*0usm4N!2Y7!klnY-=DGKp>Z!t?iyKT>R?+DxY zV?8UGEuq`rj4o^W_WUyLIBia=!mE8Q#<~%4e6}7%x}?%nA98Bjc=h@|VuMM{=EaM~ z=MxvC&MBIjZR6~XrN2D?)^=KiI2=rUCst*1*Algyh3jzaF8u|n_L0O%k(BJTy&@JV zztDKt&JE9dQhp9b!}+>ZPtVc0egJw*vI|#;@XhHlV?F;t21&y+V>{AMFTEjRkdi{k z!iZ#gc#fgDa*;S%zGrqG65i<2ORMg?XxxKi+GVFtE!V)00 z0|+vL)&k0{$qq~1(0b%Ec(;m>l#~?61rf~b*MRG+w%;0?pPPE>e#0ppk6+~St4_hJ z8487Ps6mC3$0CO1>sO}eTT|uA8n5!GUx6F%w6JBY;PAT`drsHGBC0f6aZDPi(>}UC zQI+SicsvDe6aX>WwtSX+re*F5>q~A0vAjDt^_Ju&Pt$Ves-4wc{X=?j0jpnQgYs|U z{(;D@g?$Li=`}m!6kR%6?FRZ%Fyz$t`L1KP_)`8~fGch#k%gH*ADXSU?_5vKlsT}S z0N9o%speNOi_`*@3I<2r%ZS$@TNrYlMMxwqo-j$UKQPfQ8X8ACu3TrI%+zb7K? z7+`vOUBDk3l|l#HlVNr#a@Wmql<=hZ!Op{a>ah;ejBqVsZA>wH}b`~QoodGaW@UV0^ z2zRq2awkd6nV4+wX2;K-rI)aGdMdv3#17H}$t3_dpiNHd4qm{}ExWM#*2;*qyZlT= zeTXY#MV+(o=A_3HrFIXG{xr4*m+Eyt)10UxhAA^DnwFA>g38NA7>4gn{)O=MB11lk zo&@6y{==`X;E6tRp$o>1&fV{Kc5QVyOp9MAiCmA(R7V~G zuC9C94Ci2$RoUvP6jPnfKb4U&&6@(StVjVc7)7^tzwgM%^dkZpZwW%VK8P_e{TMUN zru>ffP8Vq~hhBXf;Wds@PX%4YtSaJ6<_F4lfoSs9x}I~7RRV_Xu|t~c1AUS-rKrl~ zy$!RM2DSV^elz4S#oC?OE@_JcC^=9$-#;mWVF6eh{XsE93CGFDty$SRH&0Q5yYs%W z^Ag$`5L+epch(0A5M77@o$c7K)usvubfVnvmcj}s-20PKt2kph>R+L9R8rsB>F^Yr zA=wdT+d-AV;o+K)ySM)wR;-Xub3d%sptq4zC+dz9UKW^-&0oGecc$mDNvb{aWcoHX z_Pg^!;aTF`*|%KlvM!T+_zosJv@4@}_~ypc&&NMgwqi&F!2a6wP^gbK@m?{rt&w0G>-WUjSH`*4gE}%^0F3;-)l~ zbrFe%guTuj0)lhg9J}EpZt}-^*t))x>t|g%NjsxQX@xxKj_4nlqx|l{TFt4hi9`DS zm~NJ^Ghl7FwtW7n()zjY&8SL-svNNT>2j^5Qbzyi;^W*g%=K%Cm%UfM(h(dQV~v|G zR|@kM;8L`lhXTgNS}%^m5wB$y&sE zFR3bt^Xs6^$e+*M<|~X}Ry&!V^+U@#xw}4?od)^L=QnTNN}=6caQU)63KZhbRvxZS z&Xgjx6JU;mFEXtMpsq~Khl8L9=&Rn%7Zw(-JD6hfFgtb*#KmDduIO7!o!)>3cVg~? zGJ(UN0PV_U#^=w$qgWZwM*^dkH`z1_H`j6;zfSi7nvvvceO%WaXoFWNa*F^dG`%e8 z?%D6(Itx~12`{~eK`PZcCs9|;p3qhm5zww078*pIVnqB_+dyAs*qDGUsQx$8ub z(jB}>Rr=}0Hw%~3qdi-K7pR@{mJ`_;k|`(44r^&Y2EJ>7-zGD9;76afd?AAVL>#fk zmG%Z($4v#{dkYc}gQolUyeirCyS{aGVFq?~If5M9?W@1sdk)qgptD;Joz+09Vo4+0 zs`n(gR-akhaZ9m8=PFh^AAA+qC%Ubhv{Z!yYdW3SVU8Qv@YZyAbNvXrk~*gK3UzG1 zjpDi{6n(e>&^>IYetIc@8`dD+ch&n z#Z#3+;Q1?ChQ52-)SN7PU`WnwSXX7?Z!pH@QFs;KjJF0YTH{mrjP|7XFC^0I)yQ#=ttFBMmGcFMwg?Jg06L`EZ% zW~rGja3ou<2|$=uKD`(opb*|EeGr-+>EHSoH4wd67)#6!fZpDERlsDA!T@%UC1H5l z!YhVWF>-aKe6XZ}(Uo-U zX43X2nrozsL0&!2pdurgBsL#T&f{y!K!4^8SdR4Bg^G(kP=qdV`AD0Jei4Upm01`8 zw;;&CjdSjqDpFkrEKl}e3d4?7X~ROpq<$dq_~^`FG2|eLW~Eb3Vl5V>6LHt#gv~Uu zO`Qq5JLi^qcW!l7Gj~t|rcLEek%qP)=ajc;)AU?->Yyk3^|!%bvbLwmqm^!K;6--| zKD6t*xT-oDhaKR*;+Gx=(nf%< z3fs5<^c3ZgAB96cu(Sf&WS#1@qY;X!6Y!E#0h0;`7@EMd(5TL$xrc~GL{L^aW?pv9M_B?b_*RMA#RANbKVSm zOD)w8U5OX!2ZO6+1En9{4P`mFn^fyQA8%duX|PDYnlx2?ocx?zUuqOZx4p@>VjrMULOkvcu zZ<#yhS^L=|XEvk9bg4+PW3rMI-I8Nx-bG2NM#uBjD~A+AT616Hf!Dl>W!@{7n^#7& z=XoEo0vPf2u>Is|E@HJOe}0q)DhxZ&;Xs4zuE9u&6XH(ji9~}3)k9Z*of5+q!x>M& z9vd_uxy%7Qs84j|0zBd&RiRRle&#iy*E>`B=;Unj?k#{mGPR?CYk&^dBH3c6jM!MI z(S5?9^8F`USM+#NYaD;Pux4uqik`Y!Vc)Tu1pY#^s^3RHzs(F+P?vr*R-Zk$52fo%Wa;8y{49*$UUU1y--CZ_i)tXS-hP04~ ztW)&zHkiy2jSO6V3Vj9zxr`$|20GCivFuAbV>Pu*LlN#WztE za-^t;NEg4|3lXq+ovHo8Ecc}?^TrCxx7@n|IlECenj~iI4n$JdG(EEAoYbsxybS)5nAIs&3$6y-LR}dT_Tz)t+FqZuPjHSssSHD&% zuQjG2fB(lw^81nMEql{VgwA@~h!^FNQz(vx(28hJ&2Mj@IV$%AS@o1K{Hm8&_Sx)| zDd#p8*q_S+HJ#gNkq3H8y$VU|pyJ9X^8VONH{m z?q?Gx(%@9clDmyKT{wO>9~ejga@nq3+YuZcJtn}f0ISveT+AuKpac&M1(|qH_YLnU zP1U==2KP`vJD)6U^}ULw1~@_b;2#2L1B$;o$|IYcG_jl<9UIFx-0VUz0kcCrXqJT2 zT9KyIO*Qjb3<9GQy&@JY0YWEOOk>xCy-rFT#S^53JxY=%6A5RB@Vq~&Sa0C5)FT3j zAPV7Y^qYpc@&dR)bTfn7#xPRVxLT#o1f``Gj=Ek)0gU$+3Zw)9`90#)CYqK7W_WVQ zd!&R-inJ#CXewv^u>y9WS(h1Vx3jH37!k@k6NX2=y`9ilQZmz6ERz%u8;|+=?t0gS zN(=HK)(?7UHg)-_`i;TzI6E$V_L|uQD%ki7zHfwwu#r!}wC}rDyMW$8S7mv9=$2-U zdzj`o!tOalDMDr;&v)T7nlp|!shreJ=D4?xqJwuv&N;q0AaL2G8`vja@IRNry<|d_ zWzhh1Hc=FzGsY{*YA5LXTRnQ@fJ)dNz_xszL`T)u)Rz)IG%a?=0WQOJb3a-={+&@d z{8Ito`&oetAvP@XIksX62=9*gDGkKxxLR8{(;C`Q0qbNC7-;TAqXq|m9qkKvKoL_O zV5eRV-|Sv`#*p?`spJ(IUPeYn9v(>>`AGWPQBi{t8P7TMYhvtfUlZm@6~8M1G@aSw ztTs}7RS$&pr=w+QfOEKewO~FENV>Ut6aL)X++lA__2KGmsymhzFD-0sKS3TRo?eon z-6-c|FwuCW$f`|xBlO%19pBcJ`_UQX%WyBPqHr2EsbNO2n-CT`G{ zuu}C-Pe!Z;GY<~!O1&4<0gewaWue&bLmv+g+`EkV@Q%HAFIAMONjBy_e3dQHnj@-4 z;s80qLkDNRpx|hX^KYea4(UfdoJi`MZ!!jK*$(Vuws!mL5O~&EF+g@9XmSAg`M3R= zKyfle9wW#B?07feADr0#4WjS=CDij@aLTvP00a|ov=4aJamAmw_5atM|Ltb@9}u9& zkEDM$uY&*G!xsPh|3Y@tzhEN$cdsa$Yqn!~D+6-gF_ol`cNhUZf(q9ww5MTzIPefY zzJI7R{!d(mtn)0G0bfck^v<7%a&YNwVmE}T1#C~}R~{atc1YRWHje6y`qq~OZMt+^ zZiKdMGDFWUrd2s+{<%{~W0(i|E0Y$))Yq?1R3Dm{?ov}xDV1dKa4v|tR=tKmh$itG z0kaj$qwbFPe-bZ&CyRuqb^omA_ACMjg?*%f`j3~4!-$`qy`!y-)n%z?Zf?#Ii&P}vm5CgKE9>Emb*Sy0*s&C)_1Bbu7{HIz&AFh@^>jn zlz?VGlvc#0$fWG|@ra$Rl-5wqx&O+?6#+Xv)P&e@o~FxgPT1~JA23bM*KG*r|NNka zUAuZCTfv7(9WMQ)=dDT@DP9ONx(0H4?)(55_VkN1q zt#{0#tE0Gee}8|LP;w=`!HO}VzX#+%tp}Dojz$O;d*4v`_cbLJ9%!?@HCm%qG3w-S zi;9ZMv<1NpZH5D9?;Cz6+>*eBX-Z5gj0T$bYHp&&u{)%N*K`fNoa_GL<+E+oG??Y_ z?uH?tmBq)``BO5W<#jYdjz{8t^yKtQOG~UG9l+)Z*m3I>n}}HadJtrf+B5!}`DnDq zb159W{NVy!;}@YTD=&j@Hu9L-&Xi^PWIO?49&pZecJHL8y3Mf~I4;c3%H${OkM{K~mxufs?^q0 z@hY@NwTah0b=a--F^BQmGL5LO#l|&J$5&eV5|jyj2&>A9n|y~0^|b(*%W>-0=jSpO zvftk-#gNuerrRO8C(L0mQx#*ePQ#0rE?TlF+&nk?qYMKY6%Xu9B9@)6*Tuvdxbz#d zXelU`(P*}cv63~OlvL+ra!$i z7i@p=L*>JfQ*27pRPXwjjLeg4^(w=6%5mk(9dom@8oBN%Y!PnQcNtO7Ei5eTpFYi$ z54&~CZH7zqSXVLDt?xKoI8Et>06hZ|l~=yD*r4jm%`NJGXkO_*(B><=+kFbaW|>Gow>MB=q#nR{Lvu#l{KT`aA&nTaWK;Ml>Jz znZA$e8SPElUF@D|vhL@IVbv`)!!<|;jL_@xKyw7Q5qo(aX)u<8$gAdyA$PP~o}D*~ zx~<1?829y$siy_p|0wl;HFxguOz(dj*RP~sH}%UUA?1>zR?5#M&JQP%rd&cvH4>Xk zOp2jlojQk#BqkBLtod0bHK9#2$stz9{j$lW6Ec^r#mv~{ymvb1{Qvv!_xXR{$7kQq z_j7x{U$5u8!iR4+EZ^%emmhjvdMQcP`q6lU-bVhel(wa!j+zu^5bot}cln zNonmgyX4r&f}#Y<^6I}7!-^*_{C7y8uEP~nbCa*%x^cYFI*+(QqlKQCN8~2A8yg@Q z30i<`f_CM019QU#iQKi&(kMSpA-7dhk~n28bG~rl5AiK9VKiM=emm+YA^$fC!a-$u zWI9{t+kgGSj~6X-HfcL|DcDsqnTl&w@ur<)v%8x20wkLzyd7Qh?(_RC0)qMC?WcZBoz)f_b>C_V;sCbe1nA?(PErW6NA> zdc>S?kRJUKFLcakTQ}_Nr`?Azx9mk(>szQ|^E26+ZBLit@j{+>tXFu1tg|r3X^8LY zR$rt3j=-JsqY%|a5*nww+mNks-~3S)U~FE!&l$d*zc+p{|E5J=Q)8nVVq`qe*hiM| zl-gT7IMudwz{ECg*jn_aJ!$e3ATC@Ysos-hLTJT+xX!j4N)hm1$v^G#^r{PQIj^}M zsVzRkj5~=d#Ry$$tDh2`u<^5p19RuOLbBSSB>NwbuU;O!oF&caL6gE(mGGKgqo#UH zFS4dWKBt$H{bUADP=d#ZOgV!3i!6~2wfmrr=*2fLb7o=I1#yDwRRNFml{N;i`Yt6zq4t_)Q<<7uN`!N@(C|OhN3Ao)YkBbf~vR@Wc~s+?7*{U zzxd7`a$2qa_FT5O{%H&hbw7ju<<|Wg7IEiuLNn*#{arpyUR#zHmGf;b9Vi#X^I~~d zbJVt2J&D+{FPVab>Iyp35vF)lTX^WcGD4e%YYaXvq-`a425K;TFVZ2(RA+{^fkCjG z%$WA`@OTT}Q!<;Az2;25F2GNB;wPqw5z@)wt@fb3)Z3^<BKfG5I^tPct3Th=Z-sr_MB>6*B!xTdEF#{ls0%Ue`llC$&|7!<#>E)J zai~^b?El!2#;MY-7S5_>T&!)wTSW9YnXNbRLHL&b;^c8u8rPziMPrREdk;~1XuS7hX{e1Fu? z)nWdJ#BHZ3UXSxQe$=!Q@_9=v+qd<2n1$)IZ@GL#FJ-Nsaiv@O`p&qdY*Q13qKt- zFd!L@w^!(!Tw646FDvsgOB|ItmIefTG=otn^LR>BgD;O={V`U$6T;;5XmQ>tyC~0C z)1zR%N5&dJ{L|%}Tk)?DJYuF@&uB4;W!u=c^8X$uOZf?)N2i@;=N0Favu1j8qiC1a62kP+0(TR-jebQ5IZ#_agO8L#q zV(;F~!Z-C5T_Q<`LaiS?Ta8MmrM5JixJQX6y4FR>N_^l<8%{n{uh`bv+;NnMkHj+yl77*wh9Y%Qe{d&3k*`{Hv(f8!zQd|N`on(VSuR0jsKE9u7dG~(bA zecni6IGZ&4HL=Sr;L_Q&N}QG3ZalE zob+~gFVwD?CLS&J{dAeidmFI)!60*ClqU|#Ifgj-1V8$jqdsdTeI7E_X>Gh7n?3RI z?Q<^$-e%pqcV;LXMy$AmZIH};u$PV=;ALcZAgv5Fe5*rn04o}zC(Mc8umJMnS{)N; zKx*uH-@NCg4u|&{*I;U^+-AA+F@t+VUa)mQS*Y7aOJt-ex$vuIP(aM~QArQw{ zQe|=nB2Y~8x1XERT1bXUtJid)no9iYzioj-y>Mw+G_u2jZC)<3wHNh~%_6*+y^~e< z2@h)f>wn-1jyvCOQ05Qe`>Xnj%;}x5vFL5+FYW4s#gNJ4P~0m_!Ok+-$slyHzH1a2 z7$Z=1mV5Tpfjo`KYEAj~ z^Q*vO3#;m_E!}rFlMht7Rrom>4+LbtkmE*H6+#MWV3zP;`riAbsY38KKKJ?agKewl zw4lTu7eBWOKJ>ZE#vznfP~f`5%L@W+Azsg}+$aD1v#dkABxIw=V99cjwp0|JoeYq= z%xFW_7AT7a+Ib#i5D(RK?nuqft|%l>FGE%niY8A?-18Ql_?lW&bk;~Ws1S8V2W_0~ ze)2ChsbYzgc=5k(ClRLfhOge4LmNe2oSJ8t-!0ve4%}RF0k$k+G>d8n2l5`MPBA<* z$w<4t(Ac$3@k=F{+%BYE@S|__ts4G^Z&%NH=b6Cu`x_ek0j^>7?L3$;Z8r|7 w14T>F|NoO{%2a{mF2^Zb+d>CdD(<<&Rh0{Se|kCu6{W4|{R{NT^edeEUiK)HD}H^=3kz(7q+ttuyA`G{*kqs9dRn>@bn zS5lPAb1=uHtxsli4MtT_iC1MUh#o^hZc?tZB2?9=Hu4KgeSj^mATO_=Fr~0-V9fCM zmJYGsEqOqp_xT?0)5%0MfDsErH(R%|L@lVeE z*3`E*q^iN&Z58JmMR-z zQFj90Zd=EcSzAr9oYuG^9yG4+I||E1gvYbE4je?~Pv(#2RC>Giw~%xScm5LNNbS-Y zo!BFE#waNsP6^jZGIRB9Kz$ldZ~wCXld~b_#JaKBSl?I`$C&_4Le+MiOaBwKQLI1f z;YO%=hlMRDh@72=5gs!$qoP2#ccY0MWpa5KMt8gP$?@$mOJFhr&a86 zGEEkRjkVS<5j3rL4+k~lP=u%0isHolAPjLyo^xL6kIrXjH_FMEeNV=cya)Xv*y%RL z76Pr~<=FsN$Yv|rN~NCV-?O>yGdyD8xBB3R&}r&)linT6Yk}F6lsE3#rG8x<-(@ks zbIx8P6?~IDppnt}JxO)8$}9{Dm1Z8^gp?9NI1v_eocjR|KM@4{dzB-A$zEf!m>|C|1owF-UP`9oZjB<6d)4I+eu9!E&QD6ObGo@wnUHzr^ zyClx4TATL00WLbH67wYXs(it6A$c~t+%p(aoC>LjAq?aWf6+*nBgu`n*&97Jh_ls=}OqvGO5I#45UH=|STZf762c zbn7EJ;ws+hFY24|@+q2Q?wSogg#4kyl2XE<3WO%yQS)TUa zvzpFxYtLbIs-XJ@Cp(->xTDCxIc)-Ip2KHPrtS!>v!prf;k<2Ev?l8cllVA#S)A3T zTa)!DsshuG8#8>vQd=g_^KttV-ftV7jR?w1(+gkf?9SxQmkTE&=8NHX`VkonTW9!A zc8y1inF0euv+lEd4=V|{Tx3d#TyB%adh(<17V@1p+tKvtu5~o(S!lMy!s9#sTFK2r zm_>7HQI^Vd)hq0>vT|bXV^@5<0Y$N*UCO>Bw`uD*! zr*GJ8-u+GChiDhA4?2HXwoU(BPK~*cOWCBYD?0;|1=CK~2%Y8G8Y#qp;#p|xyf%hk zZ%#GytE%=6@h&b52q_4H(pOJTrWqBxrewXQTbFh8(NIw}E>L9RI#P zgqF%VOH-wlXp;sf9C+M%JS1dgWtAH&?3N->$T9blxlFZBvVt-e)eoDnv8*neP$S1~ zYAtu0mzzpM0+jq=L6U(93<69mPT?BVpkqb2=o{W;gA_J2Ex2RmeDdvdxrfV1-Y6{WF=lqf{A z%48x(cXg=b>9(^q_qp=`PR{Z4i4wE1eCF}tdsoVhZ@Y~R0sByXKA}Q9DHG*)FGPxe zdWNiK(|*>pNxX8Z8=;#wpWo%Oc>ajtCyZSZZ#)|%ijlGW?rYUmmmecsHRLuK4_CwA zJ2NjYtsvu9^fa|onGzK?s$k@o&zhlQ^ulVn_(+tLRH8lX?Cc}ry84?~yE@m=XYL16 z<@bk{+t)L4v;raScPg{#;=+H6u_b*3=_3qvbyM+xG;u09&v(yW# zj;?A$;}|A8vEbFoI{9MpVYzsAmC*r4*CK59o$4Eooa!~jVpZ0nP>mA3vpgoh%rUP^ zt5u*#kZ^9|G+~>lBih4Yr$H9$o7hHG7BnrY;un|?CA*d?*x@s8FAsP3XjDl@#+S+{*pD3od zw$%K#(G~Mw&3J+B(hZi+l6XT(iQ7Pu8wYax2-Xq~hC(p+ONvcQwP<0#kf2ca%Nfb@ z`IiFMF_j;B-uYne_qq&Q4rr8^TqWKo)jI^aUN$Z2D;|mwACe4>j5Jv7b$o9l-#Y3n z8{dBWHoTfv-8Lc;f5LuvY$)^>arlzgT%D^qJmc-@#o{r~G+y^6G&V9Z{(MmZl-cyg zysb5E5S*_!{FWMU!kHU{l!~lH*X&|eAMFIZ6*9SP?Uvo?&%>%%l{;3gR6T!h68`wZ z`&6yxe+;adZb_V|WjI@Z_>6Aj7N&i@FJMv|AF*o6dZZ?6MUCo*9k6+{I3M}r%NHT@ zYkN9XoTrcJZXV?+<+rY42OWk<56`n=V`JxW%#=b@vwcKGDNP)wiLJ?ki21yi1+MpI z?0*0|r7H=&ZQ56#0ehrnuI_)`o)>5U3 zSPo105TBqhl6noC1A)i{5}t}er{U|)Qi!N|;00~r;Dq@d=X?4v$_9l0+MCeWshYz3 zeYYz!8ff!G-7EBhuaMOzXERkswCbZJ$pLG66+rnyz+!iNk!w8_U^$e9{&vXsZ`pi- z>{9BXvtmv%#sAeox0Ko#TaJN!FTIa=Do`b z%A6GzYX3+cP>d~hTQnxTl{^s45Xg=eU9N;kvC)~n-(Yx068Ib)NMWqbGhcK_ z1h$>x@2ibRM@G*)*RrTi*Oe>R5SQgyV#059uhtf&i0AXOvU*?zx{y-$P8`wb_bsbO z=Vpr|>Bi*SV%sNiY~C@JVQPFnj`syPqFla|c^TguPaCVPXMMs1P^1W0tp=MCqzPt7 zYr80%U0$DA=pHnoZAe5caM+ESX*S7}*S-Ev8d);~MhHZKz1RJC$4h;CqYwXBLJx zoK_UG?gn2SZZ`<0p6n{p65ZTAz%$(5mX)Q)!^6uW6RMH>>v%VEutQVr2W}QX0aUrD z-casB)zhOuu*WmyLxf#?PjL)#?0&{z#>xf60lz?h?)&`}ybQtej&>X%Dx{$NdxTxS z<_CvfI}^3Hm*Psriw%P7^_UG??{D{`&+;WC1OIh?6hAy3Euplp9$~;|?3T-R{eZeo z6S-^>-8L7T4;Yk|*Y&JYmDb;kvqY%o^$6Bv?ud>t-M+bb;$Ss`OM(3Nd(Q}39=yZ1 z(AWO+rH9qKrN8eZ{U2Lqo*`tksHn)Xv9Z`&Q_J-t9P7b#D&ny7ii_7h) zbI}!sfe}1=f4R9g`@*Sq2_ZO`%w219^dSnDPT6U5z!c1QLZy%=Lig(R>vG3cpJfsu z4mOUF-mRRRrfO5up&YGjDfJ>Xn9b~Hd2qQk6n2}#OBYbcuPnIk9uR|t-^Mdrbx!K9KF)zzjv(%)eCTHkEF6imxtvYvo@T_Xg)d|5+#92^`RX=XB8M#{*j zGm%&u39@*DQE6|ND_$$qcy*TBw{9g?^nskydHon7KV2`)Lt|Q~&RGR!Ct*A(ZelUc z$(4pO8{aQ9E-x?3=jNmgm4dVJ%_h0#<_)nMoe_RP0hw1DG^$Pu#}pJ4DcB;4|5g;K zY+XD#O)fXQvcdC)1{1Rr5i%Sa9TO8HB?rz;Ob+7{M5Q#EC`t-XBzXBUIXwIo4xUP* zlcWaJJ%~ zSt9X9yLNlDm5O1z-{Tq@8Y;&r+p6y+Z;}pZ>|@lc~ZPyKJuq! zOH0iZr1#I^CCGjH*mKq%G+Svt*gUv3y*%;B<)xiqX;X>6w|CDJh|ev0rz}2BBPgqt(!)MlL~omWit5?SsX9`-7^znZ+jiYS9R?VcFsE4kA`_7UK!ESMJS&n}^-` zffxMc$Bs8HRAU8th}oYS@b{oaaSM?zPC|<8=pWKo+gf#c442$GuahSuVDqzEyN2}8 zik(e5ui~2d1WwyGC(Qr-Iau=T^E9|VQ-sp^HaL=Cp&E}Dxh~(-E|N*9lp0kgCK4#X zoX+ybAkd4;+L|#!b|a>!)fH_nb4MnVsT|Mz8k#5{k-j+2kVZ-Pg@PhCq)y%5 zbhJvnSFtc>V+lrMi_i28AMLRxr@dLV!EpF@C{_r$FduKZQ02)9GNK`wh->evYqCj$ zkX$Ib{TG>vYFwqkN}o$!#cAKrp~1g0HM@D3!Ri}~fsk-MaV{|t4nIzy%+Kkvtc{C{ z>r-l5JsKMy2g}L5xd@x$U~_a%)P=?Sl~0yDK)JS>NRu4TQzV*b0voAxEVSWzE&MRd z$*(ljD7LjeQc#~TV>d9#*}UJ8G;-?T+1+%$hJhUKo|`9)A+WfxE~Ympb~fcij0iBZjSrMQ*M}GYl1?( z;faZ-EjC^<3B~bdpS@9x-_2jN4=3_aa|(+K1Z9mHv+NCa_b0F$igdCB8EC^#zPS95 z6*IcxDkk4kVUA_AeN#DSd#D!BVXJt<-ID9yAMC1c*h+vOQ{i|mi^fP2U%{HEnk0#1 z(9%}`2MoQiFGyb!kDu|xRhU*5s1^1H9_jCn*?eWQ&af_|LJ3yS(#yp7#KE$>*qCif z9Vc5IRY>CkeNu9E(QUGl1ur@#_Z6!zL4s(TsJn@VIfY2k3SQkmd8c_G_~+fO9< z7Tueq-ACB*M_Ss$AT8blyXt*zw;MCf`8q_6bv;>x5F*GwxM|BNI}!czoB4IHn#<`$ z`{8$W1L|n>Azc-zmYyaYlPadJg{)@{fZ>lL;3J+y4^uyLa!$_meP;W(;&uH+QbAc* z)dlFRec^C|tk17g8@{y!z9gLm^9l?CG0;@f)?cvogEr-d)c7mfado2rw8Hq>#HJPH z0iVc4)uea@mm7@{q9Wz~;Dva4%NNc^W1N^!nY)p!+6!*sRUgEuGfphsi^w`@Bev z+#26+9iKI;ZrGY9N>Uvw1T%l&SeO!#w(^q$zkU?2UJhYlp!j6K;4K^=vt@DdYuuN6 zFxQ<(TB;%Kp=>Tmpi`47Be*R#_A%9C7)GXt-Z^ms>1v?#CpWASz~G!Q)KeOKS|1v{ zs~xs#AGglMd4&?OkQ^)U0*qw9+5&y3+_K+NB4#U4zY1KDB28>9V_gUM8f#$PTb z<|Lw1HdR-gKBpQQ(8V8Gn8>AEh#RJV2G13cSgw?x`kXoDD!1c%*0T3o3PSRLK6!|7 zaVbj6K&5V&LJoVr=Y!1%M512hD#ulhQxOq~T8q8&eSytAYSs8R+oHrDUP41na*Dwd zD{j({4tShcTw8gaReKW?8@AC{BgaREcoCKI)s{!1CM{Xv%4+<_J%X>}%Jw)MlGDB; zGYaSA=MsV3)=gtCrclJJKW69XViR@AV6fBLTP$LDT~)Z_+1t#V(3i!_;@w7Ld8XFr&uOsC8B%V|H`}R0SStiCJpnHz%K1|J5{P-CICBLp@q z9(nQ;UxDY!!Vpn z{Kgc2hD@d?Jbla`!pRMIH?-vaIzlMDc;}ly3rbKNAl%Jjm@nE#?-(v}JHZc5*09g) zZs2FwyNJUR4zg0$3ZiB!;!Qe9$$@r?ViD3m9nG6){Er>!k-l?KsG%=6aSYbj8wX$*< zT~Ofj?e%1JcX2$E>4u5JLhutuSkW!RKXOT+3r_qcC;+uOVhlY29@wdL8e z>zQce8O~WxIz!-G_E#dKWI;LT;}r%AKTwVGs7Ct5?8MP?l$&5&(@*rJh-NMi^r}GS z1E*;EA#$l2T>4ZFb2VRRV(65=ep-2ebfH&PUasG#{w2gqSn6|`-uyD}N7dX8SC zVSRFZY-v-nDEo$kBA=qSx3}7S@~ZS8BE3^e^eQq2nB$yO2M3=gUb;89U|X!^Tog_g zFUq_pkD)VEG^z&T(#_3HM-ZOOBSuE>&JX?YK!`JG5!dNzy~Q?O(9Piy_)nAH z+FotR8V zC^*QXfj!};RkMtW9Mq@*rATA`Z9+3#E=kzrz;fKnh1x}>G0O(yX#({ufYh?Q9JWuC z45;FO3$d_7d1?f__TL%LM}7Q|#pl~ErTrSILG+z*h~|R(;le>QO*|_zE9?9@qR68; zwEQ>7VGbR8;plGVD2y9Y-t*~@_naR8b{00W+*QM zdgI+m>mucJ6{jH6!`$;p(%7p7{`lJ3JttaHP zV{=Xlwu&4tOqiviVltoTYMmQfoiNp0B**e*GM@R#mH1-;@3X<$(QEzV<71U7gDUqy z9>SSwEhHq^4-^gV-(&r)<9$78B{g#q)~6wXDwmsCS*CRprZZ;4W#g2&x#86#j0|QH_eeW0nBtU1$?0W0dNvPTQKPbgv=6}N1mRzH|Bx> zDq)(a25=JuL@y~D&lQYWW${%SBsUkvKE0=xJ*I_1o2(B2w4$^8fL61dZ-l*jp!?Ff zb0PVW4<|e8TSiNv7iqwqA|YMK19U^wMa@)7vy$C;aQlwtRg;LwPaP`wb~$bk-b6%9 zjD5XhfX2r92I)>J(FtYBPS=DbjFi;T3T8-M6`GIcB5n~Xu6^qa)s}3e6Cpm$J9lF3(ghS-}5a}>(Koi!1gVUeDH~rX~P=7#^h+Y>-H7+b{ z+I=fFv|0<^o8*kK(0D+qtSF$KMA`5I1swb3Ypmaai0$PJdX--Hfi;Dh!BCo42G^ND zh_Tte(jZ}ZdF3#$P#u~R+tjQU^Hch=5TuKsGZqc1vE?9jFb7+sMl2R7+`;#Uv95nBqVjTY( z+?{17#X+MD|cBR zKWwS$-{r73;xYKPs3q_uD>JhR9i6(8)v0&%WPhEysH%$PwL8B0fK%`G`F8o{#zvN( zXg8J}$ngrq!?V~mv~-?yeX6KH-q~1xzfk5_=+&q`+JO!*Ag!%!Z;?C(qdPaDjTUjJ z%=w(ku?e>OL(!DaKT#BZ!!NksC1#li6oW(_YHBK4_<_%1{4O4B+k zIj)6xuEQ?u(hmFb+ToiKzAGgxaG)MCzRCobE2YR|@i3_x^Hf{J#~PkJ}x; zEJ^gG6GXP3H1__sQY4p#V%xCc(^Dqm1K&PCV851BDgCDAfpB|fW)`DnOSUaOHX%@PxV)d9o(_*-(;K*l zo~jl<^z;;1P>D|{rHjJCCN&4(0<7I^{c0(AJ10eZ&9+9mQ{L29!^Kz8u{b)dxAR{jgIsc@Dm`+71 ziW}B%5K_{=&B)<1Hy;2BUD5|&HYGVdI=2=qq8BYVwCc6orXy@Uloar%sBRIlu^HLf zy+4bGjxxUT0GOmc+T`3E<#q}`K={SBKG*JTs*i2mwNwMyPW6qkQL<+mC3Q%&v&|^! zp$%%>*mL)GGvaEmtW+>D`(nU%<+7s@Iy`9B<;I&KF=})D^ULSzN9><~0tev9_`FLF zp8_L--#*CQuLS6qz%sqmr&H5H(+}QrK50#G1v!4T*NywyXz2_@~ohbzYN3rc|iO=uCtVR zMSnX!d_e8=h2NLP`bw&7Nh_nPc@*haufQA?h6dUhgYsY9U`u1(2^=$cdnzp7ki;UQ zqWT91^$*UkN&#>afDcY>Y}wb{dwZjyP~x}PItDq#X$5M>>Q@Q=Km8R$V_2Dn=xR|_ zSV);AH4jE7{~*#H0f7&(s;`GGg+tnvByNoRO$5Eup#77kTyF?o(HoB}^|0VTXtwNj zfysf4TXU()-hKg`V5ds7O{K3h#c#K;KE$@d{~?AF0%e-rah{W<)8)lSfB}||lm3$X zCAZ~Edv|ZIQW@1ru~#@68sn%{H1p7;&hMo_;(jD0`@kWDm*VuuxjUM;iTbl(r;STd zpv09QmoQi$DD8E9nOC@f4crMa6Lg*S@Z}%3K|*5le3TMPKIru&&2vYAn&-sb^%|zx z=EU4%D3RJ(%2>1jqeFhk2M7Qei(*oeJPvTHp#%#@qGA<=+0Fe}}S zKyUo)yNN|!&CRLpU1`n7>uVd^28O4*(^Y!R^*OOW&QviK^0hg&LocYFIb&!#93A%; zbbC?agxPT0A4P-QxUu!-r8aK&*f|HUb2^-Ra z^(Ks%%ie7AtETj|sRNVu%XKE0#rVm@e5a!etT3ouaFR1Ju;eU~yFjgJ6S6exWSa=5 zRt08lO}3lQn6cOMf>};!L&%lteZ1oHdg221Fk`*W?uW*#*Un zIA#^B#5fwUp8gowEmWURZd}nR+&S;?sS6=$s@Xr1Q4%hW7zv|iFvv%(>} ziw!|tU41MpBgVj(ON0BwU2-14aL@N=dO8m>lY=LKnOhFb*f(oWl~rC*uCKe>`D(@1 zNJXfAB;>Q97rK^=F+mq$!2oPGvU|#5i}KVJJ{hQGJcWr%Ps4q`(>` z``3jx{dVyCBc~hB@49*D5$~aBLN584&3Cd<||vUs82!m`#iXB2!x{4Xsa!da!nGB z2g6>X{;)z-vt^DFEEfh6G|Cg-eLnQU2b)G)zZ=0zBAYO=&-y@ zD3ASRw8+Pq3M;+R_T2Dfk~D1rev)Y$uQxlppI3AQMkwx5bU$*xuN_m^-8K?z{|M){ zfvNXC0o2t6*NY50bYG_dDZHILtaJn8ynjGI(7qi|w$7 zt5CYuRTu3TL_{U6Iu2Ind7;PjY8Bt+rlm2tUs23C(E!US0yKolD4Z4TW} zil0_V0Jk0w?t=j(k|v&Q(xU8dh(U5AC7>Zed9qYrW@~YL&eq*kaO3hlC&gpMC)|8} z;|TfT_xL<9WK_=uQqqL}kCremW?kz!HkYb3a^L4ADkXK8iZ47w=^W96S1{VzicH6?0v-OLW z#dG4dUcl22Pg-97&W=Uyh-4hUFUKGa7%%S$wYc@Fa>NtrNIStvCKE4NSF75dSq*TRb#JBvY^r$>g0tp7_j^HMr zI-l>RX5~qs-y|a^!?uz`W3^2od6~)!g?50bFP+@<@-3k>4@0&2c#7q1yu`BKGn_0R zx<`v0ssHnr*u?iv;KH(b zatwZ#uJ7V@H*}Y@RTw!INNQ|%L$4p0mv=mOe>+~5+q-uSW3P2Q+hIG~9VVk^x9!im z9Wxp4aIG|EdrmwB*=P*{cVgz~YViv&urg=RNQz12?--1xDtK}E(tga&0*){HPgp;#r3GO_4@%Z2{a2O(kcwpzQW@aw`UmUujT0uD=WJ7<6s(400}OQ8j( zm?6*x4*Pa6L!o+uo^aD`rvx++V7lGnVDigPz%+E)khEJj?eeQuZV zh=kb^v!JPqjG6I0Rc2?pLn`BnT*J5W#HVBu0_ug5Cr(%q^L5u>o%R4hC@ti8%TUQk zl<^SIOqueOykLgm^R(Oku=mawNg@8CGKz90i*@f^6*5ph>nwK^z!L)|%@%9U4BGs+ zPbm*LKWG$}g@=ppQH871G`BWsj=S1!M&&^e^u-e{pt zxiOqGnj!waaOUp1K-Q^FrD7=zJZTW{1hK~pxuQT2*WV0Xzb&kTfHw#B;<2um#BReK zu3;HX4|k&NmO}3c^QTnHgVg7V)#@#wb|Q~rcD$}^mMq?+%E()RGVcfl+keHJKxoen zdOHtDUA3loR2g4;ody;hkvRVjx-zuCn+RT(p}>HR6BflRd2X8<|HV;{(l+N2FR~xL z(rTGK#;imQZm+o=O?aG!z9jLZXB{3txZme8RqZxeI9>h=DnyREo2ZR!>6wNy9pl%SP*AlE&S|myL&mdPxr(zZ*qNAJmu~xJ4>D6>`{pFhvgI$m>PB=ED8J%LN;3e<3VQh5B1JT!xwXh0b+80@nelS0aGSn4&iYG-?6+q)hbF^-JIBreCN$i$HYGT$fa zjaD^!IQ1k5Kqb=7J;uRMTk6`*kGMaRSs%5=#qT9UMH*^ z`9SL95$4l47PoJkRY3=!L+7gxKaIo)rE}ed*WKg9?_7r$#|9p48Ysz-2irxNEh5Cl zdvyULTM__H69OII-tif5!h>sdb90nQ8Z1y~mXs&&ct2bYn^#p;egm!rszcnOd7_yn z@E|SC79V3H9Ij@~*tXOv{$|AdIFG8Q?TJk)apLyo3ZPX_n^8VUjl)(|l$FhTyw-if zdzI>Ja6e$u3m_qw4bXB!LD!faTJO$>6-FEUO^xE1gL7k{Zd+N=6-HOv2@?=~9c7c( zHnvRHcIIs^BGCQ9J(Evw@B@4o=v<)4BL@pYPgbC1LCsDgBXVbUSt0ss(Iuoo(mZ1y|7yVIBOmY3a>hDt??>AL=utEzl$iZmd&2*bxC zZh~oXLFL>6!rtXbNA>4dy5xa70$!z646cN)_Sa9+WE=6q?Zt1yyH0-H)22;{$jZ9O zKrv23D~P#M7$LM{QL}}@smsHs5NcZxc z_zD|eD36WA!_NF@8Tf(R@TcC>JUI0=rO3DWIRSrzP2!f6c)P(Ed&0^s<$` zo2p0m(>ABwO`pQ3$y@xrX1Pw1 z=?;K=*%|8l<>6{*LXn&!EJ*&DxOfdCRU!}Q7t_!;t(eCG4yMY4@UdS@5B`RGG)JD2 zntr6f<8)cs5rA%<(t1B!(L+v?Lt$~#Ee4p-JM%L;ppxGRN^-U_(@^Z;-OW4_8Ng&< z9?5os>6bw7QIQcBNSc3<_1Q1|i@?>qT+_$#{27e@!i5ZQ&$i|~ zgRtW4HT_yWeR$t}G6t>R5JqLLEh@=b1{Cav?{|SZvDb%j4JvH_% z4{q7|?zlf`X=mVm8fV~sKP{u_o9p3Yn-7I8NtM@hZCzk$knu_dQ?m<_QrGiOhTr^f zmK+LM*wFJC;b{`@?ds!j+i(J4M-|rm{FHY*?%fksbqt?@Y+@p$S5+5(ag1t7ET*wF zno8;F!tbA1dvOu}BLhq5hH`7DW8>&#JKK5YQQ*bWOYV~+AYmuN9Og?Ub>ex*KW1f! zC7sCK8K9s3B3=iGqmNoLqW@c-sH`lgJ^XT&q1=kdiH8S3H6@QpDS7FAt`368x#_(b z!ldibWt2Ii8)eZ7TXPQOV}Qs8Sx+;SkQ_|e@<-QPc@>VnaL*lcMrsInGnWBEqsGn| zk$Q3-MZ;qRTE8b$IP5inn8B}^JYbAKAjHbLXmSEGgaYlHN4Nz#Fo1c>Yu(B{ugc?g zOa)?yCNoihQxkIS;k_k{iB7J)UUEE-aZHuz%0#fud8Ei(5hkmbs3@o!0mLXdm-E62 z#J!SMp^~bKNh@oyC^s`^5DoQ{nvp_o*VLH20s)n(>Aa&SSsmP)sF$y4*;nva1FDm_ zaq8EMn~prH73jWUmT=P*BXL`;VpKgQa2U{CGU5=wnn*IPK3b97F@|fy`y5-65PbNjoK#Zb-(Ap&D@zQdbZaXg?|p`v2$#{;(|Y`9=-q1EP#&#`z?)R zE2VNuG3q!#1CHQsOvUZCPYe)oz9T0WCdn{YQr1~IX;+>Fs_0w+yJI>!&5aP!$S#?3 zF5dwFvh@BTl+?H;yLu~M^pQr=cBW&hCc_ia=22$%37=`MW<4rAo=;O{dv;|ci;&ID z8#WGrQ^Z{7#R~lzb!~t{MN0O4tqHpt9v-eJWzew~$z27U2#9j7V==YK*35#)-asu* z#AbIHqHr&k)(i{+t^^|P7=Xc)2NQ5`0_hz--ej@k`c>m^-aD3lrTS%K6&zG*vS*fM z!Ox}@c~Rrn;>u~ZXz#RffWNKe#EFk)hx3I8j5wG+2PS(;bNjNrz@TRuGUpYsk(U0% zKHZV7U)?Z#F4uGw=(_=6`^mlF@*Km!mlVy0M;g9mlRgwaV3Fgp|A8_!A zex{}IkNQ*^tTC}BGZfvsw}s+`+uWbJPrs}((_p1%KZ9~*PnFXEMe93P{>M?X6*>0j z$23kr-|at$yc7w~%i$d!>GSitpLcd@88IDGXscs3P*l5P3C3Tro-K;N;{Cj0OuIFZ z#1S~(h`y_+uViFwDmCh6_Y&n=tV75N|DJsWKsyyBccYsG08ANofMEOVLQqd{;K#75 z!MWDn+4>k@gyo-V3K_EC&cVZvm@LTRR(DASk% zrDy7k?}Q+Qvg!}oV*5YKc(&y3gQ<=o(KoKcV)wC(SKkpQT{&z!@0max9~gpn(sf=@ z(x-|Pt&m>;wbPB<|D5wa@Cf<%;_3FLO;oFwhG?vGftqISshyzmbs5DLB?_9ejCM?X@G;fTU{>h0)w<8MxR zJ0Q)|8e=Wm8-9z1+^Ws_kw-I>?nMc16oum8>Jj%;b;x3`5?|^gr^@s=Yk7&*5gvQF zF@^j(*LL9q05KD5M<5zbXHHN<=yJL?W5YzR6>B{?e@sx*4dyTTiV!FAzN6S}4wbVv z0pxpRzkKKR#eyQBzb_qXzp@t{&ugYHsAjUi3~LoWbY#rIiBi zIyP?(=r&ap{P$h2_m4S#_bso2mw?t=fyppJO(0A#g2|S7Je(#M=DGcZggg3fbtEXr zo%opp;Y_1WIcgSuMj|y|HzXZRE}S^@8e8r!7(C0%G%} zk>!!h$;VA5z;09U zJ`lEOM)(5rT-_O<$$8>5ler*`1*=J%YnU`|9c3(&2t1`$j+ejr`6`s=gDl@rLdlO` zSX}Bf#^T`i5YeB&uW(ZaxU%d2!s4LMvU7lUf4}}uu-yNEu3Vt!1$}*+gV0|cUl55mHv(9kltN$A1yequdpc;k7pQ)n-Z!cBMUmo zeAiI4&Eqq7l@|ui6xZ9H9B)UUP>(naHN({FZNBvusxlXA4rVHk3PG5OhKiK}^dwJD zZ31YX{=HMAvaMd?megW%nM{9hsThVYKqi9u`PE$1K7qB`e4eN1s}mAJNY{b$70399 z-BA)!(!JL9e0hLc*+S+oZ$bVFRBqUxOR{)D4AV?A`+_b1U zKUauDUA3wm&r@jJ-dC2987kJ47HupHIs-!J+&pcaI%oANR1!$5@{X4)qUh-8;^NkM zCc_P)Ni)otgj3t!%&RS0;Q299vP$r>N7sndCs|}25oE#h)tgXlv?}w-ovZQk(QK*6 zg5ADu0sP6r-%Zc&*HY&>fQ-?Y?nyi3XA~uI{x90zIxOn1ZTD6YP!v!Jr4^7AknWI_ zZjcs`j**sD5D*ZM9J+_@lnqhj6_#DqkU%xJOEN(T|?-aAO z1yT0?--hy^r@@-nuxD_%Iq;g3)2s1Em#Ws$(Gf8HWfs}A*>_4v)ZIYN5;oZSbbc@H z4uozF;E4ssb@%j0i;GWG-TXTA64{w{kA$}EAPRZIal)~Cq@=f}U0>Ej=sl{`uFK5K z)HOF3h8(R;)d277W`+OXRscA>MuF}pI67qiAp}Q=+SAKRO;Av8LG%atFG3O$cGj1B z|E?L3C+`3#&4O=2Un%W#!RhMW&dysUBkaaM9Y=Grw^v0{@?d{x7Fnphq-aaZ+C)Lq`vF zK0M6~$)+uvt$3trqLr)8=Dat*)J9DAFG221%SdIrv6EAD&`DF*^S)b^OwoK71`#4C zJ}CZ_;pY=HNwn@u+2K3Xv3(lqbF5t6g z^;IWG5^~u7#0BnD2}wyKGhu~}7UxH}d9*LzzSnfWWcjBdccO-?jNFwn8qeEKCX zC%5}>&HK;;h(|c>Kjjw|4uFsRAAjqeUr%{ZQh%VK>FMjqf=)L6iR?cg^Dm+Y9McPH zj!@!%E#G$R|0G4eOpLF6{C8Km;j(6ZVAjC`Z5;*d9}x2Hxz8`Y?yQ>KVE*|S>-#{H zTfnLcGtT{RGy%;$H!3&dWuFUw-Zo~D&3>hWd0@t0pKPK_kyFfvuld3+vCmIyK6M4n z;M799@*MJ~elyTKVf?!$L8;!eE&qO9Kw?pD=~??bEml@UhSc3= zt^D+dzSO`lMl{Z1I$nUQoR(@x$m7Pzk(#jw>cOD7m|1QEg&b|dlR=ePsqW)g***D& ztQ+b5V^_z+5;M=rArFrxvXq5Qb2?`S3j<42j=!zS(%2efo0Vf}bv7%*zfUjmG3cbu z@;Yo^4A`vL2OJ%h%y^5X!;IivlRVe^c2yw0)fJMUxy75+cgX4yG@vWD8XOhglt=ZNm6w&*6RqUs5Gb|2 z2Za!jz;f~148*OQsg|!;x1^^bvMeFy8Gl7dcLc^j!KvPz$~6&NNLgeIKKZ-0c(BC` z6k?r*lkA#ie?9PfMYx$m5wIrNLtim1E+L-YJ-^*Vd1&3*{LYX9B{!N2Sjj?Ty4(-g zNXeM1Kz0{ac(pnO`Xlgph)_zWjvYvr5S*{66<2bd@lGR5&yUQV`!=X^4L&9uTWfv_ z{`rh%O~tX&?@De}O8iRphAHh*nSfwzpg1JiqbP*biy9gEG5woAGJrdBR;Ow& zX8Fr^&WgO67g^$~Ij$w8yMFljveQVB=*>SHb8wBXuC&?+7I=vm8hFMoBayVSh&YUi z4+5E2%!>_wf;L>%K>9b4FMwt(hWvFp+(w&Nhnyy3HskxfSO09UrAE-~5t+wQ)LRI} z##v;~kMAMowOUU0$-eO9#*DYM9QocB>b%dh3X&|P9ji7mNyQqwgGx}z)UL8}U*H#u zM&vuL%!Q^dmJA40s@fH#wf5e=*Tpy(KxHRiEje$eIF|7hWTPk3jOo^UjWa;?o&S)&Ko~gumx#GKWJvQ zyI`jh{QW%%aNqy^>CV4H&$(*WC#9qt8Gp)3&gu+>8r_k2vbX|-j4f@#uPI;u153Lz z(zix)A#{(~~B{{UH#OkeS@dg0FOMz&miQgRmQlLs%p2ECiX!@*Jh@4xC7 z5q0Eo)O0}N!*w{WUV| zro5Mu8h1bCzyUJyv^4JvsoVFuUJUfY@9ZwDkJ^0ApJDEhSo^6?ZZu+I7f#jV4g-yRMBg9+^J zrYRc&pbA7JasJGA>|DLgPfm_U=RNyA5|4V-3nD9Fz0Mdf-1huv|19RZulaWOeL|G! za=l$0qrH`IQ(Iuu{J{k4Z7$?Q7Crbf7(g*Yo}S8>kR%f7WTC+pUH1tIsg9@iN5^xf zdruYX?_jqj-B4KLIdY25iS@YU=DZmNDZ)Ko5^g*D@y(gBXpQon0Wx-Whsg|GY~G(S zJcC^mP*=15A2UEdM5P1Vfyq#yDMLoC6Myq2^hd_(%60xG%>;HW&if=J4FJfII(pae z3aG58C@IUGu5>hRrlgNrMZJ||gSvCx5Nt@if;5dDBhr(TJr0ex%yY*g#5<@fxyAwe zh|lrZV?CQ=9K^@p_n(*H9SH>O{>ae3aCpx^|Kx#*8V%SWMXY`C7j4b0uhUZ__z`YG ztp&93!66{2iy=mJxS2{bVE(fW+)Q=mboL1I3{&o+Os&x1t9Man-o{-D#k%}X*&eQ= zW9m~Jb!|u8dHm4cwcGdD6@uR!zy)t{|?L##<~euCx1Z|}|< zjcX%A!-$nBRP#zk>(zQ7C$EUFV^1%~#NKm#BHExB|)pDFOX6AmQ z!JgFQ;rOL8gEq=gp1fU$hj>*vJ!H(_ zi`rI&rsauDF7R9I3_@S)1V$fLMMs?J0AY3 zPQ`P!OQ!+A!%azjp(e?ZNl^LUlu^!qSK~8Ja>cX#rA10QLn9p@ zMhA7j8;1J*~h-`d31=Gki#-_#c#kfEMj z>M!B42VHohINg|^CS(@g1=9W?Vt)NSqj(@_K-T$Ti^PZFs=I2I^0Mvn<ZgEeFu@>Ewt%k0*Tcz9Q_gVLWJ~&Vhr5`CXVQ{8X#Au#s$>E z2sd#_6BAP^T3W_=_a)jv{*#NN)z53=bV#mKPQ63v(l6NDTAxWU4%5ZZVf$ui@M>)_ zHl52w0uGj-|7FcJroK?k)`iTfvB~k)fZNnQCmALECxPKz_lyH{0nz@EWz>fp6hDdf z+Ah^5%68PsoOneZgQW}TWzjEirl^l6_mDK{eb6c@93>?DR3dN-y$){LGmOi=y(g!M zvP^{;6?g$ys+rCD+FFqp%zs&d>gAf8n3&5?QQ%X!m%#Vg?%?;y?!cbn%CpcA)PPgE z{iDHsvFRc;Anc|gc%HABF1V!*3^Y$4+;3`?0cmSmL5f(p%$V93^U6(rtPFf`&>syC z4+n3(M0~-?eq+(8sp+8i3gEn`KdAlwvXsPo^GBJtt9bD)Kb75boCKJ@w75$+oQ=D* zMw+sp_sP-y1e8O|+?-Sv%kdi($RwxkGFkd~aOJ-J_VaVSzB=t}8V^Ja{* zg`{ivn%S{;pB+M$#XY7dy<=%XeVS;gYB9=aGZ*cz%1aIez(*iTJ|X0(vFwUxRK(qy zbPu_UM=o4{mt;rmV*D}QsLvI{oOfNbjh&cJr@5=Bp`q5wxQsH<4)1QI%N7dkPPj#G zRO5#^#F#%&+RShtOPuKIk;;f#{&r4-rGsN%tB(4bA^yEmVn$-UH!?VQ5lND9>X2z~ z1s{M~#TLz(xjf0M@rgAXnXI;N0npeYEqRH?P+GK39n?B%PD6CNBYO*V1>K=>>BGqy z8&s#jA3qKIsG>OS1cT^_sNJdUoX0}-+Hpo=&d$!tEoW#eL*Bo06oMW4csl{$?Yk>y z#SBKrOJh@99IQY2(ckm4AqaCxZRpJgU78-1lQB3SU7`3|TqVZi^%cs+D6H9>Wjm~C z_R@QHLeTDnZnk0;_9P>ai32NtTH`3Q&WpXVaiHLw*LbFsZz@|ybzVoqm2}mpe3D0` zQDiVf$zbxLJfGNQZ~tg66iVLZVcxJt4OH|c`RHEA3vE9?l$%TpZLnYIAS>C~zF;-) z-@fwk`5E2+`yFreoT7SCdNwlvfVuK&9fw7TGcV&*+0iO~=|u5D2C%nVQtYS(R$Ble zvsLS`y8{T7!rNBr&>kdr0^5p0f%RH6LCvJ|u5mW5G|88*Abu!e;gXFAay1 z7Ex|#Oqu-IrWY+hM)EuD=X|O8?RA@h0Vp207zOX0Dr6oZlgd|~ATR6l9J*D0UQ44O zLaF<56n))eYGejW&1nDOUL>hU0%;;m;K7{Tr49SDHs4i zUQ~LYGZi-$lcr2fv!e0woy%`ph!+IXWUq78mBqvpLR$k&Kg@X1u|vi)R8M;6ad6@n z;V}IwpqT%lT9kU~Ns1&vPkyyIas9l7Dol}(-;SP#Gz53@U}w1zxx)QTqzGM9<3wr1 zZQI8bQxNsPUV%p;T?zFqfD6rHqAl-Z39o5SA1hQO3XJGN6=I2&acO+A7MmIGi z2LE&C%hx27}#U^a^8GvN6X8v;hH`+YHR<+iP(#V_T;&6ahxrC@;9k@6xO zG7H^WCR?)0Djg?{%&J{=I5_<!I0SfzVc`?V$#+1X!hIPtn>EjTQ;F;7MTVxsG|-X0%2Ir+Vr93ThR757!ZR$Y6A zjRp(Vdw6@vXDn7{h3q%3$XH8cc~|0Fw1n;HlS9PW?F)*JoM&Y;($lgen&O#VLu@Gx z&`YhF&Vwwg3=IuoMxS2b;TPN>{i#{;*8m&tuP}`Z=J+W@#-MnIpi32>)#Bc1(M!%E z4}a$~o&{?T{}ov#Mm7MIO0>6Ydv#U0xOeuhA?eU&X7?L!;HLV?NFD0hOb#;zIs?_m zXgfmEXTJLf$UyMudf9xFH4&%B!2i1y%P1dxbI@`0>vm058E{rpeR23ySBEB_nCvRLl>jP*u*rVREZP9xU+%`4+{@_(=8?GnvJkcae@1E zg?kG6Sx?iMWnlm4_q=^AUv*klA96fs!RxS`h>}$R6}*YVmY9(@u`Gs9G=wTzf1Avk zZHl!t_vsYPxgSu_MKgb+<|+NOAG>58<3GaVWNTSt+knkB@XL*w6UWQR&0XXs*J^fk z4B#L#cNEAd*yawsHf}66yRoPH45lJ@nJF-C%mPk(sl#l$)ek9=?z}WpFEut*9cvv< zd-i8#?uO0-VVG+rV_|&Co{bB0>)WuH^kTrs|cpudm!P5?>~S zhH9*LS#ykQ^u@6)@A~?0iZjyQL{6uPG-(T^eHUvjK!1E_XKlStC96BDzq-EWG<9_O zky!(k5)zc}>>P~<`i(|c?_PraH!1)1P-o*oAOq+R!_l&={a(V6RG-T${HP?Q19$Iz@?utG|w2xHj{M9`;gxa z_&cDui@q_GxbH<9&qE9h0esyf!O*su!kz8}$uwir3`Tw$0AK-jJbBM5WfusK)8@^x z#p2;ZBpB@uknoQT=9%j^T;jWxZh=DdR}xLHdx4ud=I5w7wU1>~=D3r0r0mN!wFo=} z(VgP^TV|!DrU!q;o+^F&hRg6;k;CO`qWbclv(0PyyEZum=q9Tf5z*8-EjZ+7+snva0Yn(K^D*cE#M4pFVh$QMgk4vy;Mah$fTAF_hO` z_LH-bw0i1A$Oy1;=jYL5<4igRbAGaDFU7GJ;|WnE3QBVM^nsuTt30#~@{pTtW*jb8 zhzPEiHSZjC!(DDQS5p%Yj@R+|4hp5x_=C{Sr}9?!zJ1AdK0>ydMH4Tk1~8Yojei{B;YdYQ*#q<$a2X|bM%6(G`>V8oD44Qu2E^{v&@PKHLvm(+6G` zLE8I!&f)~{5&B8FpH3Ak_B(8=|A*%Asr-(D+Os zhnsN0qM1|&Lv^R+z$+u*VL?B%1u`%{R|OWDJv{ngMu5+|W;Bul&^91h(n0nHaHuk~ zp+sfH=WDGjJSW`Kz{S#csWYL(1gLenQNDMFpp$c5BU@>uBRPh^pmPlDBqgEBN@2Y^ zd)@8b#FOQosY2uQ;}b!|7b977Z*$z!L#}|?yx+}jaU&lvJj+CQfr!wZK5CznM51sD zHn+uT`-+vW2|(>OcjJNn^Y*x_FIP@7`%F&xdij$94J|G69UP&Vh@78?VX3m!RP`s; zd%85d>4s=x77ut`8N52hAij#;v#UH#d1m>YY-X#wGr`AKF&pp$yP3m{THrM~G)oie z>l3m$&wHH5y=1WA_$|y*E@1IKgAEe0J(Gc1vTz@#i@d=1Qzo>bTz|uY&`SKA7rGLh z@89Qqf$6h*9e===T0;rf`Mwm!{OnY%2HihRocp4EVTC}~ z<)pW}Hz|{^C+f(y6hPDlNLj*JW559X6Xkfj$I+rBXNL<)Y{?MLpb(R78L%9XOxQgZ2IKDI}u+_&Wws?;Gx&%^O zYy-MhNu3G~9InF*+BHe{L|&D^@;#;%&er^2vONh-+)B3f4tOg;5 z&s{2XSN2r`!I{vU?!)qicRzDD?Xy>Z24#@flFDB4rhg%Q%QVs`@U4@f$$HFz+W6NS zcu^V`GHzbbpr$K4qdz(AlnJlcUX&d6=qAeC5#lLS4e(kbTW= z^5I4X9EAbvD(O+ha#S?#8-h*x#&EtwBkt%{KqdsclZ=s?m0Ro*J>5Ln&$9C;%Kpbt1M|`{=3Nh&TN_52VP31nn(3q)JZsKAk%2qDi8iOq@ zP~L^nYgij8gb5X&2YXhjloc9JRa@d*NC2Ai&~PePAHzT&%f|DTdnO76uoDoND8U)ki&P1a3xcI z;HA+Ex}O9Q%+Je>?7NmrEr(M7G`BuDrRkJd5Ic@%HFZ9ia#G zu&^K;_xB$QJoLD{jE#-W*ezIW{vta3z2!hi??4H=BB)$NSE%|ZUEe`ln#YmbKvT>W zjbXc{JN7qSEE=&aGr1)7E=SykSzH|h-(iZg5S=6EUWivH1I0_86Y=+Ds;GR38|Iyg%WNCrZzh`$)sR;PH=sn zX)i(-klbkCOcZRv#Hx@M?V8(_4>GU(QJNM<2qz&9f20*ymQg%*+}k!usd>4 zLal^^ObnZQO=^GuRoHzrL}Gj~R?h@o_s<2akd5IyiEwECOwGK#taV@5y=5fT^guu9 z#1#M$d~Z5G9~#Y?LxfFOYv1*bST@hZQyth8gE8~~ORmcGCTd3YQbKB08iwwAZaoF_qE#&K&meeX+?3H@|jAY1Qe_J9CAujw$Q!P7CR>}=1 z7is~Vb-u~jVMrNtN?g93%H=CK{7!L&P5JbKArY@B(gX3~78|`-KuCWp3;u+?A!m&@ zqHy6W`98wsZYRI{A{HMulap~!6J_4C=ZAK znuikRS)8``1lD_0LN%LsK7)!6i8JPOhmf~CG3}hRw`0*HNtcX1GRta0A#{ydFJP7Q ziSC^uDm-(E&GU{;@q%aXYIdlTGvBkEn^R=jfvbdavy3=(^3P#g$OV94`IuToDH1;P zmR0DznNEq0i;G*iw|Zz5KKxT|Y;Zk8uYgWEpeF*KUPK;Za3DEhX3Sc}9wULFfA|^5 z9B8cc->@nJFqcg<0XUXfT<2eAB_*6IDh#fno)ry`7H9|9X<(?UMUE;lrL=@9!09z> zc!3j*tCTP$c|kR2hHNSlj7Oqm4{|$BH|p30ee4@`=Bf{Up{<8KT?-d{k2pSW7FA^G z3T0w9jlPHMO%?)_q9w8RA$`wH=1O-?vj4z)Ckl}7mV|GTJh1oy!VC?Du~*_9neY*I zYIh^}muCOe)WUDUsi6w;hVS}1sMlE3triwpkX(tDflzRcMUaRNT_Vt&HcBHBZ)tg% zcmK%Yjn!p4>czk?JaNmQE2P0a6VnTkIVTVVJmUZ;sfgC3J#xDVXEn}$e?^+5WUKLc z#hYrj*%Nlpp0jWXmlZ)`X)V=*ZJLS0Ph4A;>z9g`ZNu9mW5=q<pA2D&W7`c%%q3$xIlZ#LyNAx^@6X7-B`BDhiYA*5*oG}v z`uU+5l;)_k9M6|dVA!i19$W)ApVZ?cCkHd-sZ1tiF3$0O53?IC0S44V)AXb~d;(}> zNOXfY(KFC?=WuHcXj>?3``NFcVNdA3w;m7|&|*;ToPTcysF?*Td07>eytmd?upj<;+6QiwNfSsCi>nP^4KH1gA zHs)(Okz|j-=a%;76RSY&ffqFvp6h?Tnx|Q(kz#59szNqUUZO1;Wf!CM4HFX1B?%<| zAAU#9xT|twS)&T;>pv$;L|%z*%{o~3K)s;=0JBguf*U^4?JRGh z!U$}t63Eo=?1+zM7$mb5Yq#xE?_}myYyE9lY;zcLs@G1JezE4`3yAO z$8ivmtz#v+TPXBHQB3dCZVa|QcVhGGoN?i7v-lat^I#r5F9F%^-AG`z%`F;Kf(jL|A(}A<%vJNmD=C5N0`eGpul4mTy2!^(0{;IZb_lz+`E!g&l{X zjD9T~{xM9V`%8<1wY*A5?GHOk;skHiVOC-0VrwPkFc>B}ASrisbcmPTuGJAsz7MsZ zUF_}iii(m`S4<8@f(R$W=N|$YB*l$X1qdd+h-kuXlGn+#d;DmFpH3+s);`HLX_jAUP`_!s1+xP)*ys?PTAVYre$xXT?kQXnq_-vj|z7Y#-vNm>D2&zF)3 z^tZkJAl&%=Pvdf(5g&0a+n@hpx_{&5qvMfG zh_qX{Tl%MJ_UjdpT|-~?8GJb{YHCZ|`N>oJx8_!abbDJbw7iT*Btjzbe@kq59-JhX zG`|T&C4=YuBVAsRJz+$CEMP?_>XI5%O_=g^^AD6M9c)OjSW$>;!gNk-+{KMhSSf;eUcQ^aDTg;IUo7r5nGiFVb zVAH81r(n`w4(~5yP0osuclp}2kF9O$E>?;-yGML(Z{EhL-;f0WY%ucHzi$PT2st)L zOOdNxo<1jGz{bhZVT7J_{sw95<*#0y3P&*P;IHE8<=`qw3Fui>&98U382pjZ$fft` z1O@mS{COWfX_`xQ`xdK4VDtK&b>8w+FB3s=Rrq%n z)$JYI`I6**#f5hLeat@(^?y0E)9pbS+c2F5i$Sx)Q!IdH{FS>oQl6gXje=9{3TCYF z=--j}6&h-jsWGxK*8~`HdJtA~M0;N!110zE6ce!0py1o7((!Q_We~{=eoO3wF?@E* zx1R+_oF3y%>L_qppFSxoavtgJE@w^DfsB`UA8j!0>I44L)^tj`N3}~wd8PeEUP?-n zOW9pd6lJ@tR`9y&gTS;+fPUhlqoV`eG7Sy0<>D>=&#B52mYJC*@NgoolXIIX!)51! zqM}4;W)_xDvuzQa9xx+M3C+Af8 zHySpJ{q#xiS&Ua#OAv=4bgKE9njO44DVw7Nf_!bRaKNSevif0!)(8i?S?s=4Jd1tfsr-b) zX3{MoGm6y)jV|U_r0sfl#X?)(+ubn^&Fv6aSEHbig}KEb9FDO)^PGr?*Wua&j9?+T zIF<9{yE>=d#g>WVV$>ftFW<=Ho1V5mKE6VIJXz#uBOzg3cNd8$TPX?B=Vu@Z4#KF$ zev8S|hvVbpR#t^exzHv&UPJTOW@c{M+J-92p3WybQ&kf%jdG`>^}%u|jch!}_Rh}E z<~+Q@j2-Z5?YFo~%%_%zx}a@A#6Sg2$mJ4QT8hqE<$OYqepLPtD}i^f%Ke6KqN2uA z=wzGM-h75ncb{AtNBLZ(&Xu6ZeMcK#w?mrS<@Dw4$*ObjkH^N5Wl#nbdDM!}1Gn6% zX=zD#TyhZLynp?Cg}HmlkZ%ew2p5_l)lWutz9Ss1}l z;t*;YYI}ei)7HKgpKD=YVq(T1;jM6O)6Pobe&PBdrd3+sy5nbv@0HTo^abva4N&}n4_1-R04o&AM%*7DI&4x0P-Q3ld#b}^+pSC3Bf``D^kh*@Cy3@KC7i*_$#22EHM^Gj_1i&$sL!C z=L0w@Zii*5p!gaZ)iMGu^WPT%1fXPw^XJvHLZj<9=#caxQtwyCiXM=Rk< z+l3R0inbDrlck&ulm-W9mp19L46*!>sRXk( zbUFCYLX+D%q06h=tLLoITdVyXthVE|jkVfccM(UQSi+rkSSTDlL{Z@alkh1H(L}2J zRUpxLdxtL>3U^f}826dt0fvcs)({zB0VZxb6kF(URdlDs0+GR2!(+ExUCqP5$mg3y z#gzEz8};Sg%jOPd-MP75J5w)j?|eXJ){^f~u73E?U}n6~jGwOYM?P9dmD`bd#~dTh z^0Hf3XXpObgq$l9FAz8&9~iPhhCfs`=xCJdRJsJvVG{GP(b3g>ep_a`ZhFn2(-U8A zJm)b6CUQbX%9QQxWje0euZ~VGE0})<1WZm^#__rbo5p5wWo2dbhn3pr;#+$;C+Z}` zbK7s}&q{u-b0i}py8;`-9^?3M-+LmU;$TH_dvLeFTp0H1~Sjbi=^KKuTCx7=fjwU2I&Jx_<6>seZD4V`F1RY)nj-N?IDM)MO%0 z0VW}V?M&ScEzOafs`NwiBNGeK)nPN89KC7r*RF9Qz<&{#z_C_eTcZ-5lfbtJUOi%H zkbv1fwk!}sH85N8ONeA<%UedTJ42lbjL7H zwZQBZtoP^0D9?|!#Q7_1Dy(l$(YF#>UW;1Ckw1#O+i<^=KIK)64@uBLILc@f^U~5# zSC^;V+e;!%zPE=*N9c`3gr$+wga}g-kCJBp5t2;GTg+DoqlZ)i*mmSoWH-=8v->y0F2`w~}Nz6k`No-#rw2ty3`}u7HW3g z=a$zyYr8voo<6)E=F4IVC#R;)w2$L*a&p3U1iMr{fya@QiAiq~vQV*PiGRBd_-?hs zOhH{smeU2%@t&SSfG3(Rb-k5OcY}e)SN4I~Xl48`HX?p*<04hj$KHIY)XcKU2OSKz zva4|$oSdA5Yr04W{64+k$&|&xC3n`^qXE!7l!t`a*s;;k5^LV6eX}F44mW2%vYWSm zh34P$5(Ml2y&P2ot}v;5$6TQ?juZNNo_gWv$jRD}nI!-X2UQa*y5>f+iUz!RM!3&L zkrwudc5}i#k{D)BCV6k`|j~8fC%=dhM4rvm3L& z>@76xHpb=E)Yhhv@_n?Y0fiAvonwH^q&6>tis)>Kq1j+F-2n6C`?MrMJJ(@_%o1_6hs;+-~U z7N5Gf^=)f`v61RUQ9(hF_KCN=T5(H~&&A!U_}u2-h@gZS^vBjY^yQ6@iwqV_IQY>+ zzwC#dYKHJ$V1QW{tQk5E@Mfz`7@=C2C%~t|l5=?UPG^J3P@t7)%UYdsFQ2)7*1#a) zeCNtFlF4e{{aFl^J8gHx$^lc>(Q*1$!xd$970H%+rpVm`dHWqs0e*y{c~VBk=kr5= zwY>}DOiG%|qM<2K5T)^|a2aDI<8A8f^bmz7?_NTuoH5`2zDY1*oG3IXqPw?@h!^1@wsC`$*b1#C)e5g4_OZCM(MFR+lvUe99yCU*qx!w6# zKa_lV7@j%1HV2eY<+f*jtwoaa7}(YJDQ-F9P4$86ZkL{&Z1J5m1Xvbs8>y*;%# zb@as^vJTJW6f0|~N{%|O+q|%!AbAbW1PjORrISqvR0-S%QJk$rh29x*LC~l+bZljdPdJWYTu-a1R5ezJ>P@7k;3JY;0w!!eW z-zMN`%RT<%C^#(?77-O?)X_42wLEdoc6(XaHTyU4xBpc~1bFoSgK-Z0^gy8UCsDjZ zbLSSN7DUT2i+md#0zYtVgRH~FE>7YH9G#G z+4yS1=!vWRyOezZNLB`-g>8;m8@;*DAtxISd|-Ny%( zmKHJ7-PtKY^-Scu&u!Ma+jIM8zRDUwAXP4FB-`8ZH|8@vF7d4a1dj>(edr{geGhK( z0c0j*MXXTBlg~f#86(>k!tpk2@buM9+u`n`5F+Qu_-0&6&G@lR|ZMbuenVtZca3Xu^H=eRkOt0FS5A za@%entyK1OVVM)no=@rAeCK!E6&FSS`Vl}7i6xKF4*Q4_x3n`?MJP$@luOQ!*^4G0R7l@u zvGxv*cpc|CL`((^dlh~`(~te3bbX!OnpK8&YD8t`L!d@Yf{Ra+nW>Sl4W*x2#934f z33V{PVSoJ^wx}>XJS^mct}3S_B`K|}qKvb+0vW5Orlac_Yk}E7^B()y%&!ImD~du_ zuU6#;HMNb^^?qo7*T$Te%}R!XhKBR{As(W;ZQ=s%b?_&4x4Dho1(u2U-6+6OVc4_I zU3LS@$#L1;EmRmpPkSr;+jh)uV}=_($UI(VB3^t>JXNJ)leNDr-A>n6WoJ4P%V!`6 zbc3RX6g9An)X^FGO@AV#8mXEObTS6iq)UwAU9wDnR-ax%40@X8)=pwq{VL&p_FMAWFBEG{p} z%RI%x+St_er&;Na6%6(%FK2*uomos$NwDK1Y?9t7{0fearqJPVrzkd*N|Rdjt}?P! zvc0g+<+eBOsvmjx6lLitltFv6$RspAK0YlCR~cBY*48$*CaQdhLHJEQ^~ZohaW_)(4wu zcB1JI=hD+R)F_UlOjbws#OsE(lY4RTiz-80nB}k7ka71}U@01<2v~R6>=%`hA!HmK zQ{J8|B*FpiBcjbMs+bJrY_6Kr7s+DmPU#k4(8t5CQ;Is1bLc%re8&f+_P*C8k*(z^Hcs@n$EVMn`+Y13UZ%Iew zGmtjj`!+k!RKnzHrZzke4LyiMDE zN%qLb|JSc4Wq5&mw}0`C+RFE^~f3=w+zca`rnU0iCMdTX5R0ld2I z^O~ur6Mbu|=)6wOBZj%C&=|^RXda$3{r3a3iMj3l?gg@0M8`x$H@CK=!(e^?DJRNI zOsrq(eCXw&*F8GyT->WD~FkAWYqU9_zR1yGy>?n`H#?;7+@*}<*{mX zbOy68yHTJp{0t2n`_iG~@m!T+Xhvd2s?vHo=u?#Oo-u$Gnx?|3{ux0H`+qB{^6$;s(<(5IjM%zyRcR&06d zar^GI^F#+g{$1K*w3mctvW8b%E6ZhP+@_*kwan%+npj<}IkLgt)6+xO(=vki;V+x` zCdDrWDI79c-SSpMu^)gQ5C|o^VH_%8m+^?ND9bOtP%yYOVpYG%&B_UeIS9oeXIq{Lut+m314h~#LMdn99G0Z z>6w{pD=S?p6s?q_V-t!IZLK7rvGSUGa$j{o*~Ix|jTZYszE+K(j{5mMZtkknGu}L{ z^xlocLmPlxnzz~Q`&bQ=#5FZB;XB-q=di;~=-U~qkvuY0mSj8F+XMErwDjy^ zmo?IJ*XUfH|CFgjn_q2lpNutjD9+o6A8&GpMTHL~x=o2=wJoizNS>z>Hs37K0+Pqm zCe$E8#@`6#w>$g6A#G<++tc=k>w^_CxrO@P;AJz;r$|~)^74S=LkQ(0;jJa23gfjF zbR-6mbXUX+|}eK@U;(Wg7`0n-D#4NcdAFl*tuWN#ix$UaXZYu!y_H+Py)zS@>ls?!CXRAv zdIg>=`?>znvg9D*SwfS-CQT}sWdw>E^BWkcFlcd3VWGT&!n1^gK9@@xH$qMNk$)u{ zpeigZEbKQO^zrA<$0mKk!lKJd%Z$56@?pE1Yl+=4cJT=)umrF1> zm4;YZKw0g!wQx#Y+fa@9r{SP3(L)3#z#!f8lvZG;zhV}6{7uq(IJE-Nh4<0G)p_c^wRBoP2ffyvv$4CP|312eP9)J#yj zJU{{!dj$mrJ-&w)(aFj4s4-R?gv!dS%e0EBBUCCWiHR9G$qG3kroS~g!KWZFJp16b zE;szut;YFyv(jo|_yUu}wV=@3yS4tVE4XGuoW2yXT5EFBLz`SQx}evV5XcA!=9A=J zW-fO4-i-J)olVNccP+u$KqjnHv)t+eA@jkMS5iv!$n^D{2{r1@_oINvRoU*j?*WvL zTIDnxUSv^q%c*3!CK5AW(u2g9v?)z&uX#IQ-I(qAqFwM|ct3$3z=S5jWh-t|J4pY2=p zX@Hpa>*gQ3{U8`J(LXZC3K~*;;xpfxA|bs1o74J+vh69_O?E7!8Q)cEY*<+Lr7)N6 z_E4?X24`^^lN>%PI=amxf4%Tliq>)U@$U3whfFJbKhBzMLW}YkzsPCH8ebMB5k7o%W+WqX>@oA4vx;Nz zMKmiUFF(J|`m88#s?u%~u@*ccq)r4j+)J|CW8m_!p-cjzQrX79UOvwldk&!Y{cYUoJs zAiaZ#w9rE@0qMPj9$L1VHWG93 z6E1t`?6kSLM#apeE}#$=w0SNa5~tdb*ip;WD2W`>Ne5N=DD zxmi~KqoqtMFs!U(kvk<8aE7W7Ix-d(7Sq=5Q_~(8|KrCKrHIJniP7rH+#ur zK4AGf`r;r_{KfMd0vYM$_&TnTp3KXD-1GMPYY&U9x7~=})e&)6`Wh8`#C%xhz@#t> z7{Tu(8{-u$!ffWC&lk&<3YTe|}Pn>WMMq%SJBjtX<| zaXh;F4gVC&sFFYC_=;4BU86Wk>#MV4or#U9OuLVF>H{_3uqH!=X#SX=5%Y4$ZJetW zso!)?R@SeUmRDoZF)>LJj$vo@X0Z2Fx?s`{6O(bWa&$z*Yh~rHapAd{nLVFFn6htd ztx!tcTev;*Kr~0AaQd_@0EGFkH!3e*0bX)^0=ygOstZ!Iv`^lpSCu}!n}W(6o-i<+ z&ANX5&K&6fC&3c|0Xmzxg}>6z%opAnB6MWP;=kBsWLnKEEnbQ2x^D;GPOgf|vFLk$ z7^_2bdbCe*)d!3*%Dxg?fE2g`Y(Q2ii1m zEZ>E6J(05E;o((GF4W1X+b2Mat5d-f_9!=Jf-5|REqCNn@iuvZKW+b^v4{@TXVUa@*pp6V^z|>y$tn6%6 zZ75W(7HY+gskg_E+IupPQwh0{!{6Xn$!aWb*r257N~$^(pBc z?69ngp2BJ9aMPY8exR=}dv@mMzVtMek0s5@3OZL=SvhZZ&2Ij*ADZ{hsWz$cQ-5!7+WP#<1f`7JOi|aB()13kQnS%8wuyVY2REr`1sz6g$Nc1{ zN4XL8A%N|=&3$CJadN5DZQqJQL!<1|r%z7wB6B-9K<@HCAsmiQ1i8vrSKgxc{Cb)3 z28X@Rz!VH*p?)e#!Sht%mN|Q66-KAxElD#S5I2`d3%D`xS#OU8K_5nz-sK8Q!cS>D zy1c(C+2a=yAI)p|OjoxyCguwms39!rw!Mqn)h4;krC+qSNk7jItu;#bThwD8$Wq~A z7xC4MjY@pRBWOJ-P4EaTEp=f3_3IZUw72W@_Pg!ziey7wjh@&Z5 z1m|@_`FTPhh?!kEE#2&idOb^G#hbI?BFYcCp!tERVV>JDs?E)%!isT8c~V*rpOIjgSoc=;b!-wq_{30&5v6j zzrJkwj^BJpOpu4??r-39g(bExXq5C#6m|D~&R_Ldm22N490))zUH+;?*DV7(G#QU- zFmvD8_9vUFbZvS!ed5}{6YV+PU!pJ$h7V#7VpkIMbO!_kx|1jw2cK&kUA_!X&(P2t zq0=`a#A2gk{a5GbN*zn%_|2<<`s93u7mMFRU?^;KjeteRu-yF85|p&Ec%ZH=RizbD zU^&dmlgrfAQ{~govY+y$v!yNi~E-;$;#qv z(s%L1S`S%TIi9%6$-i2lN~8fMgVpD;<)4@ERG^4X)p2Q-srM?s&%)2Y2E6^4nC6Rb zr!(d@2krb?P5-jmxg4&iYoW%}EV%XtWMi*h@6!u~MMuvqy=gGs(SgGO;9K_7SgO>2 zq*6#n%I=i~wV`1JQif5c$a%!j;-MX}X`25l;!Yl(xMi2A=ke7 z`IG-<{U8N`iG@WLmJ?p@K}!3i8quZUrme=u2T*`qB*eB|_0pwgP|IXwrh`VduUS*y z8Vwm4muU=d-J%^wg~f%XWu&QET&JQsJ}e5!$k24t5rG!w=PPu9trrlRo`K%Vaw|kiS=n!1t2xQ z^~6I^#ZD!P`;jvS1_xV@<_HV|ufm}`?bYgBj?-%#Y^-2d9>FKB_yPdvy*g7p-mXR8 z$n4R7U99dgfneg0D?f9zy(;d!I`$qI14c#7tO}_5CubBqaYVW7f4Z?0HyREQUs4q6}h6{|JaHn!) zXN{jL`FOq=DVC+${C0WfQm5aqmd}MWk31*kbqYUz^Z+`L>~Fa&iNsDT1Hq{6?Q~u7 zl^^%8Qq*kAz))hV);`H3%~rX-KQO^hinpT3$PK;U>c_=$7ca!iQ5u}HN+}vC>I?3^uo(3`0Gl6Ws2RTrI3=<79SijnjCp(J` z8>es(q;F5(PbE;`t@G7jR-@K%3|nnf;|FhlpYcdVALl+B=GZRViJ<6l1=4sW>VULLTE!G z$vc`FC}%#W8Ijd-bo5B>aDm#}gGBFJ2wDM`H?WMYALl}q)h+O*J^FbwW>YDyYWrj1 zR$i*RoNhLu@e_2;ru|RtV;pUz!*|JK2Y4owMyB+i~|7!K~_2t$r{al~BdZydd za$Q0~A}E$$yWGGS2yt>KXC6c@*)L%#7@O%RCSBc;2vMV>ytp#6WyEZq`-RojRj9Uh zKCD8sI?V2CaKbMbA&$@pSezXnJbA@i>gZ5PZGt1lc4@Lvy)Lm}q6B0kH7%_gC*9kI zlQ15amoM9POnX{_kAqOt({ID3>yBSh2&9sVd2U!8Fo@F%SiI2L9GJaEeR(iK<8uGT$KODKJkz)iVMc<&Mm5~&9^oVunE}Jlu zxSmHWQM352b=p9XArSC{g@ujS+r9>J@(dH^>9;EAWB&})?b{3Lfb9nJ-Z(8O2O4~R zr40;jUVy35n=W$eRdZ_>zlO>|kLZF7Rs_s?tJIMA)1uD_DYa@ZwxHiOHZ~LB9kr$)BZFj5$NvQ+HuIJ727({^1_sh>A42{( z%O2VhFraf+JUjtf@@lW>+?AKKVp&s5p^^a_`p)*SDmU>R9}jPhr#R45jK!a^HpCoS zk&$JNO!LukYk9CX56e%0p!{@9F#2Gcf)apA*AgV)(*TZZW)BZni{V+M%{z}e5Mf<9 zEJ|gxVpimRb&hJG8u8OiN2EW18iwy~($Bm{{4_Y3|TGv7AiNDYGCjcqgw2wfs}IygW|e^$V<} zV>W{j(p1o1(U_H_s%gzrw8x3Wf{y4C}uFp$xD`0UD`4lypEN8#}NE^#1R% z*}l$Bhts4E;3-jJZ?`c~g$F$GJ7`6;u;F%Ot~ITo&FXN)B6wIs=pk@?W9r_jCOBW! zSinT;fV0jEPvl7WR+51r&w98U zC$^tz>_T&Ie1DzT z2biA$H6ar-bNSrJD?Lq^l1)=}B;bNeRRBPtTkAks7B6o9s+H_rw(mYsO|4EX9QSubB%3Z+Y*c^dq!tjZ(SHo+D`1uZM;YlXG3UT~t&g13g zQe4Y=OiayynKN(fH|1GcUQs^(d{e(};Wh|=K3+fW0!jsTcqO8mI3+o`ATP}*t58Ny zrBh{S3siRhLABoXY7OrnK&R2T$*D;u5CcP>Ko_0dyyRr;ipJeY(NY`9p3;35r6jd` z!c0CWhKdd4(am-~cF-%5os;wZ)N355X%4-DZ zxZ%+s)t!=lBJxhsK$T$)Z>pclr)RF`x>^!)M9@|fjRnQk)gooflVxgP4E>826vm{n zu{1ys4z}*wx6{-y+5;|aU(W~`f(d_J<|3h-)gJJy;h8-ua5FQZpt#`gOZXFJEpU<|2D~3iTyx4QU0f z>9!Yw@BxdL?uUj{2tJVb#G|)pgV{YG{;Npd-Hu%a1|1OR%gt8~7{x$y1QQ88{hFj? zlbN=($GoETC9TGJl zU%9bG(&Kggs(*1L;a~R`3u#@XO0c@mdA;vITQ~b#G>;3p zIH*6IH(0nW1^ceQ)R3t4EG&L!EO`OzV*}bXmbI0XAi_+` zqx~%GDE8RcSTwz~^oy~P5!=c`XOIW{;JJuzA}6}L^Dk_nEO&9G<9K&`JYDQbPb@FN z*uM5BiT-!fY~>H)5Ffp$+c7|U=(ZQ1eq8%caiYOm^fXtj0JkG0WvlO9@bINAtc?cT z5B+?Z8S_nDw!Iw)tg~4;LheYo%zbVIBd0TG&D6yhLNb1-e?z9{h2Eftynh1XuYYP2 zz{cDU)^hUm+tqDL1I<>;>4D%B=zI7c^Is))Tc4kAT3iIaX^}`|rVCTW)_1b)_FOLm zAQS-wZtLk21dt{A6BCnfPH8EB#T#P7iW-7Ek9K7hot~7r9<6E1TXpVgmqc;FgxxxY zP@Df%mk4B|^2=m4m~CZMP5mM}jVZ+GpTk=Kd=ENcy^i{)A^fJjc^`|=Z>i>ST3T8r zCMH?3HB)M8>YVIssXs(uWJz9nx|EB{3t=`%;oL(be(%BF-k0+72KCHi9(-!y1YJU@ z0}aGIZS@ck0Mm~~ye>#At)#1=P@8(&-j5S*=ZpU7l-i1fd%H3c z%##lV0`G{cJbR{JEzNrUkBjq`8i6SxYVbd>p8aFX525i^+!7b;7Q4`eWj1iGO*(qo({6A(YcohbnX+zt8M*Oq0>hj*Q71ncqLll*>t$%nJ zLyda$)aX+(2`j7j)e#~}64sX#H7Pgci!7htDmXU}8x5JAEbqff|iJrQg z2YpvW*Aof%i0Bgw$nuDYropxUe(Uem`1{%Tdpm$Y@ONVdCl>H`VE;R? z{~g%>4(xvi_W#pFH;Kcxlx_0C1z6iIIiwA&=MUtX{Ux%7R6?0EWmKO+c>RBxQX}JV z%xs*TYrRE=K+Zt)#$8?e_fr32R&D{#aejJFaB`ple^7Eo02TlLc#+ zm_#9wR25tdf*YrRKjVbe*M9&09oPg|4&|9+GP*ir_|Vas`8o-977g?(gU~v+y?&+O zmkJMqaLvs~k4NUJt%?bWh@t&d>hTO7n{86 zD&t)jw1HlqgM))s{HTknBHn7Wcs~2I9vKyS8~5W>l8Q?wf>8C`&qX=ga_UrmfA{|V z7#p{>=zg8E+SpQW@{A8n5Dl0V>uQHUoM|t{$Ho5q+UUL5mEb-`j))F>0jjbzoMM-s z_~Gvea%R&-SyRN5t&ixSbaKdvviD^vaW}f7f$#?#)jPp%d&K0cqa38HSsJ}na>&I* z_$9OW?-jY%jknFbu3(9OU4Xr<~mzmQHu|4;W8( zup1g(p$hOPe6 z)28)?DX_BZW0X^_?L1j!w$mS{blnx^>n7a~5}YRy9P9i5iQM|(jqdJs-|Vvb@&Ht< zZZ8iuM9a*ofUg6yLhN7Fi-`_3*=m`9f;8KU9n6PTDALbOPm@R5OrG?9e~Z+0GgoQy zjdUgyp!ai7Kd_ByvVQbKe8vH2G&2&v`K!z9Sk|6L`O>%xMujA9}`+q?0-9GA;; zsNBNrTEp-bg^L7I>uNsxh1Q>!NKhzLz5B1w5|eIuzy1ukdcIB>_wl;m>1;^LIUoT#FF;GCBv(!tUpAsbLT zqlF|m%y`)ZG3oH81pS#w{N@H@;O_OK)8)|9bL8v{3DFX8H$}znYb=TCx!=B- z7B=lz4j!4U40tBq8}!G{wggf09aYYT*bIDE0jr|_esFM)b^vdSiqPQDN*69^+TR?? z*WOu>l9H-(+mD45rocd(Q~|W2z-qQd0v?R&%0H}nB_{|Xz)6IfBId0hy_4nmeu^A7 z8#lLN$IB!J_*#*G<-myP6uhu*<7oF!hu`Ns_N3o0GZYgiz|C1hK6?y3bFxa64$4%> zJ!C+|3Rp5QNSvhA!)qMYbu=~iJNuQm^ouJSAq>ApTKJU22Q#_~Op}}^xdbr-b?yf+ zU5EEhzHiA-ghk(M#0x{HxB4U4Re#>u|Lwipnd=EpAD*rgbzZJE!iJ&9?0PWH3Hb1H zNIis<6R%-wJ<^E*J~us}UF|6P9Y{(C+Cn+B3Od2k5{1qWSn9^(EC$(GUu&rr>mk*1 z_49S~%fLdLl6%@?NFPXxNxVJz6pIt6NMsij1hyI{WtPL%_O1v7!hE>}HSB3L2&lR= zqxFTZI6e=|7{w-bqpg(yj%`V4U~kbSweAlErCAwj?KFVCyg0DC4PDrf#*<~8#shZO=A zum1$TdI=3iq5aX^*@T7$Y#?J|ERLAoA`%@6J4}=z@=)lXVORf~}Ou9nO2#FnrHE z4rf=2$=~b1Z-ySU1Vg3=O3>45!aHCGnRF+H69RBOv&PAHNfqy-Y-^)~9^swRPUPm< zvj!zc!zN#J%@v4kf4-+~k%2Krq)g4L&BNS+iXshKI+Cruzm+VL$T^nX#JE|_Lm+BA z6mo=Ybq{y!`<*(Wo9XN1$4R=bXL*cpo1-g+peL-_rTfSOHUH>G$Cg7p;{JS{a|fE=klqv6=Lk zXwQT>&65gFIklb>4<4<2Z#a(8*__B}E_mmps1@Xf#X3)UNOV0qm_6Xe&-avQ4d!V- z=!%k1SnNMB_h_E+bW(J3-7yW+6R}YhDS;vDSQ;SgL9+OaKzA;#h29N}zKhB0qpUsd zeDv>9dfT&Q^F>q#cbk=KCAi8^61S3Im-N@Q$I|s%F8fz-W;3*qr76J#CHyT0_?FUg z>TUxQ=KXK45&}B=gG#me!|W%+keB1c(9@xHugUR-|FvHk=gH5 zq6XcAiuB`D0weE#{$g{qpwi{vs+|qVem$LqOnh0QaO~2u!xJRG6XJ&b!e9Pfzm8vm z%Lca{Y11IeP3rD2A`X~JxA{RqG;tii_2J>xvz-N=wr*IlHHM?GJ`oX5Pr^}Pq1^B& zf4>YVoi^GM94BPFQo1`E!hn;vwzhVnc6YK!Dv0H^S8PhTEU4>lKGrbzlkq@g?KjR7 zX1kc=3mR?o;5Z*P6d~R5TGxvFU^x6lG;?Yaa_Z2WH);=aKebuz{Be%bCR=FM?-af7 z3$=z6O=-J3tw%m?Emo4queInY5O-&AMFD3e^jY>41Rouah@g=fgS=_zmN^g>gETDc zQkQI29@ldq;BsL02*Y|n1;Wn^3D)hM#YHHO;ZdNrd5u=4c(Gr4I_>x-CbiXB>FAW+ zyMLPeC`mn66M?mUhqglMInL@GOyIG*+RNN^WTFp6CCUQHjv3qru0dWNU!ixJ*l?$R zyfu@{jT>}a{{H@Sw0)k4=#sDOi)ZSoDg1~2mez{LBvjlRo=09v659H%Q>9}Ok5w^3 zCs_zTI)s6a(<^oi`6T3cIcLxk-7|^%@gRx^rc(3m+q!i=v}_{Vz?D|8p7&@SbFh(U z9kBvho`{G>5E{6!zJSY#N#&v&uWe4od8jA{m5y1GTsj9EsswyON+H{2yyi95n4<_` zb{^Ttd&f_D_f`ed*5j*o`knSx1kB^&;z~S+;XtlFlz+DU>ogMSu-0pR@~M6Fl-D!& zkXwGoM4>{z1?;zWt7VGOre?XQ0xmXL(Vn`kl^^F!340Nt$DWZ7T#knNoyHsNM_YN`-JIl81VaB67K(Nck znWtsrx2bE7$-m;b8mro|1(_;y-C1a;6W<@H&-{~tfd@p%magx(w^Ts@P|C5nni?d)(;I^ z6*zf#l$@8}zpPk`8Q=lJHN!F2g;8X&sXxs?kMEZY`{ROY*vi(QR>#0vV|l3mY$b=b zWL7W}U%oU@1aAlrftFe9y=O`4%c-=q;9b^_tmv+nHnP0MtSCEPUQ^!9P(Hcsr#Fe0 z;g-E-7>6af&23$=*yz4Fb({gkzb>}c)0wO?g+mCj)T)72AQ6x3~_ zNp5%233?ulykkhMT%7dRLP#9_#wbZVHX7*_Z3*;XPWKz0^03^fZ7{tuw>V6P-|ry* ztLoHWj$`uC(JoA1x5`cs^_0T=hOUOKxIVe_}mxxDA6HPa&#J z85pZQrU#}bJsgRj=4xq)wS>XF2W%n)@V~QSEGY=?nb6m!?d@$mwda*ARFN=XmgG6L;>6=l8~QLgU#Sb{!G(syb8DJ6$4 z-aKpS3Zi-90v@(R_{Kcpod14mw<5sZ?V$l4<9ah}*siFjiRfS<>~)EBP{czCLwRuI z4Rf<*dF)A}li}FaLNOc&LV9E(pzmEe+rg@z#$pg=3sj zEG#^jtHr^EIpoobJ8IsZ=XjhXsbk-t5rn?ahj0AX3~63Y8m|O9x4wgYwJ(4e^=}SVIy2s`VD>( v{k-*m`>kU4ykklt@-e@kPP&Hy{5E$xiP$ literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-llm.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-llm.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee6a345cbe37f488dbfa4787b230a62b1bf754b GIT binary patch literal 49304 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 Qa2STyO|F&wdH3;u1EGy2Pyhe` literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-mobile.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..9875a0e67d2f756aa30e2acfdac32c28e0a967ed GIT binary patch literal 36258 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 AEbM#o-@2vJCo? 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 D@hXmj literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-save.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-save.png new file mode 100644 index 0000000000000000000000000000000000000000..ff1319481bff89d764d31c33926d22351a9493f8 GIT binary patch literal 62526 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-tablet.png b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype-settings-tablet.png new file mode 100644 index 0000000000000000000000000000000000000000..333df8d7dbb4d16f7f15bec8b005f512569a69e2 GIT binary patch literal 39954 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ⅅagSZ(H*t13av9ZI@j> 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 literal 0 HcmV?d00001 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype.html b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype.html new file mode 100644 index 0000000000..3097b57670 --- /dev/null +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/prototype.html @@ -0,0 +1,1824 @@ + + + + + + Aevatar Workflow + Activity + Settings Prototype + + + +

+ + +
+
+ +
+
+ +
+ + + + +
+
+
+
+
+ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md new file mode 100644 index 0000000000..30b85dd5ff --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md @@ -0,0 +1,915 @@ +# Workflow + Activity + Settings vNext Design + +## Status + +Proposed for review on 2026-08-04. This document is the deliverable for the +design-first phase. It does not authorize implementation until the product and +engineering contracts below have been reviewed. + +Implementation branch: `feat/2026-08-04_workflow-activity-vnext`. + +After approval, this document is the normative product and engineering +contract for the Workflow Activity vNext frontend. It must be read together +with the in-repository design baseline at +`apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/`. + +This phase changes documentation only. The eventual implementation is strictly +frontend-only: no backend source, endpoint, DTO, projection, or persistence +change is in scope. + +## Sources And Precedence + +The design is based on these sources, in descending order of authority: + +1. The in-repository + `apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/` + `aevatar-workflow-activity-vnext.excalidraw`, containing 17 named frames. + Its SHA-256 is + `30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de`. +2. The backend contracts in `origin/feature/integrate` at commit + `72093afa4490adf6eb25d83c3db0b9c182cd6ff0`. +3. The frontend architecture and identity rules in the repository root + `AGENTS.md`, `apps/aevatar-console-web/AGENTS.md`, and + `docs/canon/frontend-design.md`. +4. The PNGs and interactive HTML beside the Excalidraw file. These are useful + render references, but they do not override the Excalidraw or backend truth. + +When sources conflict, the Excalidraw controls intended product flow and visual +hierarchy, while the backend contract controls what the UI may claim, filter, +persist, or execute. The frontend must degrade honestly rather than inventing +data to make a mockup appear complete. + +The source package was imported from +`/Users/xiezixin/Downloads/mocks/workflow-activity-vnext/`. The repository copy +is the reviewable branch artifact and must be used by later implementation +work; the Downloads path is provenance only. + +## Production Data Truth Rule + +Production code must never make mock, fixture, demonstration, generated, +cached, or hard-coded data appear to be an API result. + +- Remote Workflows, Runs, Run details, settings, identities, receipts, + statuses, timestamps, revisions, usage, lineage, and availability come only + from real API responses or real user actions acknowledged by those APIs. +- Pending requests render loading or accepted/observing states. Failed + requests render error or unavailable states. Neither may fall back to + successful-looking demonstration data. +- Empty state means a successful authoritative query returned no records. It + must never be used to conceal a request or decoder failure. +- Browser storage is not an authoritative Workflow, Activity, account, or + settings data source. A request-recovery helper may retain only the explicit + non-authoritative receipt facts allowed by this specification. +- Test fixtures must remain in clearly named test-only modules and must not be + imported by production routes, components, hooks, queries, or API adapters. +- The prototype's hard-coded records, `localStorage`, and timers exist only to + make a serverless design demonstration interactive. They are explicitly + prohibited as production implementation patterns. +- Bundled Workflow templates are allowed only as explicitly modeled frontend + product content. They must never be labeled or decoded as an API catalogue. + +## Goals + +- Deliver an isolated Workflows, Activity, and Settings workbench under a new + scope-aware route namespace. +- Let a user create a Workflow draft directly through Describe, Start blank, + Import YAML, or Template, without creating a team member first. +- Reuse the connected-node Workflow Studio interaction model for every draft + creation method. +- Make `Run` the regular execution action and Activity the single history + surface inside this new workbench. +- Present each observed Run as an immutable record; Retry and Run again create + a new Run rather than mutating the source record. +- Preserve authoritative identifiers, accepted-command receipts, projection + latency, and state versions throughout the UI. +- Reframe current AI defaults, Account, and Advanced settings into the new + design without changing their backend behavior. +- Provide complete loading, empty, error, disabled, accepted, observing, + observed, and recovery states on desktop and mobile. + +## Non-Goals + +- No backend modification, new endpoint, DTO enrichment, projection change, + migration, or persistence change. +- No replacement, redirect, behavior change, or visual rewrite of existing + `/runtime/workflows`, `/runtime/runs`, `/settings`, `/studio`, Team, member, + or authentication routes. +- No global navigation change in the first implementation. The vNext routes + remain hidden from the existing menu and are entered by their explicit URL. +- No assumption that `memberId`, `workflowId`, `definitionActorId`, or + `publishedServiceId` are interchangeable. +- No locally fabricated revision history, Run lineage, Activity records, + duration, usage, outcome, approval state, or published runtime identity. +- No use of browser storage as an authoritative Workflow or Activity database. +- No mock, fixture, example, timer-generated, or hard-coded server entity in a + production loading, success, empty, delayed, or error path. +- No attempt to reproduce prototype-only behavior that the reviewed backend + cannot support truthfully. + +## Product And Identity Contract + +### Product Semantics + +The vNext model is: + +```text +Workflow draft + -> edit + -> run request + -> accepted or streaming execution + -> observed Activity record + -> immutable Run detail + -> optional Retry or Run again creates another Run +``` + +A draft is a first-class Workflow resource. Team and member creation are not +prerequisites for entering its editor. Publication and callable service +identity remain separate concerns and must not be inferred from authoring +identity. + +### Identifier Rules + +- `scopeId` comes directly from the route. Every scoped request passes it to a + server endpoint that validates access. Display names, cached selections, and + other IDs must never be used to reconstruct it. +- `workflowId` identifies a workspace draft or scope Workflow definition. It + is not a member ID and is not automatically callable. +- `definitionActorId` comes from `ScopeWorkflowDetail.source` and is the only + reviewed identity suitable for the Activity `definition` filter. +- `memberId` remains Team-member authority. The vNext draft routes do not + invent one or route through member APIs. +- `publishedServiceId` is a callable runtime identity. It must be supplied by + an explicit response or read model before a published Run action is enabled. +- `runId` identifies an immutable observed Run record. +- `stateVersion` is the authoritative projection version. The UI may use it to + detect fresh observation, but must never create or increment a local version. +- `acceptedCommandId`, `correlationId`, and `statusUrl` are command-receipt + facts. They indicate acceptance and observation targets, not completion. + +## Route And Navigation Model + +All vNext pages live below one new namespace: + +| Route | Purpose | Existing route impact | +| --- | --- | --- | +| `/scopes/:scopeId/workflow-activity-vnext` | Namespace entry; redirects only within the namespace to `workflows` | None | +| `/scopes/:scopeId/workflow-activity-vnext/workflows` | Workflow catalogue | None | +| `/scopes/:scopeId/workflow-activity-vnext/workflows/new` | Direct creation chooser | None | +| `/scopes/:scopeId/workflow-activity-vnext/workflows/:workflowId` | Draft editor and Run console | None | +| `/scopes/:scopeId/workflow-activity-vnext/activity` | Scope Activity ledger | None | +| `/scopes/:scopeId/workflow-activity-vnext/activity/:runId` | Immutable Run detail and recovery actions | None | +| `/scopes/:scopeId/workflow-activity-vnext/settings` | AI defaults, Account, and Advanced | None | + +Route requirements: + +- Declare the literal `workflows/new` route before the dynamic + `workflows/:workflowId` route. +- Set every vNext route to `hideInMenu: true`. Do not add or alter a global + menu item during the isolated implementation. +- Leave all existing redirects exactly as they are. The namespace entry may + redirect to its own `workflows` child only. +- Keep the existing authenticated app and runtime providers. Do not set + `layout: false`, because that would bypass established shell behavior. +- The new surface may hide the current ProLayout chrome through a narrowly + scoped display-route predicate that matches this prefix only. It must retain + authentication, React Query, runtime configuration, and error boundaries. +- Workflows, Activity, and Settings use a vNext-local navigation rail or mobile + switcher. All internal links preserve the route `scopeId`. +- Unknown Workflow and Run IDs render a scoped not-found state inside the + workbench and do not redirect to legacy pages. + +The first implementation is intentionally discoverable by direct URL only. +Adding an entry from an existing menu or Team page is a separate, explicit +product decision because it changes an existing navigation surface. + +## Primary Flow + +```mermaid +%%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%% +flowchart LR + A["Workflows catalogue"] --> B["New workflow"] + B --> C["Describe"] + B --> D["Start blank"] + B --> E["Import YAML"] + B --> F["Template"] + C --> G["Created draft"] + D --> G + E --> G + F --> G + G --> H["Connected-node editor"] + H --> I["Run dialog"] + I --> J["Accepted or running"] + J --> K["Activity observation"] + K --> L["Immutable Run detail"] + L --> M["Retry or Run again"] + M --> N["New accepted Run"] + N --> K +``` + +The transition from `Accepted or running` to `Activity observation` is +eventually consistent. An accepted HTTP or SSE request does not itself prove +that the Activity read model contains a Run. + +## Frame-To-Product Specification + +The Excalidraw frame order is the review order and acceptance checklist: + +| Frame | Required page or behavior | Backend-honest interpretation | +| --- | --- | --- | +| 01 Workflows - catalogue | Searchable, compact Workflow table with New workflow, Open, and conditional Run actions | Do not show Last Run, revision numbers, or callable state unless returned by an authoritative contract | +| 02 New workflow - direct creation | Describe, Start blank, Import YAML, and Template create Workflow drafts directly | Every successful path persists through the draft API before navigating to the editor | +| 03 Describe - generated Workflow draft | Description generates connected nodes and opens the common editor | Generation is not persistence; create the returned document as a draft, then navigate using the returned `workflowId` | +| 04 Start blank - empty Workflow draft | Empty connected-node canvas with Add node and Edit YAML | Publish and Run remain disabled until the current document passes real validation | +| 05 Import YAML - imported Workflow draft | YAML is parsed and validated before draft creation | Parse errors stay in the creation surface and do not create partial drafts | +| 06 Template - populated Workflow draft | A chosen template creates an independent editable draft | Templates are versioned frontend product content unless an authoritative catalogue already exists; they are never pretend backend records | +| 07 Run - unified execution dialog | Confirm target, input, connections, and external effects | Show only facts supported by the current document and capability preview; do not claim that Activity has already saved the Run | +| 08 Running draft - Studio canvas and Run console | Keep the editor context while streaming accepted/running output | Show `Accepted` or `Running`, then separately show `Observed in Activity` only after the observatory returns the Run | +| 09 Activity - filtered by Workflow | Enter Activity with a Workflow filter | Resolve `definitionActorId` from Workflow detail; never filter by display name or guessed actor ID | +| 10 Activity - all retained Runs | Newest-first scope ledger with server filters | The endpoint is bounded by `take`; call it recent retained Runs, never claim a complete lifetime total | +| 11 Run detail - immutable record | Input, output/error, diagnostics, steps, timeline, graph, statistics, and usage | Detail-level duration and usage are derived only from detail DTO fields; the source record is read-only | +| 12 Failed Run - recovery creates a new record | Explain failure and preview Retry or Run again | Fork returns a new accepted receipt; it never edits the source Run and durable lineage is not claimed | +| 13 Workflows and Activity - states | Loading, empty, errors, responsive reduction, and recovery | Every state keeps its primary next action and distinguishes no-data from unavailable-data | +| 14 Settings - AI defaults | Preferred service and Default model are the normal-state decisions | Reuse current LLM settings contracts and catalogue state rather than storing preferences locally | +| 15 Settings - save and recovery states | Dirty save bar, accepted observation, fallback, catalogue unavailable, and save error | A `202 Accepted` receipt is shown as confirming until the authoritative settings query observes the saved value | +| 16 Settings - Account | Identity, claims, authentication, session actions, and service access | Reuse `/api/auth/me` and existing auth actions; do not infer claims or expose credential material | +| 17 Settings - Advanced and responsive | One read-only effective runtime/request view plus tablet/mobile layouts | Reuse `/api/user-config/runtime`; no duplicate or editable representation of the same effective value | + +## Workflows Experience + +### Catalogue + +The desktop catalogue is a dense operational table, not a card gallery. Its +authoritative row union keeps draft and scope Workflow identities explicit. +The minimum columns are Workflow name, kind/state, last updated, and actions. +Search is client-side over the currently loaded authoritative fields. Filters +may distinguish draft and scope Workflow only when the adapter can do so from +their source DTOs. + +The page must not join Activity rows to Workflows by `workflowName`. The +observatory summary lacks a Workflow ID and names are neither unique nor +stable. Consequently, the Excalidraw `Last Run` cell is omitted until an +authoritative join key exists. + +`Open` always uses the row's real `workflowId`. `Run` follows this policy: + +- A validated draft may run through the reviewed scope draft-run endpoint. +- A published Workflow may run only if an explicit callable + `publishedServiceId` is available from an authoritative frontend contract. +- If no callable identity is available, keep `Open` enabled and render Run as + disabled with a concise reason. Never send `workflowId`, `serviceKey`, + `ActorId`, or `definitionActorId` where a service ID is required. + +### Direct Creation + +All four creation choices end in the same persisted draft and same editor: + +1. Describe calls `/api/workflows/generator`, validates or parses the generated + document as required, then creates the Workflow draft. +2. Start blank creates a minimal valid draft document with a generated display + name, then opens the empty Studio canvas. +3. Import YAML parses the user's YAML through `/api/editor/parse-yaml`; only a + successful parse can be submitted to the draft create endpoint. +4. Template copies a versioned bundled template into a new document. A + template ID is frontend product content, never reused as the new + `workflowId`. + +Creation buttons enter a stable pending state, reject duplicate submission, +and navigate only after the create response returns the persisted identity. +On failure, user input remains editable and the page offers retry. There is no +optimistic route built from a locally generated Workflow ID. + +### Common Editor + +The vNext editor reuses the behavior and visual language of: + +- `src/pages/team-member-workflow-studio/components/WorkflowStudioCanvas.tsx`; +- `WorkflowStudioNodeLibrary.tsx`; +- `WorkflowStudioNodeDetailPanel.tsx`; +- `WorkflowStudioYamlPanel.tsx`; +- `src/shared/graphs/GraphCanvas.tsx`. + +Reuse should happen at component or shared-state boundaries where the current +code is genuinely independent. The new route must not impersonate a Team, +create a fake member, or call the existing member route with a Workflow ID. +Any extraction must preserve the existing Team Member Workflow Studio API and +behavior. + +Canvas, node library, node configuration, YAML editor, Save, validation, and +Run all operate on one document state. Switching panels must not create a +second source of truth. Unsaved edits are explicit; leaving the editor prompts +only when real changes would be lost. + +## Run And Observation Experience + +### Unified Run Dialog + +The dialog identifies the current Workflow, uses `Current draft` when no +revision DTO exists, accepts the real Run input, and shows any verified +external-capability requirements. A single Run command is the primary action. + +The dialog does not say “saved in Activity” before observation. Its lifecycle +copy and actions are: + +```text +Ready -> Submitting -> Accepted/Running -> Observing -> Observed + | | + +-> Failed +-> Observation delayed +``` + +An execution failure and an observation delay are different states. A run may +have been accepted even if the Activity projection is not visible yet. + +### Accepted-To-Observed Reconciliation + +- Preserve receipt and stream facts in route-owned memory while the editor is + open. Session storage may support crash recovery only if existing run-session + helpers already define that non-authoritative behavior. +- Poll or invalidate the scope Activity query after acceptance with bounded + backoff. Compare server-provided `runId` and `stateVersion` only. +- Display `Observed in Activity` and enable the Run-detail link only when the + observatory returns the matching authoritative Run. +- If the execution response does not expose a Run ID that can be matched, show + the live console and a general `Open Activity` action, not a fabricated row. +- When the observation window expires, keep the accepted receipt visible and + label Activity as delayed. Do not reinterpret delay as execution failure. + +## Activity Experience + +### Ledger + +Activity is an immutable operational ledger. Desktop rows prioritize status, +Workflow name, Run ID, origin, started time, updated time, and the detail +action. Status, origin, definition, schedule, time range, and result limit map +to server query parameters. `scope=:scopeId` is sent when needed to retain the +route's explicit scope contract; the server remains the access authority. + +Supported UI status values map only from reviewed observatory values: + +- `running`; +- `completed`; +- `timed_out`; +- `failed`; +- `stopped`; +- `not_found`; +- `disabled`; +- `unknown`. + +Unknown values remain visible as Unknown and keep their raw value in an +accessible detail, rather than being coerced to success or failure. + +The list does not show duration, token usage, outcome text, source revision, +or linked Runs because `ObservatoryRunSummary` does not contain those facts. +It also does not expose a `Needs you` filter: step-level suspension and tool +approval data live in detail and do not form a stable list filter contract. + +### Workflow Filter + +Opening Activity from a Workflow resolves its +`ScopeWorkflowDetail.source.definitionActorId`, then sends that exact value as +the observatory `definition` filter. If the source is not available, the UI +opens unfiltered Activity and explains that the Workflow-specific filter is +unavailable. It must not substitute Workflow name or parse any actor string. + +### Immutable Run Detail + +Run detail loads summary/detail and graph independently so a graph failure does +not hide the committed Run record. It may display: + +- input, final output, and final error; +- diagnostics and recovery hints; +- step status, timing, request parameters, output preview, error, and usage; +- full timeline content, tool calls, and event data; +- graph nodes and edges using explicit node and step fields; +- total steps, completed steps, reply count, type counts, token totals, and + cost. + +The source record contains no edit, overwrite, or destructive action. Copy and +download actions may export data already visible to the caller, while +preserving existing secret-redaction rules. + +### Retry And Run Again + +Recovery calls `POST /api/workflow/runs/fork` and always creates a new Run. + +- Retry is enabled only when detail provides an unambiguous failed + `stepId`; that ID becomes `startAtStepId`. +- Run again is enabled only when the first executable step can be identified + unambiguously from returned detail or graph fields. +- The confirmation dialog shows source Run ID, chosen start step, input, and + any supported override fields. It never implies the old Run will change. +- A successful `202 Accepted` response shows `newRunActorId`, command ID, + correlation ID, and observation status. It then reconciles through Activity. +- The UI may keep the current fork receipt while mounted and offer navigation + to the new Run once observed. It must not use `localStorage` to manufacture a + durable source/child Run graph. + +## Settings Experience + +The Settings route has three local sections: AI defaults, Account, and +Advanced. Desktop uses a compact secondary navigation; mobile uses a select or +menu that preserves the same section labels and focus behavior. + +### AI Defaults + +Normal state contains two decisions: Preferred service and Default model. +Changing service refreshes the valid model choices. Dirty state alone reveals +the sticky Discard and Save changes bar. Save uses the existing LLM settings +contract and follows accepted-to-observed semantics; success appears only when +the authoritative query observes the selected values. + +Fallback, provider unavailable, and catalogue unavailable are exceptional +states with direct recovery actions. The UI must not silently select and save +a different provider or model. + +### Account + +Account reuses the authenticated session response and existing sign-in, +sign-out, and service-access actions. It may show user ID, roles, groups, +provider, scope, and expiry when present. It never renders bearer tokens, +credentials, or guessed claims. + +### Advanced + +Advanced renders one read-only set of effective runtime/request values from +the runtime settings contract. It avoids duplicate presentations of the same +base URL or runtime mode and offers copy affordances only for non-secret +values. This section has no save action. + +## Information Priority + +| Surface | Primary | Secondary | On demand or omitted | +| --- | --- | --- | --- | +| Workflows | Name, state/kind, Open, Run availability | Workflow ID, updated time | Raw YAML and source details in editor; Last Run omitted | +| Creation | Creation method, required input, create action | Validation and generated summary | Raw generated/parsed details after validation | +| Editor | Canvas, selected node, save state, Run | Node library, YAML, live Run console | Diagnostics and raw events in drawers/panels | +| Activity | Status, Workflow, Run ID, start/update time | Origin and active filters | Usage, duration, output, diagnostics in detail only | +| Run detail | Status, immutable identity, failure/output, step trace | Graph, usage, diagnostics | Raw timeline event data on expansion | +| Settings | Current decision or identity | Scope and effective values | Exceptional diagnostics only when relevant | + +Primary commands remain visible without scrolling on common desktop sizes. +Secondary information may collapse on mobile, but immutable identity and +recovery actions remain reachable. + +## Visual Brief + +Direction: **Operational Automation Ledger**. + +The interface should let operators scan Workflow definitions and immutable Run +records quickly, then move directly into editing or failure recovery. + +- Density: compact on desktop, balanced on mobile. +- Typography: use the existing `AlibabaSans` application family. Render Run + IDs, timestamps, versions, usage, and other dense data with the existing + monospace data style. +- Palette: dark navigation rail, white work surface, neutral gray dividers, + blue primary actions. Green, orange, red, and purple are reserved for status + meaning rather than decoration. +- Geometry: 4-6 px radii, 1 px borders, low or no shadow, stable table columns, + stable toolbars, and stable icon-button dimensions. +- Signature element: Activity rows read as a chronological immutable ledger; + the editor reads as a connected-node workbench. +- Motion: short state transitions only when they communicate panel or request + state. Respect `prefers-reduced-motion`. +- Avoid gradients, glass effects, decorative blobs, KPI card walls, nested + cards, oversized headings, marketing composition, and decorative animation. + +The PNGs are visual calibration references. Implementation should match their +hierarchy and density while using production tokens and real data states. + +## Responsive And Accessibility Contract + +### Desktop, At Least 1200 px + +- Fixed-width local navigation, flexible main work area, and optional detail + pane. +- Workflows and Activity use stable tables with persistent headers and no + hover-driven layout shift. +- The editor canvas stays full-bleed inside the workbench; node library and + node detail are functional side panels, not decorative cards. + +### Tablet, 768-1199 px + +- Local navigation compacts; tables drop low-priority columns before enabling + horizontal scrolling. +- Editor side panels overlay or use a mutually exclusive panel region. +- Settings navigation remains visible without nesting cards. + +### Mobile, Below 768 px + +- Preserve Workflow search/create/open, Activity status/identity/detail, Run + recovery, and Settings save/recovery. +- Each Activity row becomes a structured list row with status, Workflow, short + Run ID, and time. It remains one accessible link or has clearly separated + actions. +- The editor keeps Run, Add node, Edit YAML, Save, and Publish availability. + Node detail becomes a bottom sheet with a bounded height and a visible close + action. +- Sticky action areas account for safe-area insets and never cover focused + inputs or validation messages. +- Long IDs and unbroken error strings wrap or truncate with an accessible full + value; they never widen the viewport. + +### Accessibility + +- All primary paths are keyboard operable with visible focus. +- Icon-only buttons use existing icon components and accessible names; any + unfamiliar icon has a tooltip. +- Status is conveyed by text and semantics, not color alone. +- Loading announcements use appropriate live regions without repeatedly + announcing polling. +- Dialog focus is trapped and restored to the invoking control. Destructive or + external-effect confirmations identify the exact target. +- Tables retain semantic headers; responsive list rows retain equivalent + labels. +- Canvas nodes and side-panel controls expose an operable non-pointer path. + +## State Matrix + +| Surface | State | Required presentation | Primary recovery or next action | +| --- | --- | --- | --- | +| Workflows | Loading | Stable table skeleton and disabled row actions | Wait/cancel navigation remains possible | +| Workflows | Empty | No Workflows in this scope | New workflow | +| Workflows | Error | Inline error with no invented empty state | Retry | +| Workflows | Partial source failure | Identify unavailable draft or scope source | Retry failed source; keep valid rows | +| Creation | Ready | Four methods and method-specific inputs | Create draft | +| Creation | Invalid | Field/YAML error near its source | Correct input | +| Creation | Submitting | Stable pending button, duplicate submission blocked | Wait | +| Creation | Failure | Preserve user input and error | Retry | +| Editor | Loading | Stable canvas shell | Wait or return to catalogue | +| Editor | Empty draft | Empty canvas guidance | Add node or Edit YAML | +| Editor | Dirty | Visible unsaved state | Save or discard on leave | +| Editor | Invalid | Exact validation feedback; Run/Publish disabled | Fix document | +| Editor | Save accepted | Receipt or confirming status | Continue editing while observing | +| Run | Ready | Exact Workflow and input | Run | +| Run | Accepted/running | Receipt/live console, no Activity completion claim | Open Activity or stay | +| Run | Observation delayed | Accepted receipt plus projection-delay copy | Retry observation/Open Activity | +| Run | Submission failed | Safe error, editable input | Retry | +| Activity | Loading | Stable ledger skeleton | Wait | +| Activity | Empty | No observed Runs for active server filters | Clear filters or Run a Workflow | +| Activity | Error | Query failure distinct from empty | Retry | +| Activity | Unknown status | Neutral Unknown label plus raw value | Open detail | +| Run detail | Loading | Identity shell and independent detail/graph loading | Wait | +| Run detail | Not found | Scoped non-disclosure-safe not-found state | Back to Activity | +| Run detail | Running | Partial committed trace | Refresh through bounded polling | +| Run detail | Completed | Final output and immutable trace | Run again when eligible | +| Run detail | Failed | Error, diagnostic, failed step | Retry when eligible | +| Run detail | Graph unavailable | Keep all other detail visible | Retry graph | +| Fork | Ineligible | Disabled action with backend-fact reason | Inspect trace | +| Fork | Accepted | New Run receipt; source remains unchanged | Observe new Run | +| Fork | Failed | Source remains unchanged; inputs preserved | Retry request | +| Settings | Loading | Stable controls without default invention | Wait | +| Settings | Clean | Quiet current values | Edit | +| Settings | Dirty | Sticky Discard and Save changes | Save or discard | +| Settings | Save accepted | Confirming saved values | Continue observing | +| Settings | Save failed | Preserve dirty choices | Retry or discard | +| Settings | Catalogue unavailable | Existing choice remains visible | Retry catalogue | +| Account | Unauthenticated/expired | Clear session state | Sign in | +| Advanced | Unavailable | Read-only query error | Retry | + +## Interaction Contract + +- All list rows have one clear navigation target. Secondary actions do not + trigger row navigation through event bubbling. +- Search inputs debounce only network-backed filters. Client-side Workflow + search responds immediately and never changes source data. +- Server Activity filters are encoded in the URL query so refresh and back + navigation preserve operator context. Invalid values are ignored with a + visible reset path, not sent blindly. +- Query keys include authoritative `scopeId` and normalized filters. Changing + scope cancels or isolates in-flight results so one scope cannot flash in + another scope's workbench. +- Mutations disable duplicate submission and preserve a request receipt. They + do not optimistically synthesize authoritative IDs or projection versions. +- Save and Run failures keep user-authored input. A retry repeats only the + user's explicit action. +- `Open Activity` from a Workflow uses an authoritative definition filter when + available. `Back to Workflow` appears only when the Run response provides a + trustworthy association; name matching is insufficient. +- Browser back closes modal/detail layers before leaving the owning workbench + when those layers are URL-addressable. +- Copy actions report success or failure without changing layout. Export uses + structured serialization where available rather than string concatenation. + +## Backend Contract Mapping + +All mappings below were reviewed from `origin/feature/integrate` at +`72093afa4490adf6eb25d83c3db0b9c182cd6ff0`. The implementation may add or +extend frontend adapters, but may not change these backend contracts. + +### Workflow Drafts + +```text +GET /api/workspace/workflow-drafts?scopeId=:scopeId +POST /api/workspace/workflow-drafts?scopeId=:scopeId +GET /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +PUT /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +DELETE /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +``` + +The current `src/shared/studio/api.ts` already exposes +`listWorkflowDrafts`, `getWorkflowDraft`, `createWorkflowDraft`, +`updateWorkflowDraft`, `deleteWorkflowDraft`, `listWorkflows`, `getWorkflow`, +and `saveWorkflow`. Reuse those typed boundaries rather than issuing ad hoc +fetches from components. + +### Generation And Editing + +```text +POST /api/workflows/generator +POST /api/editor/parse-yaml +POST /api/editor/serialize-yaml +POST /api/editor/validate +POST /api/editor/normalize +``` + +Use `studioApi.authorWorkflow`, `parseYaml`, and `serializeYaml` where their +current typed contracts match. Validation and normalization remain explicit +server operations; UI success must not be based solely on client parsing. + +### Scope Workflows + +```text +GET /api/scopes/:scopeId/workflows +GET /api/scopes/:scopeId/workflows/:workflowId +PUT /api/scopes/:scopeId/workflows/:workflowId +POST /api/scopes/:scopeId/workflows:save-and-bind +``` + +`ScopeWorkflowDetail.source.definitionActorId` is the Activity definition +filter identity. `ScopeWorkflowSummary.workflowId`, `serviceKey`, `actorId`, +and `activeRevisionId` retain their own meanings and are not automatically +callable service IDs. + +### Draft Run + +```text +POST /api/scopes/:scopeId/workflow/draft-run +Accept: text/event-stream +``` + +Use `src/shared/api/runtimeRunsApi.ts` and its `streamDraftRun` adapter. A +successful connection or accepted command is not equivalent to an observed +Activity row. + +### Activity + +```text +GET /api/workflow/observatory/runs +GET /api/workflow/observatory/runs/:runId +GET /api/workflow/observatory/runs/:runId/graph +``` + +List filters reviewed from the endpoint are `scope`, `status`, `origin`, +`definition`, `schedule`, `from`, `to`, and `take`. Normal callers remain +limited to their own server-resolved scope; cross-scope access is enforced by +the backend. + +The list summary contains only `runId`, `workflowName`, `status`, `success`, +`startedAtUtc`, `updatedAtUtc`, `stateVersion`, `scopeId`, and `runOrigin`. +Detail adds input, final output/error, diagnostics, steps, timeline, +statistics, and usage totals. Graph is a separate query. + +### Run Fork + +```text +POST /api/workflow/runs/fork +``` + +The request includes `sourceRunId`, `startAtStepId`, and optional supported +YAML, sub-YAML, variable, input, command, and correlation overrides. The +`202 Accepted` response contains source Run ID, new Run actor ID, Workflow +name, accepted command ID, correlation ID, and status URL. It is a receipt, not +a completed Run detail. + +### Settings And Identity + +```text +GET /api/user-config/llm +PUT /api/user-config/llm +GET /api/user-config/runtime +GET /api/auth/me +``` + +Reuse existing Settings selection and save-observation behavior in +`src/pages/settings/`, especially `userLlmSelection.ts` and +`userLlmSaveObservation.ts`. Reuse existing auth/session actions rather than +implementing a parallel authentication flow. + +## Excalidraw-To-Backend Deviations + +These deviations are mandatory. They are not optional polish deferrals: + +| Excalidraw idea | Reviewed contract reality | Required frontend behavior | +| --- | --- | --- | +| `Current draft · revision N` | Workspace draft DTO has no historical revision number | Show `Current draft` and real updated time only | +| Revision on every Activity row | Summary has no revision field | Omit it | +| Duration, usage, and outcome in Activity rows | Summary has no such fields | Show them in detail only when returned | +| `Needs you` Activity filter | No stable list filter; suspension detail is per Run | Omit the filter; surface actionable detail inside Run detail | +| Last Run in Workflow catalogue | Activity summary has no Workflow ID join key | Omit Last Run; never join by Workflow name | +| Published Workflow Run inferred from catalogue IDs | Callable `publishedServiceId` is distinct | Disable published Run until explicit callable identity is available | +| Run is immediately “saved in Activity” | execution and projection are eventually consistent | Show Accepted/Running, then Observed only after query confirmation | +| Retry and Run again create persistent linked records | fork receipt exists, but Activity DTO has no durable lineage fields | Keep source immutable; show current receipt and observed new Run without claiming durable linkage | +| Run again always available | first executable step may be ambiguous | Enable only when an explicit first step is determinable | +| Retry always available on failure | failed step ID may be missing or ambiguous | Enable only with an explicit failed `stepId` | +| All retained Runs implies complete history | list is bounded by `take` | Label and present the returned recent window; do not display a fabricated total | +| Prototype uses `localStorage` for Runs and settings | backend endpoints are authoritative | Do not persist authoritative Runs or LLM defaults in browser storage | + +No backend work is proposed to close these gaps. If a future product decision +requires exact mockup parity, that must be scoped separately and must not be +smuggled into this frontend-only branch. + +## Proposed Frontend Structure + +The implementation should keep route ownership local and transport adaptation +shared: + +```text +apps/aevatar-console-web/ + config/routes.ts + src/pages/workflow-activity-vnext/ + index.tsx + WorkflowActivityVNextShell.tsx + navigation.ts + models.ts + workflows/ + WorkflowsPage.tsx + NewWorkflowPage.tsx + WorkflowEditorPage.tsx + workflowCreation.ts + activity/ + ActivityPage.tsx + RunDetailPage.tsx + runRecovery.ts + settings/ + SettingsPage.tsx + components/ + styles.ts + src/shared/api/ + workflowActivityApi.ts + workflowActivityApi.test.ts + src/shared/models/ + workflowActivity.ts + src/locales/ +``` + +This is a boundary proposal, not a mandate to create one file per label. Keep +view-only state near the page, reuse existing Studio and Settings helpers, and +put request construction/response decoding in typed shared adapters. Do not +duplicate existing APIs merely to give vNext new names. + +New user-facing copy must use the existing locale system. New icons come from +the enabled Ant Design icon set. The implementation should not add a UI or +graph dependency unless an existing capability is demonstrably insufficient. + +## Implementation Phases + +### Phase 0: Review And Contract Lock + +- Approve this specification and explicitly accept the backend-honest + deviations. +- Confirm the direct-URL-only initial entry and the namespace spelling. +- Recheck `origin/feature/integrate` before implementation in case its contract + commit has moved; record any change in this document before coding. + +### Phase 1: Isolated Shell And Typed Boundaries + +- Add new hidden routes without altering legacy routes or redirects. +- Add the vNext-local workbench shell and scoped navigation. +- Add typed Activity/fork decoders and query keys at shared boundaries. +- Prove route scope and identity isolation before building product pages. + +### Phase 2: Workflows And Direct Creation + +- Implement the authoritative Workflow catalogue. +- Implement Describe, blank, YAML import, and versioned template creation. +- Navigate only with the Workflow ID returned by draft persistence. + +### Phase 3: Common Editor And Run + +- Extract or reuse team-independent canvas and panel capabilities while + preserving existing Studio behavior. +- Implement document save/validate states and the unified Run dialog. +- Implement live Run console and accepted-to-observed reconciliation. + +### Phase 4: Activity And Recovery + +- Implement scoped server filters, immutable Run detail, and independent graph + loading. +- Implement conservative Retry/Run again eligibility and fork receipts. +- Verify source Run immutability across every recovery path. + +### Phase 5: Settings And Responsive Completion + +- Recompose current AI defaults, Account, and Advanced behavior. +- Complete desktop, tablet, and mobile layouts and keyboard operation. +- Apply realistic long IDs, errors, empty lists, unavailable catalogues, and + delayed observation data during visual verification. + +## Focused Testing Strategy + +Tests must protect observable contracts rather than internal implementation. +The expected high-value coverage is: + +- route/config integration proves all vNext URLs resolve, `new` is not captured + as a Workflow ID, and legacy routes/redirects are unchanged; +- Activity adapter tests prove query encoding, scope handling, response + decoding, unknown status preservation, detail/graph separation, and fork + receipt decoding; +- a Workflows route test proves each creation path persists a draft before + editor navigation and retains input on failure; +- an editor/Run route test proves Accepted/Running does not claim Activity + persistence and Observed appears only after an authoritative Run response; +- Activity route tests cover loading, empty, error, server filter changes, + immutable detail, and graph-only failure; +- recovery tests prove Retry requires a failed step, Run again requires a + first executable step, and the source record remains unchanged after fork; +- Settings route tests cover clean, dirty, accepted/confirming, observed, + catalogue unavailable, and save-failure recovery states. + +Use a small explicit set of colocated Jest files or individual test names. +Do not run the complete frontend suite, all jsdom tests, integration tests, +end-to-end tests, or smoke tests unless the user explicitly requests broader +verification in that implementation task. + +## Verification Commands + +For this documentation-only phase: + +```bash +bash tools/docs/lint.sh +git diff --check +``` + +For the later implementation, choose exact incremental Jest paths based on the +files actually changed, for example: + +```bash +pnpm --dir apps/aevatar-console-web exec jest \ + --runInBand \ + --runTestsByPath \ + src/shared/api/workflowActivityApi.test.ts \ + src/pages/workflow-activity-vnext/index.test.tsx +pnpm --dir apps/aevatar-console-web tsc +pnpm --dir apps/aevatar-console-web biome:lint +pnpm --dir apps/aevatar-console-web build +bash tools/docs/lint.sh +git diff --check +``` + +The final test file names must match the implementation. Route changes require +the production build. Biome should be narrowed to affected files when that +meaningfully validates the changed surface; use package lint when cross-cutting +scope makes a narrow command misleading. + +Before installing dependencies, starting the frontend, opening it for browser +QA, or running an integration-style command in a linked worktree, run: + +```bash +/usr/bin/python3 /Users/xiezixin/.codex/hooks/sync_worktree_env.py --cwd "$PWD" +``` + +Before OAuth browser verification, confirm without printing dotenv values that +the actual origin exactly matches both `NYXID_REDIRECT_URI` and +`AEVATAR_CONSOLE_FRONTEND_PORT` in scheme, host, and port. When another server +occupies the configured port, update both coupled values in this worktree to a +free accepted origin and leave the primary checkout unchanged. + +## Acceptance Criteria + +The later frontend implementation is complete only when all of the following +are true: + +- The diff changes no backend file and requires no backend deployment or + contract change. +- Production routes contain no mock fallback, demonstration rows, timer-made + API responses, or browser-storage substitute for unavailable remote state. +- API loading, empty, delayed, unavailable, decoding-failure, and request-error + states remain visibly distinct when tested without fixture fallbacks. +- All vNext pages resolve only under + `/scopes/:scopeId/workflow-activity-vnext`; old routes, redirects, menu + behavior, and legacy page behavior remain intact. +- The route `scopeId` is retained across navigation and sent only through + scope-validating backend contracts. +- A user can create a persisted draft with all four creation methods and enter + one common connected-node editor without creating a member. +- Workflow, member, definition actor, published service, and Run identities are + never inferred from one another. +- Run shows Accepted/Running separately from Activity observation and never + fabricates an Activity record. +- Activity filters use only supported server parameters and authoritative + definition identity. +- Activity list rows contain only summary facts; duration, usage, diagnostics, + and outputs appear only from Run detail. +- Run detail is immutable, and Retry/Run again always create a new accepted + Run with conservative eligibility. +- AI defaults, Account, and Advanced use current backend settings/auth + contracts and expose complete dirty, accepted, observed, error, and recovery + states. +- Desktop, tablet, and mobile layouts preserve primary actions, do not overlap + or horizontally escape, and remain keyboard accessible. +- The implementation task and pull request carry the baseline declaration from + `docs/design-baselines/workflow-activity-vnext/README.md`, including the + Excalidraw hash and the real-API-only data-source statement. +- Focused affected tests, TypeScript, affected lint, production build, docs + lint, and `git diff --check` pass. The final report lists exact commands and + explicitly states that the full frontend test suite was not run. From 19ddbdc32a6a2afb32186924df62a0d446645192 Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Tue, 4 Aug 2026 18:28:38 +0800 Subject: [PATCH 002/167] Document workflow activity vNext user paths --- apps/aevatar-console-web/AGENTS.md | 13 +- .../workflow-activity-vnext/README.md | 17 +- ...26-08-04-workflow-activity-vnext-design.md | 10 +- ...8-04-workflow-activity-vnext-user-paths.md | 779 ++++++++++++++++++ 4 files changed, 807 insertions(+), 12 deletions(-) create mode 100644 apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md diff --git a/apps/aevatar-console-web/AGENTS.md b/apps/aevatar-console-web/AGENTS.md index 4397e59fbb..8e82731c8b 100644 --- a/apps/aevatar-console-web/AGENTS.md +++ b/apps/aevatar-console-web/AGENTS.md @@ -137,15 +137,18 @@ pnpm --dir apps/aevatar-console-web build - Before changing any route, page, component, hook, query, adapter, model, style, locale, or test for - `/scopes/:scopeId/workflow-activity-vnext`, read both + `/scopes/:scopeId/workflow-activity-vnext`, read all three of these sources + completely: `docs/design-baselines/workflow-activity-vnext/README.md` and - `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md` - completely. + `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md` and + `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md`. - Treat `docs/design-baselines/workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw` as the primary visual, information-architecture, and interaction reference. Treat the design specification as the normative route, product identity, - API, state, and backend-compatibility contract. + API, state, and backend-compatibility contract. Treat the user-path + specification as the normative journey, decision, recovery, and completion + evidence contract. - The PNG and HTML prototypes are reference artifacts, not runtime data sources. Never copy their hard-coded records, `localStorage` persistence, timers, simulated receipts, or successful-looking defaults into production @@ -159,7 +162,7 @@ pnpm --dir apps/aevatar-console-web build - Include the design-baseline declaration from `docs/design-baselines/workflow-activity-vnext/README.md` in every vNext implementation task and pull request so the Excalidraw hash, contract - document, and real-API-only data-source rule are reviewable. + documents, user paths, and real-API-only data-source rule are reviewable. - Keep this feature frontend-only and isolated to its new route namespace. Do not change backend code or alter existing Workflow, Run, Settings, Studio, Team, member, redirect, or menu behavior to implement it. diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md index fb300d8c64..be5c481052 100644 --- a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md @@ -6,8 +6,10 @@ Status: **Normative for the Workflow Activity vNext frontend**. Any implementation or review of routes below `/scopes/:scopeId/workflow-activity-vnext` must read this directory together -with -[`2026-08-04-workflow-activity-vnext-design.md`](../../superpowers/specs/2026-08-04-workflow-activity-vnext-design.md). +with the +[`design specification`](../../superpowers/specs/2026-08-04-workflow-activity-vnext-design.md) +and +[`user paths`](../../superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md). Use the sources in this order: @@ -15,9 +17,11 @@ Use the sources in this order: information-architecture, and interaction reference. 2. The design specification is the normative product, route, identity, API, state, and backend-compatibility contract. -3. The PNG files are viewport references for individual states. -4. `prototype.html` is an interaction demonstration only. -5. `aevatar-workflow-activity-vnext.gen.py` is the source generator for these +3. The user-path specification is the normative end-to-end journey, decision, + recovery, and completion-evidence contract. +4. The PNG files are viewport references for individual states. +5. `prototype.html` is an interaction demonstration only. +6. `aevatar-workflow-activity-vnext.gen.py` is the source generator for these reference artifacts; it is not application runtime code. If the Excalidraw or prototype conflicts with a real API contract, follow the @@ -67,6 +71,9 @@ Design SHA-256: Contract specification: apps/aevatar-console-web/docs/superpowers/specs/ 2026-08-04-workflow-activity-vnext-design.md +User paths: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-user-paths.md Production data source: Real APIs and API-acknowledged user actions only; no mock fallback. ``` diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md index 30b85dd5ff..7bd4acd38c 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md @@ -11,7 +11,9 @@ Implementation branch: `feat/2026-08-04_workflow-activity-vnext`. After approval, this document is the normative product and engineering contract for the Workflow Activity vNext frontend. It must be read together with the in-repository design baseline at -`apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/`. +`apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/` and +the normative user paths in +`2026-08-04-workflow-activity-vnext-user-paths.md`. This phase changes documentation only. The eventual implementation is strictly frontend-only: no backend source, endpoint, DTO, projection, or persistence @@ -909,7 +911,11 @@ are true: or horizontally escape, and remain keyboard accessible. - The implementation task and pull request carry the baseline declaration from `docs/design-baselines/workflow-activity-vnext/README.md`, including the - Excalidraw hash and the real-API-only data-source statement. + Excalidraw hash, user-path specification, and the real-API-only data-source + statement. +- Every user path defined in + `2026-08-04-workflow-activity-vnext-user-paths.md` reaches its documented + completion evidence and recovery state without mock fallback data. - Focused affected tests, TypeScript, affected lint, production build, docs lint, and `git diff --check` pass. The final report lists exact commands and explicitly states that the full frontend test suite was not run. diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md new file mode 100644 index 0000000000..d492d2d72c --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md @@ -0,0 +1,779 @@ +# Workflow + Activity + Settings vNext User Paths + +## Status + +Proposed as the normative user-path companion to +`2026-08-04-workflow-activity-vnext-design.md` on 2026-08-04. + +This document defines how a scoped Aevatar Console user moves through the 17 +Excalidraw frames, which route owns each step, which real API fact advances the +journey, and what the UI must do when that fact is unavailable. It does not +authorize frontend implementation until the design package is approved. + +Implementation branch: `feat/2026-08-04_workflow-activity-vnext`. + +## Required Reading And Precedence + +Anyone implementing or reviewing these paths must read, in order: + +1. `apps/aevatar-console-web/docs/design-baselines/` + `workflow-activity-vnext/aevatar-workflow-activity-vnext.excalidraw` for + visual hierarchy, information architecture, and interaction intent. +2. `2026-08-04-workflow-activity-vnext-design.md` for routes, identity, API, + state, responsive behavior, deviations, and acceptance criteria. +3. This document for end-to-end user movement, decision points, recovery, and + path completion evidence. +4. The PNGs and `prototype.html` for visual and interaction calibration only. + +The backend contract in `origin/feature/integrate` at +`72093afa4490adf6eb25d83c3db0b9c182cd6ff0` remains authoritative for data. +The prototype's hard-coded objects, `localStorage`, timers, and simulated +receipts are never production data sources. + +## User And Job Model + +### Primary User + +The primary user is an authenticated automation builder or operator working +inside one authorized Aevatar `scopeId`. The same person may move between three +jobs without leaving the vNext workbench: + +- author a Workflow draft; +- run and inspect an automation; +- manage personal AI defaults and account/runtime context. + +The product does not require the user to understand actor prefixes, projection +internals, member bindings, or service-key conventions. Those facts remain +technical details unless a real backend response makes them useful for +diagnosis. + +### User Mental Model + +```text +Workflows = what I can create and edit +Run = execute the current valid Workflow +Activity = immutable records the system has actually observed +Settings = my AI defaults, identity, and effective runtime context +``` + +The user must never be led to believe that: + +- a generated Workflow has been saved before draft creation succeeds; +- an accepted Run is already visible in Activity; +- a failed API request means the collection is empty; +- Retry edits or replaces the failed Run; +- a local prototype record is a server record; +- a `workflowId`, `memberId`, `definitionActorId`, and + `publishedServiceId` are the same identity. + +## Entry, Navigation, And Exit + +### Entry Contract + +The isolated preview entry is a direct authenticated URL: + +```text +/scopes/:scopeId/workflow-activity-vnext +``` + +It redirects only within the new namespace to: + +```text +/scopes/:scopeId/workflow-activity-vnext/workflows +``` + +There is no new global menu entry in the first implementation. Existing menu, +Team, Workflow, Run, Settings, Studio, login, callback, and redirect routes do +not change. + +If authentication is missing or expired, existing authentication behavior owns +the interruption and return. If the server rejects the route `scopeId`, the +workbench renders the existing access/not-found behavior. It must not switch to +a cached scope or show sample data. + +### Local Navigation + +The vNext shell has three stable destinations: + +| Destination | Route | User job | +| --- | --- | --- | +| Workflows | `/scopes/:scopeId/workflow-activity-vnext/workflows` | Create, open, edit, and run Workflows | +| Activity | `/scopes/:scopeId/workflow-activity-vnext/activity` | Scan and inspect observed Runs | +| Settings | `/scopes/:scopeId/workflow-activity-vnext/settings` | Manage AI defaults and inspect identity/runtime context | + +Every link carries the route `scopeId` unchanged. Switching local sections does +not rewrite it from a display name, remembered selection, or API response. + +### Exit Contract + +- Back navigation returns to the previous route without rewriting legacy + history. +- Leaving a dirty editor or dirty Settings form requires a loss-of-changes + decision. +- Leaving an accepted or running Run does not cancel it unless the real API + exposes and the user explicitly invokes a supported cancel action. +- Returning later reloads authoritative API state. It does not reconstruct a + successful screen from prototype or browser-storage records. + +## Path Map + +```mermaid +%%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%% +flowchart TD + A["Authenticated scoped entry"] --> B["Workflows catalogue"] + B --> C["New workflow"] + C --> D["Describe"] + C --> E["Start blank"] + C --> F["Import YAML"] + C --> G["Template"] + D --> H["Persisted Workflow draft"] + E --> H + F --> H + G --> H + B --> I["Open existing Workflow"] + H --> J["Connected-node editor"] + I --> J + J --> K["Save and validate"] + K --> L["Unified Run dialog"] + L --> M["Accepted or running"] + M --> N["Observed Activity row"] + B --> O["Activity filtered by Workflow"] + N --> P["Immutable Run detail"] + O --> P + P --> Q["Retry failed Run"] + P --> R["Run again"] + Q --> S["New accepted Run"] + R --> S + S --> N + A --> T["Settings"] + T --> U["AI defaults"] + T --> V["Account"] + T --> W["Advanced"] +``` + +## Path Inventory + +| ID | Path | Excalidraw frames | Completion evidence | +| --- | --- | --- | --- | +| UP-00 | Enter the scoped workbench | 01, 13 | Real scoped queries begin under the route `scopeId` | +| UP-01 | Browse and search Workflows | 01, 13 | Authoritative catalogue rows or a truthful empty/error state | +| UP-02 | Create by Describe | 02, 03 | Draft-create response returns the `workflowId` used by the editor route | +| UP-03 | Start blank and add the first node | 02, 04 | Persisted draft opens; valid document state controls Save/Run/Publish | +| UP-04 | Import YAML | 02, 05 | Server parse/validation succeeds before draft creation | +| UP-05 | Create from Template | 02, 06 | Bundled template is copied into a newly persisted independent draft | +| UP-06 | Open, edit, validate, and save | 03-06, 08 | Authoritative draft update succeeds; unsaved state clears | +| UP-07 | Run a current draft | 07, 08 | Real draft-run stream/receipt reaches Accepted or Running | +| UP-08 | Observe a Run in Activity | 08-10, 13 | Observatory returns an authoritative `runId` and `stateVersion` | +| UP-09 | Filter Activity by Workflow | 09 | Exact `definitionActorId` is applied, or the filter is honestly unavailable | +| UP-10 | Inspect immutable Run detail | 11 | Detail response renders committed facts; graph may load independently | +| UP-11 | Retry a failed Run | 12 | Fork API accepts an explicit failed `stepId`; source Run remains unchanged | +| UP-12 | Run a completed Workflow again | 11, 12 | Fork API accepts an explicit first execution step; source remains unchanged | +| UP-13 | Change AI defaults | 14, 15 | Authoritative settings query observes the selected service and model | +| UP-14 | Inspect Account and session | 16 | `/api/auth/me` or existing auth state renders real identity facts | +| UP-15 | Inspect Advanced runtime values | 17 | `/api/user-config/runtime` returns the read-only effective values | +| UP-16 | Complete key jobs on tablet/mobile | 13, 17 | Same API and identity evidence is reachable without desktop-only controls | + +## UP-00: Enter The Scoped Workbench + +**Intent:** Reach the isolated vNext experience without affecting existing +console routes. + +**Start:** An authenticated user has an authorized `scopeId` and opens the +namespace URL directly. + +**Steps:** + +1. The existing application shell authenticates the user and retains current + providers. +2. The namespace entry redirects to the vNext Workflows route only. +3. The local Workflows, Activity, and Settings navigation appears. +4. Workflows begins its real scoped queries. + +**Completion:** The URL contains the original `scopeId`, and the page shows a +loading, populated, empty, partial-source, or error state derived from real +requests. + +**Recovery:** Unauthorized, forbidden, missing-scope, and network failure stay +distinct. No alternate scope or demonstration catalogue is substituted. + +## UP-01: Browse And Search Workflows + +**Intent:** Find an existing Workflow or start creating one. + +**Route:** +`/scopes/:scopeId/workflow-activity-vnext/workflows`. + +**Authoritative reads:** + +```text +GET /api/workspace/workflow-drafts?scopeId=:scopeId +GET /api/scopes/:scopeId/workflows +``` + +The existing typed `studioApi.listWorkflows` boundary may coordinate these +sources. The UI keeps draft and scope Workflow source types explicit. + +**Steps:** + +1. The user scans Workflow name, source/state, updated time, and available + actions. +2. Search filters only the currently returned authoritative fields. +3. `Open` navigates using that row's real `workflowId`. +4. `New workflow` navigates to the creation chooser. +5. `Run` is enabled for a validated runnable draft. Published Run is enabled + only when an explicit callable `publishedServiceId` is available. + +**Completion:** The selected row opens or the user reaches creation. A +successful empty response produces the New workflow empty state. + +**Not allowed:** + +- Do not show Last Run by joining Activity on `workflowName`. +- Do not invent revision numbers or published availability. +- Do not use sample rows when one catalogue source fails. Preserve valid rows + from the successful source and identify the failed source. + +## UP-02: Create A Workflow By Description + +**Intent:** Turn a natural-language automation goal into an editable Workflow +draft. + +**Frames:** 02 New workflow and 03 Describe generated draft. + +**Route transition:** + +```text +.../workflows/new + -> .../workflows/:workflowId +``` + +**Steps and real evidence:** + +1. The user chooses `Describe` and enters a concrete goal. +2. Submit calls `POST /api/workflows/generator` through the existing typed + authoring boundary. +3. The generated Workflow is parsed, normalized, or validated through the + reviewed editor endpoints as required by its response form. +4. The user sees a generated summary or correctable validation problem. +5. Confirming creation calls + `POST /api/workspace/workflow-drafts?scopeId=:scopeId`. +6. Only the draft-create response establishes the real `workflowId`. +7. The router opens that ID in the common connected-node editor. + +**Completion:** The editor loads the persisted draft and shows the generated +nodes and edges. Generation success alone is not completion. + +**Recovery:** Generator, parser, validator, and draft-create failures preserve +the user's description and any safe generated preview. Retry repeats only the +failed explicit action. No local ID or demonstration draft is routed as saved. + +## UP-03: Start Blank And Add The First Node + +**Intent:** Build a Workflow manually without a generated starting structure. + +**Frames:** 02 New workflow and 04 Start blank. + +**Steps:** + +1. The user chooses `Start blank`. +2. The frontend creates a minimal, well-formed Workflow document as explicit + frontend authoring content. +3. The draft-create API persists it and returns its `workflowId`. +4. The editor opens an empty connected-node canvas. +5. `Add node` opens the searchable Node library. +6. Choosing a node adds it to the shared document state. +7. Selecting the node opens Node configuration; `Edit YAML` opens the same + document through the YAML panel. +8. Validation controls Save, Run, and Publish availability. + +**Completion:** At least one meaningful node is configured and the user can +save a valid authoritative draft. + +**Recovery:** An empty draft may remain saved, but Run and Publish stay +disabled when the real validator says it is not executable. The UI does not +insert a sample node and call the draft valid. + +## UP-04: Import YAML + +**Intent:** Create a Workflow from YAML the user already owns. + +**Frames:** 02 New workflow and 05 Import YAML. + +**Steps:** + +1. The user chooses `Import YAML` and enters or pastes YAML. +2. Submit calls `POST /api/editor/parse-yaml`. +3. Validation or normalization runs through the reviewed editor contracts when + needed. +4. Parse and validation feedback is shown against the YAML source. +5. Only a successful document can be submitted to the draft-create API. +6. The returned `workflowId` opens the common editor with parsed nodes and + edges. + +**Completion:** The editor represents the same authoritative document in +canvas and YAML modes. + +**Recovery:** Invalid YAML never creates a partial draft. Network or decoder +failure is not relabeled as invalid YAML or empty content. User input remains +available for correction and retry. + +## UP-05: Create From A Template + +**Intent:** Begin from a known useful structure while creating an independent +Workflow. + +**Frames:** 02 New workflow and 06 Template. + +**Steps:** + +1. The user chooses `Template`. +2. The UI identifies templates as bundled frontend product content unless a + real template API is introduced and reviewed later. +3. The user selects and previews a versioned template. +4. Its Workflow document is copied, parsed, and validated. +5. Draft creation persists a new resource and returns a new `workflowId`. +6. The common editor opens the copied nodes and edges. + +**Completion:** Editing the new draft cannot mutate the bundled template or +another user's Workflow. + +**Not allowed:** The UI must not render bundled templates as “loaded from the +server,” attach fake server timestamps, or reuse the template ID as a +`workflowId`. + +## UP-06: Open, Edit, Validate, Save, And Publish Boundary + +**Intent:** Change one Workflow through a single connected-node authoring +model. + +**Route:** +`/scopes/:scopeId/workflow-activity-vnext/workflows/:workflowId`. + +**Authoritative operations:** + +```text +GET /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +PUT /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +POST /api/editor/parse-yaml +POST /api/editor/serialize-yaml +POST /api/editor/validate +POST /api/editor/normalize +``` + +**Steps:** + +1. The route loads the exact Workflow under the route scope. +2. Canvas, Node library, Node configuration, and YAML panel edit one document + state. +3. A node or YAML change marks the draft dirty. +4. Validation exposes exact actionable problems and controls executable + actions. +5. Save serializes and updates the authoritative draft. +6. Save success clears dirty state only after the real update succeeds. +7. Leaving while dirty prompts Save, Discard, or Stay. + +**Publish boundary:** The Excalidraw retains a Publish control because the +existing Studio supports publication/binding behavior. Publication must use +the reviewed scope Workflow/save-and-bind contracts and explicit capability +confirmations. It is not a prerequisite for draft Run, and it must not invent a +`publishedServiceId` after save acceptance. + +**Completion:** The saved document can be reloaded from the API with the +expected Workflow identity and content. + +**Identity guard:** This path never creates a fake Team/member, never sends +`workflowId` to a member endpoint, and never assumes publication makes the +Workflow callable through the same ID. + +## UP-07: Run A Current Draft + +**Intent:** Execute the current valid Workflow without leaving the editor. + +**Frames:** 07 Unified Run dialog and 08 Running draft. + +**Authoritative execution:** + +```text +POST /api/scopes/:scopeId/workflow/draft-run +Accept: text/event-stream +``` + +**Steps:** + +1. The user selects `Run` from the catalogue or editor. +2. The unified dialog identifies the exact Workflow and says `Current draft` + when no real revision number exists. +3. The user reviews input, connections, and verified external effects. +4. Required explicit capability confirmations must be derived from the real + preview/validation contract. +5. Submit serializes the current valid document and starts + `runtimeRunsApi.streamDraftRun`. +6. The button becomes stable and duplicate submission is blocked. +7. Real stream/receipt facts move the UI into Accepted or Running. +8. The editor remains visible and the Run console opens without replacing the + current document. + +**Completion:** A real stream or command receipt proves execution acceptance. +It does not yet prove an Activity row exists. + +**Recovery:** Submission failure keeps the input and returns to an actionable +dialog. A disconnected stream shows the last authoritative receipt/event and a +reconnect or Activity action; it does not fabricate completion. + +## UP-08: Observe A Run In Activity + +**Intent:** Move from live execution to an immutable observed record. + +**Frames:** 08 Running draft, 09 filtered Activity, 10 all retained Runs, and +13 states. + +```mermaid +%%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%% +flowchart LR + A["Run submitted"] --> B["Real stream or receipt accepted"] + B --> C["Live console running"] + B --> D["Query scoped Activity"] + D --> E{"Matching authoritative Run observed?"} + E -->|"No"| F["Observation delayed"] + F --> D + E -->|"Yes"| G["Observed with runId and stateVersion"] + G --> H["Enable immutable Run detail link"] +``` + +**Authoritative read:** + +```text +GET /api/workflow/observatory/runs +``` + +with supported `scope`, `status`, `origin`, `definition`, `schedule`, `from`, +`to`, and `take` parameters. + +**Steps:** + +1. Acceptance invalidates or polls the correctly keyed scope Activity query + with bounded backoff. +2. While no matching authoritative record exists, the UI says `Observing` or + `Activity delayed`. +3. When observatory returns the Run, its `runId` and `stateVersion` establish + the immutable Activity identity. +4. Only then does the UI say `Observed in Activity` and enable the detail link. +5. If the execution response exposes no trustworthy match key, the UI offers + general `Open Activity` rather than guessing a row. + +**Completion:** An actual observatory summary is visible in Activity. + +**Not allowed:** A timer, local array, `localStorage`, stream completion, or +HTTP success status cannot insert an Activity row or invent a `stateVersion`. + +## UP-09: Filter Activity By Workflow + +**Intent:** Review observed executions belonging to one Workflow definition. + +**Start:** The user invokes `View Activity` from a Workflow context. + +**Steps:** + +1. The frontend resolves the scope Workflow detail through + `GET /api/scopes/:scopeId/workflows/:workflowId`. +2. Only `ScopeWorkflowDetail.source.definitionActorId` supplies the + observatory `definition` filter. +3. The Activity route encodes the supported filter in its query string so + refresh/back navigation preserves it. +4. The ledger shows only the server-filtered response window. + +**Completion:** The active filter is visible, removable, and backed by the +exact definition identity. + +**Recovery:** A draft-only Workflow may not have a definition actor. In that +case the user enters unfiltered Activity with a concise filter-unavailable +message. The UI does not substitute `workflowId`, Workflow name, `serviceKey`, +or a parsed actor prefix. + +## UP-10: Inspect Immutable Run Detail + +**Intent:** Understand what happened in one observed Run. + +**Route:** +`/scopes/:scopeId/workflow-activity-vnext/activity/:runId`. + +**Authoritative reads:** + +```text +GET /api/workflow/observatory/runs/:runId +GET /api/workflow/observatory/runs/:runId/graph +``` + +**Steps:** + +1. Selecting an Activity row opens its real `runId`. +2. Detail and graph load independently. +3. The page prioritizes status, identity, final output/error, diagnostics, and + step trace. +4. The user may expand input, request parameters, full timeline content, tool + calls, statistics, and usage. +5. Copy/export actions operate only on returned, permitted data. + +**Completion:** The user can identify success/failure and the relevant step +without altering the record. + +**Recovery:** A graph failure leaves the committed detail visible. A missing +or cross-scope Run renders the server-safe not-found state. Partial/running +detail is labeled partial and refreshed through bounded observation. + +**Not allowed:** No edit, overwrite, delete, local usage calculation, or fake +source/child Run timeline appears on the source record. + +## UP-11: Retry A Failed Run + +**Intent:** Continue from an unambiguous failed step while preserving the +failed Run as evidence. + +**Frame:** 12 Failed Run recovery. + +**Authoritative command:** + +```text +POST /api/workflow/runs/fork +``` + +**Steps:** + +1. Run detail identifies a failed step from the returned step/diagnostic data. +2. `Retry` is enabled only when one explicit `stepId` is a valid recovery + start. +3. The confirmation dialog shows source Run ID, start step, input, and + supported overrides. +4. Confirm submits `sourceRunId` and `startAtStepId`. +5. A real `202 Accepted` response shows the accepted command ID, correlation + ID, new Run actor ID, and status URL as receipt facts. +6. The source Run remains on screen and unchanged. +7. The new execution follows UP-08 until an authoritative Activity `runId` is + observed. + +**Completion:** The user can open the newly observed Run; the failed source +still shows its original status and trace. + +**Recovery:** If no exact failed step exists, Retry stays disabled with a +reason. A failed fork request preserves the confirmation input. The frontend +does not use `newRunActorId` as a Run-detail route unless an authoritative +contract establishes the corresponding `runId`. + +## UP-12: Run A Completed Workflow Again + +**Intent:** Create a new execution from the beginning of an earlier successful +Run. + +**Steps:** + +1. Detail/graph data must identify the first executable step unambiguously. +2. `Run again` opens the same immutable-source confirmation model as Retry. +3. The user reviews the source Run, first step, input, and supported overrides. +4. Fork submission returns a real accepted receipt. +5. The new execution follows UP-08; the original success record remains + unchanged. + +**Completion:** A new authoritative Activity Run is observed. + +**Recovery:** If the first execution step is missing or ambiguous, Run again +stays disabled. The UI does not pick the first array item, parse a graph node +ID, or pretend a new linked record exists before observation. + +## UP-13: Change AI Defaults + +**Intent:** Choose the user's preferred connected AI service and default model. + +**Frames:** 14 AI defaults and 15 save/recovery states. + +**Authoritative operations:** + +```text +GET /api/user-config/llm +PUT /api/user-config/llm +``` + +**Steps:** + +1. Settings opens AI defaults and loads the current real selection and + available connected-service/model catalogue through existing adapters. +2. Changing Preferred service updates valid model choices without saving. +3. Changing either value makes the page dirty and reveals the sticky Discard + and Save changes bar. +4. Discard restores the last authoritative values. +5. Save sends the selected intent and receives an accepted receipt. +6. The UI says `Confirming saved values`, not Saved. +7. Existing save-observation logic requeries settings until the selected + service/model is authoritative. +8. Only observation clears dirty/confirming state and shows saved success. + +**Completion:** A fresh GET returns the chosen values. + +**Recovery:** Save failure preserves dirty values. Catalogue unavailable keeps +the last real selection visible and disables invalid changes. Fallback is +explained rather than silently saved. `localStorage` cannot be the settings +authority. + +## UP-14: Inspect Account And Session + +**Intent:** Understand who is signed in and manage the existing session or +service access. + +**Frame:** 16 Account. + +**Authoritative identity:** + +```text +GET /api/auth/me +``` + +**Steps:** + +1. The user opens Account from local Settings navigation. +2. The page shows returned identity, user ID, roles, groups, provider, scope, + and expiry only when present. +3. Sign in, Sign out, and Manage service access reuse current application auth + actions. +4. Any navigation/confirmation follows those existing auth contracts. + +**Completion:** The UI reflects the current real session state. + +**Recovery:** Expired or unavailable auth is shown explicitly. The page never +uses the prototype's `signed-in` browser flag, invents claims, or renders bearer +or credential material. + +## UP-15: Inspect Advanced Runtime Values + +**Intent:** Diagnose the effective runtime/request context without editing it. + +**Frame:** 17 Advanced and responsive. + +**Authoritative read:** + +```text +GET /api/user-config/runtime +``` + +**Steps:** + +1. The user opens Advanced. +2. One read-only representation shows the effective runtime mode and request + values returned by the API. +3. Permitted non-secret values may be copied. + +**Completion:** The user can inspect the active real configuration without a +save action. + +**Recovery:** An unavailable runtime query shows retry. It does not substitute +build-time defaults and label them as the user's effective server state. + +## UP-16: Complete Key Jobs On Tablet And Mobile + +**Intent:** Preserve the same product semantics on constrained screens. + +**Frames:** 13 responsive Workflows/Activity and 17 responsive Settings, plus +the mobile editor references in the baseline directory. + +**Required mobile paths:** + +- Workflows: search, New workflow, Open, and eligible Run remain reachable. +- Creation: all four methods remain usable; YAML and validation errors remain + associated with their inputs. +- Editor: Run, Add node, Edit YAML, Save, and Publish availability remain + visible. Node configuration uses an accessible bottom sheet. +- Activity: status, Workflow, short Run ID, time, filters, and detail access + remain available without desktop-only hover. +- Run detail: failure, output, step trace, Retry, and Run again eligibility + remain accessible. +- Settings: section navigation, dirty save bar, save observation, Account + actions, and Advanced values remain operable. + +**Completion:** Mobile and desktop reach the same authoritative end states. +Responsive layout changes presentation, never API source, identity, recovery, +or completion evidence. + +## Cross-Path Truth Checkpoints + +| Checkpoint | The UI may say | Required evidence | The UI must not say | +| --- | --- | --- | --- | +| Workflow generated | Generated preview ready | Real generator response | Saved | +| Workflow created | Draft created | Draft-create response with `workflowId` | Created from a local ID | +| Workflow saved | Saved | Real draft update success and clean document | Saved after timer-only feedback | +| Run submitted | Submitting | User action in progress | Running | +| Run accepted | Accepted/Running | Real SSE event or accepted receipt | Observed in Activity | +| Activity observed | Observed | Observatory summary with `runId` and `stateVersion` | Observed from local insertion | +| Retry accepted | New Run accepted | Real fork receipt | Linked history persisted | +| New Run observed | New Run available | Observatory returns authoritative `runId` | Detail route from guessed actor ID | +| Settings accepted | Confirming saved values | Real PUT receipt | Saved | +| Settings saved | Saved | Real GET observes selected values | Saved from `localStorage` | +| Empty collection | No records | Successful API response with zero records | Empty after request failure | + +## Failure And Recovery Paths + +| Failure | Preserve | Show | Recovery | +| --- | --- | --- | --- | +| Workflow catalogue request fails | Successful source rows and active search | Failed source or page error | Retry failed query | +| Generator fails | User description | Generator error | Retry generation | +| YAML parse fails | YAML input | Exact parse feedback | Edit and parse again | +| Draft create fails | Creation inputs/preview | Create error | Retry create | +| Draft save fails | Dirty document | Save error | Retry or continue editing | +| Run submission fails | Input and document | Submission error | Retry explicit Run | +| Run stream disconnects | Last real receipt/event | Disconnected or unknown status | Reconnect or open Activity | +| Activity observation is delayed | Accepted receipt | Delayed projection state | Bounded retry/open Activity | +| Activity list fails | Current filters | Request error, not empty | Retry | +| Run graph fails | Run detail | Graph-only error | Retry graph | +| Retry/Run again fails | Immutable source and overrides | Fork error | Retry command | +| Settings catalogue fails | Last real selection | Catalogue unavailable | Retry catalogue | +| Settings save fails | Dirty selection | Save error | Retry or discard | +| Auth expires | Non-secret local editing state when safe | Session expired | Existing sign-in flow | + +## Excalidraw Coverage + +Every frame has a corresponding user path: + +| Frame | Covered by | +| --- | --- | +| 01 Workflows catalogue | UP-00, UP-01 | +| 02 New workflow direct creation | UP-02, UP-03, UP-04, UP-05 | +| 03 Describe generated draft | UP-02, UP-06 | +| 04 Blank draft | UP-03, UP-06 | +| 05 Import YAML draft | UP-04, UP-06 | +| 06 Template draft | UP-05, UP-06 | +| 07 Unified Run dialog | UP-07 | +| 08 Running draft and Run console | UP-07, UP-08 | +| 09 Activity filtered by Workflow | UP-09 | +| 10 All retained Runs | UP-08 | +| 11 Immutable Run detail | UP-10, UP-12 | +| 12 Failed Run recovery | UP-11, UP-12 | +| 13 Workflows and Activity states | UP-00, UP-01, UP-08, UP-16 | +| 14 Settings AI defaults | UP-13 | +| 15 Settings save/recovery | UP-13 | +| 16 Settings Account | UP-14 | +| 17 Settings Advanced/responsive | UP-15, UP-16 | + +## User-Path Acceptance Criteria + +- Every `UP-*` path can be traced from its start route to its completion + evidence without leaving the new namespace, except existing authentication + and service-access actions. +- All four creation methods converge on a real persisted draft and the same + connected-node editor. +- A real returned `workflowId` is required before editor navigation. +- Draft Run reaches Accepted/Running before it can reach Activity observation. +- Activity observation requires a real observatory `runId` and `stateVersion`. +- Workflow-specific Activity filtering uses only an exact returned + `definitionActorId`. +- Run detail remains immutable in success, failure, partial graph, Retry, and + Run again paths. +- Retry and Run again require explicit step identity and create a new accepted + execution. +- Settings Save remains confirming until authoritative readback observes the + selected values. +- API pending, empty, delayed, unavailable, decoding failure, and request + failure remain distinct in every path. +- No production path imports mock fixtures, inserts demonstration rows, reads + prototype Activity/settings from browser storage, or uses timers to simulate + authoritative API progress. +- Desktop, tablet, and mobile use the same identities, APIs, and completion + evidence. +- Existing routes, redirects, menu behavior, backend files, and backend + contracts remain unchanged. From 9909a7e32e79f0e3a58bb21fe2588eb86c156676 Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Tue, 4 Aug 2026 18:38:24 +0800 Subject: [PATCH 003/167] Clarify vNext auth and localization reuse --- apps/aevatar-console-web/AGENTS.md | 13 +++ .../workflow-activity-vnext/README.md | 35 ++++++ ...26-08-04-workflow-activity-vnext-design.md | 103 +++++++++++++++++- ...8-04-workflow-activity-vnext-user-paths.md | 91 ++++++++++++---- 4 files changed, 217 insertions(+), 25 deletions(-) diff --git a/apps/aevatar-console-web/AGENTS.md b/apps/aevatar-console-web/AGENTS.md index 8e82731c8b..30a1e7961d 100644 --- a/apps/aevatar-console-web/AGENTS.md +++ b/apps/aevatar-console-web/AGENTS.md @@ -159,6 +159,19 @@ pnpm --dir apps/aevatar-console-web build or fixture fallback data. - Keep mock and fixture data in clearly named test-only files. Production routes, components, hooks, queries, and API adapters must not import them. +- Reuse the existing protected-route, `/login`, `/auth/callback`, + `NyxIDAuthClient`, sanitized `returnTo`, session restoration, sign-in, + sign-out, and service-access review behavior. Do not create a vNext auth + route, provider, callback, token cache, session store, or identity fallback. +- Reuse the existing Umi locale configuration, `ConsoleLanguageSwitch`, + `getLocale`/`setLocale`, message helpers, and `en-US`/`zh-CN` catalogues. Add + every new vNext message to both catalogues; do not create a vNext locale + context, storage key, or hard-coded visible-copy path. +- Login, callback, language, and account presentation may adopt the vNext + visual system, but auth, redirect, callback, session, language, persistence, + error, and accessibility behavior must remain unchanged. If vNext hides the + global header, reuse its existing language/account actions inside the local + shell rather than cloning their logic. - Include the design-baseline declaration from `docs/design-baselines/workflow-activity-vnext/README.md` in every vNext implementation task and pull request so the Excalidraw hash, contract diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md index be5c481052..c130b2c57a 100644 --- a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md @@ -56,6 +56,38 @@ hard-coded data look as though it came from an API. without a server. Those mechanisms have no API or persistence authority and must not be copied into the production implementation. +## Existing Authentication And Localization Contract + +Workflow Activity vNext reuses Aevatar Console authentication and +internationalization behavior. It does not create a vNext-specific auth or +locale system. + +- Protected-route handling continues through the current app runtime, + `ProtectedRouteRedirectGate`, `/login`, `/auth/callback`, + `NyxIDAuthClient`, sanitized `returnTo`, stored/restorable sessions, and + existing sign-in, sign-out, callback recovery, and service-access review. +- The user must return to the original scoped vNext URL after successful login + through the existing `redirect`/`returnTo` behavior. No second callback, + token cache, session store, or auth provider is allowed. +- Language behavior continues through the Umi locale plugin, + `ConsoleLanguageSwitch`, `setLocale`, `t`/`useIntl`, and the existing + `en-US` and `zh-CN` catalogues. New copy must be added to both catalogues. +- If the vNext shell hides the current global chrome, it must reuse the existing + language and account actions inside its own presentation. It must not clone + their state or behavior. +- Login, callback, language, and account controls may receive a visual-only + treatment matching the Operational Automation Ledger direction. Route + names, redirects, auth protocol, return behavior, session lifecycle, locale + keys, language persistence, error recovery, and accessibility behavior stay + unchanged. + +The existing Login page currently uses a decorative gradient and large rounded +card. A later visual implementation may replace those presentation choices +with the vNext dark rail/white work surface, neutral borders, 4-6 px radii, +compact typography, and restrained status colors. That visual change must not +alter what starts login, how callback completion works, or where the user +returns. + ## Required Development And Review Declaration Every implementation task and pull request for this vNext route should include @@ -74,6 +106,9 @@ Contract specification: User paths: apps/aevatar-console-web/docs/superpowers/specs/ 2026-08-04-workflow-activity-vnext-user-paths.md +Authentication and localization: + Existing Aevatar login, callback, session, returnTo, and Umi locale logic; + presentation may change, behavior may not. Production data source: Real APIs and API-acknowledged user actions only; no mock fallback. ``` diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md index 7bd4acd38c..2d57453536 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md @@ -86,6 +86,9 @@ cached, or hard-coded data appear to be an API result. latency, and state versions throughout the UI. - Reframe current AI defaults, Account, and Advanced settings into the new design without changing their backend behavior. +- Reuse the existing Aevatar authentication, return-to, session, service-access + review, and internationalization logic while styling their presentation to + match the vNext visual direction. - Provide complete loading, empty, error, disabled, accepted, observing, observed, and recovery states on desktop and mobile. @@ -93,9 +96,11 @@ cached, or hard-coded data appear to be an API result. - No backend modification, new endpoint, DTO enrichment, projection change, migration, or persistence change. -- No replacement, redirect, behavior change, or visual rewrite of existing +- No replacement, redirect, or behavior change of existing `/runtime/workflows`, `/runtime/runs`, `/settings`, `/studio`, Team, member, - or authentication routes. + login, callback, or authentication routes. Login, callback, language, and + account presentation may adopt the vNext visual system without changing + their behavior. - No global navigation change in the first implementation. The vNext routes remain hidden from the existing menu and are entered by their explicit URL. - No assumption that `memberId`, `workflowId`, `definitionActorId`, or @@ -184,6 +189,85 @@ The first implementation is intentionally discoverable by direct URL only. Adding an entry from an existing menu or Team page is a separate, explicit product decision because it changes an existing navigation surface. +## Authentication And Localization Reuse Contract + +### Existing Login Path + +The vNext namespace is protected by the existing application runtime. It must +not introduce another login page, callback, guard, token cache, or session +provider. + +The preserved flow is: + +```text +Protected vNext URL + -> ProtectedRouteRedirectGate + -> /login?redirect= + -> NyxIDAuthClient.loginWithRedirect({ returnTo }) + -> /auth/callback + -> NyxIDAuthClient.handleRedirectCallback() + -> window.location.replace(result.returnTo) + -> original protected vNext URL +``` + +The implementation reuses these current boundaries: + +- `src/app.tsx` public-route and auth-session bootstrap behavior; +- `src/shared/auth/ProtectedRouteRedirectGate.tsx`; +- `src/pages/login/index.tsx`; +- `src/pages/auth/callback/index.tsx`; +- `src/shared/auth/client.ts`, `config.ts`, and `session.ts`; +- current Account sign-in, sign-out, and service-access review actions. + +`sanitizeReturnTo` remains the authority for the redirect target. Restorable +session handling continues through `hasRestorableAuthSession` and +`ensureActiveAuthSession`. Login configuration errors, callback errors, +service-access review errors, retry, and back actions retain their current +logic and security behavior. + +### Login And Auth Presentation + +Login, callback progress/error, language, and account controls should look like +the new workbench while remaining the same functional surfaces. + +- Replace the current decorative gradient, oversized radius, and high card + shadow with the Operational Automation Ledger palette, neutral borders, + 4-6 px radii, compact type, and restrained status treatment. +- Keep Aevatar as the first-viewport brand signal and retain the existing + NyxID action, pending state, configuration error, and callback recovery + content. +- Do not add a marketing hero, new credential form, new auth choice, or + vNext-only callback route. +- Preserve keyboard access, visible focus, pending button behavior, live error + announcements, and reduced-motion expectations. +- A visual-only auth change must remain compatible with every existing route + that uses `/login` and `/auth/callback`, not only the vNext return path. + +### Existing Internationalization Path + +The vNext workbench reuses the configured Umi locale system: + +- `config/config.ts` remains the locale configuration authority; +- `getLocale`/`setLocale` and `ConsoleLanguageSwitch` retain language selection + and persistence behavior; +- `normalizeConsoleLocale`, `resolveAntdLocale`, and `resolveProIntl` continue + to supply the Ant Design and Pro Components locales; +- `t`, `T`, `ConsoleMessage`, and `useIntl` remain the message formatting + paths; +- `src/locales/en-US.ts`, `zh-CN.ts`, and their project-message catalogues own + all new vNext copy. + +The supported product locales remain `en-US` and `zh-CN`. Every new message ID +must exist in both catalogues and preserve interpolation variables. New pages +must not use hard-coded visible copy, create a vNext locale context, write a +second locale storage key, or infer language from scope/user data. + +If the vNext display route hides ProLayout header actions, the vNext-local +shell must render the existing `ConsoleLanguageSwitch` and existing account +action behavior in a style-compatible location. Reuse may be through the +current components or a presentation extraction that leaves their public +behavior and existing consumers intact. + ## Primary Flow ```mermaid @@ -814,6 +898,13 @@ The expected high-value coverage is: - route/config integration proves all vNext URLs resolve, `new` is not captured as a Workflow ID, and legacy routes/redirects are unchanged; +- auth-route integration proves an unauthenticated vNext URL uses the current + sanitized Login redirect and callback return path, while existing Login, + callback, retry, service-access review, and other protected routes retain + their behavior; +- localization coverage proves new message IDs exist in `en-US` and `zh-CN`, + language switching uses the existing locale system, and the vNext shell does + not own a second locale state; - Activity adapter tests prove query encoding, scope handling, response decoding, unknown status preservation, detail/graph separation, and fork receipt decoding; @@ -890,6 +981,14 @@ are true: - All vNext pages resolve only under `/scopes/:scopeId/workflow-activity-vnext`; old routes, redirects, menu behavior, and legacy page behavior remain intact. +- Unauthenticated vNext entry uses the existing protected-route redirect, + `/login`, `/auth/callback`, sanitized `returnTo`, session restoration, and + service-access review behavior, returning to the original scoped URL. +- Login, callback, language, and account controls use the vNext visual system + without changing their shared behavior for any existing route. +- Every vNext message exists in `en-US` and `zh-CN`, and language selection + continues through the existing Umi locale and `ConsoleLanguageSwitch` + behavior without a second locale state or storage key. - The route `scopeId` is retained across navigation and sent only through scope-validating backend contracts. - A user can create a persisted draft with all four creation methods and enter diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md index d492d2d72c..15aa916db5 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md @@ -120,8 +120,14 @@ not rewrite it from a display name, remembered selection, or API response. ```mermaid %%{init: {"maxTextSize": 100000, "flowchart": {"useMaxWidth": false, "nodeSpacing": 10, "rankSpacing": 50}, "themeVariables": {"fontSize": "10px"}}}%% flowchart TD - A["Authenticated scoped entry"] --> B["Workflows catalogue"] - B --> C["New workflow"] + A["Open protected scoped URL"] --> A1{"Existing session usable?"} + A1 -->|"Yes"| B["vNext scoped entry"] + A1 -->|"No"| A2["Existing Login route"] + A2 --> A3["Existing NyxID redirect"] + A3 --> A4["Existing Auth callback"] + A4 --> B + B --> B1["Workflows catalogue"] + B1 --> C["New workflow"] C --> D["Describe"] C --> E["Start blank"] C --> F["Import YAML"] @@ -130,14 +136,14 @@ flowchart TD E --> H F --> H G --> H - B --> I["Open existing Workflow"] + B1 --> I["Open existing Workflow"] H --> J["Connected-node editor"] I --> J J --> K["Save and validate"] K --> L["Unified Run dialog"] L --> M["Accepted or running"] M --> N["Observed Activity row"] - B --> O["Activity filtered by Workflow"] + B1 --> O["Activity filtered by Workflow"] N --> P["Immutable Run detail"] O --> P P --> Q["Retry failed Run"] @@ -145,7 +151,7 @@ flowchart TD Q --> S["New accepted Run"] R --> S S --> N - A --> T["Settings"] + B --> T["Settings"] T --> U["AI defaults"] T --> V["Account"] T --> W["Advanced"] @@ -155,7 +161,7 @@ flowchart TD | ID | Path | Excalidraw frames | Completion evidence | | --- | --- | --- | --- | -| UP-00 | Enter the scoped workbench | 01, 13 | Real scoped queries begin under the route `scopeId` | +| UP-00 | Authenticate and enter the scoped workbench | 01, 13 | Existing callback returns to the original vNext URL and real scoped queries begin | | UP-01 | Browse and search Workflows | 01, 13 | Authoritative catalogue rows or a truthful empty/error state | | UP-02 | Create by Describe | 02, 03 | Draft-create response returns the `workflowId` used by the editor route | | UP-03 | Start blank and add the first node | 02, 04 | Persisted draft opens; valid document state controls Save/Run/Publish | @@ -173,28 +179,56 @@ flowchart TD | UP-15 | Inspect Advanced runtime values | 17 | `/api/user-config/runtime` returns the read-only effective values | | UP-16 | Complete key jobs on tablet/mobile | 13, 17 | Same API and identity evidence is reachable without desktop-only controls | -## UP-00: Enter The Scoped Workbench +## UP-00: Authenticate And Enter The Scoped Workbench -**Intent:** Reach the isolated vNext experience without affecting existing -console routes. +**Intent:** Reach the isolated vNext experience through Aevatar's existing +login, callback, session, return-to, and locale behavior without affecting +other console routes. -**Start:** An authenticated user has an authorized `scopeId` and opens the -namespace URL directly. +**Start:** An authenticated or unauthenticated user opens the direct namespace +URL for an authorized `scopeId`. **Steps:** -1. The existing application shell authenticates the user and retains current - providers. -2. The namespace entry redirects to the vNext Workflows route only. -3. The local Workflows, Activity, and Settings navigation appears. -4. Workflows begins its real scoped queries. - -**Completion:** The URL contains the original `scopeId`, and the page shows a -loading, populated, empty, partial-source, or error state derived from real -requests. - -**Recovery:** Unauthorized, forbidden, missing-scope, and network failure stay -distinct. No alternate scope or demonstration catalogue is substituted. +1. The existing app runtime classifies `/login` and `/auth/callback` as public + and the vNext URL as protected. +2. If a stored or restorable session exists, current + `AuthSessionBootstrap`/`ensureActiveAuthSession` behavior restores it. +3. Without a usable session, `ProtectedRouteRedirectGate` sends the complete + scoped path, search, and hash through `sanitizeReturnTo` into the existing + `/login?redirect=...` route. +4. The existing Login page calls + `NyxIDAuthClient.loginWithRedirect({ returnTo })`. +5. NyxID returns through the unchanged `/auth/callback` route. +6. The existing callback calls `handleRedirectCallback()` and replaces the + location with its sanitized `result.returnTo`. +7. The user returns to the original vNext URL. Only the namespace root + redirects within the new namespace to Workflows; a requested child route + retains its own path, query, and hash. +8. The local Workflows, Activity, and Settings navigation appears. +9. Workflows begins its real scoped queries. +10. Language and account controls reuse `ConsoleLanguageSwitch` and existing + account behavior, even if their placement/style changes in the vNext shell. + +**Completion:** The callback returns to the original scoped URL, the existing +session is active, the selected `en-US` or `zh-CN` locale is preserved, and the +page shows a loading, populated, empty, partial-source, or error state derived +from real requests. + +**Recovery:** Login configuration, callback, service-access review, +unauthorized, forbidden, missing-scope, and network failures retain their +current distinct retry/back behavior. No alternate scope, second login flow, +mock user, or demonstration catalogue is substituted. + +**Presentation:** Login, callback, language, and account controls adopt vNext +tokens, density, border, radius, and status styling only. Authentication, +return, session, language switching, language persistence, and error behavior +remain the existing Aevatar logic. + +The Excalidraw contains no separate Login frame. Login presentation therefore +inherits only the approved vNext visual direction; it must not introduce new +auth decisions, fields, providers, or navigation that the design does not +define. ## UP-01: Browse And Search Workflows @@ -686,6 +720,8 @@ the mobile editor references in the baseline directory. remain accessible. - Settings: section navigation, dirty save bar, save observation, Account actions, and Advanced values remain operable. +- Login and callback: the existing NyxID flow remains usable at mobile width, + and the existing language switch remains reachable without desktop chrome. **Completion:** Mobile and desktop reach the same authoritative end states. Responsive layout changes presentation, never API source, identity, recovery, @@ -725,6 +761,8 @@ or completion evidence. | Settings catalogue fails | Last real selection | Catalogue unavailable | Retry catalogue | | Settings save fails | Dirty selection | Save error | Retry or discard | | Auth expires | Non-secret local editing state when safe | Session expired | Existing sign-in flow | +| Login/callback fails | Sanitized return target and safe error reason | Existing callback recovery | Retry existing NyxID flow/back to Login | +| Locale catalogue/key is missing | Current locale selection | Explicit development/test failure | Fix both catalogues; never use vNext-local fallback copy | ## Excalidraw Coverage @@ -755,6 +793,13 @@ Every frame has a corresponding user path: - Every `UP-*` path can be traced from its start route to its completion evidence without leaving the new namespace, except existing authentication and service-access actions. +- UP-00 uses the existing protected-route guard, Login, NyxID callback, + sanitized `returnTo`, session restoration, and locale behavior; only their + presentation may change. +- Login/callback styling remains compatible with all existing protected routes, + not only the vNext namespace. +- Every vNext message ID exists in both `en-US` and `zh-CN`, and language + switching uses the existing `ConsoleLanguageSwitch`/Umi locale state. - All four creation methods converge on a real persisted draft and the same connected-node editor. - A real returned `workflowId` is required before editor navigation. From 9fec4318789e8b216dd7c0a81d971dd99b8e7e9c Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Tue, 4 Aug 2026 18:53:02 +0800 Subject: [PATCH 004/167] Harden vNext design baseline verification --- apps/aevatar-console-web/AGENTS.md | 5 ++ .../workflow-activity-vnext/README.md | 55 ++++++++++--- .../verify-baseline.py | 80 +++++++++++++++++++ ...26-08-04-workflow-activity-vnext-design.md | 1 + 4 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py diff --git a/apps/aevatar-console-web/AGENTS.md b/apps/aevatar-console-web/AGENTS.md index 30a1e7961d..bc24c7ea26 100644 --- a/apps/aevatar-console-web/AGENTS.md +++ b/apps/aevatar-console-web/AGENTS.md @@ -176,6 +176,11 @@ pnpm --dir apps/aevatar-console-web build `docs/design-baselines/workflow-activity-vnext/README.md` in every vNext implementation task and pull request so the Excalidraw hash, contract documents, user paths, and real-API-only data-source rule are reviewable. +- Run + `python3 docs/design-baselines/workflow-activity-vnext/verify-baseline.py` + from `apps/aevatar-console-web/` before and after changing the baseline. The + verifier must confirm the declared hash, deterministic generator output, and + exact 17-frame inventory. - Keep this feature frontend-only and isolated to its new route namespace. Do not change backend code or alter existing Workflow, Run, Settings, Studio, Team, member, redirect, or menu behavior to implement it. diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md index c130b2c57a..5aec505925 100644 --- a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md @@ -111,6 +111,9 @@ Authentication and localization: presentation may change, behavior may not. Production data source: Real APIs and API-acknowledged user actions only; no mock fallback. +Baseline integrity: + python3 apps/aevatar-console-web/docs/design-baselines/ + workflow-activity-vnext/verify-baseline.py ``` This declaration makes design drift and mock-data substitution explicit review @@ -118,12 +121,13 @@ failures rather than undocumented implementation choices. ## Imported Source -这个目录是根据 2026-08-03 会议结论重新整理的独立版本。`/Users/xiezixin/Downloads/mocks/` 下的原始画板和生成器没有修改。 +这个目录是根据 2026-08-03 会议结论重新整理的独立版本。`/Users/xiezixin/Downloads/mocks/` 下的原始画板、生成器、HTML 和 PNG 作为来源资产逐字节导入,没有修改。仓库额外增加的 `README.md` 和 `verify-baseline.py` 负责说明规范优先级并验证基线完整性,不属于原始原型资产。 ## 文件 - `aevatar-workflow-activity-vnext.excalidraw`:合并后的 Excalidraw,包含 Workflows、Activity 与 Settings,共 17 个 frame。 - `aevatar-workflow-activity-vnext.gen.py`:画板生成器,内含画框边界、ID 唯一性和废弃术语检查。 +- `verify-baseline.py`:仓库内校验器,验证画板 SHA-256、生成器确定性输出和 17 个 frame 的精确清单。 - `prototype.html`:可直接在浏览器打开的交互原型,不需要安装依赖或启动服务。 - `prototype-workflows.png`:Workflows 桌面视图截图。 - `prototype-activity.png`:Activity 桌面视图截图。 @@ -158,15 +162,24 @@ failures rather than undocumented implementation choices. 原型因此统一采用“节点画布 + 连线 + 按需侧面板”的编辑模式。Describe、Start blank、Import YAML 和 Template 只负责产生不同的初始 Workflow 文档,不再各自发明编辑行为。 -## 新语义 +## 原型意图与强制偏差 + +下面的原型意图只用于理解视觉和交互方向。它不覆盖本文前面的 +Production Data Truth Rule,也不覆盖设计规范中的 +`Excalidraw-To-Backend Deviations`。后续实现必须应用这些强制偏差: - 用户从 Workflows 直接创建 Workflow 草稿,没有其他资源的创建前置步骤。 - `Run` 是唯一的常规执行入口。 -- 每次 Run 一开始就创建 Activity 记录,包括当前草稿修订的执行。 -- Activity 记录明确显示 `Current draft · revision N` 或 `Published · vN`。 -- Retry 和 Run again 会创建新记录并链接来源 Run;原记录不被覆盖。 -- Activity 菜单是唯一的 Run 历史入口;需要查看某个 Workflow 时,在 Activity 中应用 Workflow 筛选。 -- 全局导航包含 Workflows、Activity 和 Settings;Settings 是明确新增的配置区域,不改变 Workflow / Activity 的执行语义。 +- Run 请求开始后先显示真实的 Accepted/Running 状态;只有 Observatory + 返回权威记录后,才能显示为 Activity 记录。 +- `Current draft · revision N`、`Published · vN`、来源、耗时、用量和 + lineage 都是画板中的视觉意图;对应字段没有出现在真实 API 响应时必须省略。 +- Retry 和 Run again 不修改原 Run,并请求创建新 Run;只有真实 receipt + 或后续查询提供的关联事实才能显示新旧 Run 的关系。 +- Activity 是 vNext 内唯一的 Run 历史入口;按 Workflow 筛选只使用权威 + `definitionActorId`,不能按名称或其他 ID 猜测。 +- Workflows、Activity 和 Settings 是隔离 vNext shell 的本地导航。第一期 + 不修改现有全局菜单或旧路由。 ## Excalidraw 阅读顺序 @@ -178,11 +191,11 @@ failures rather than undocumented implementation choices. 4. `04 Start blank - empty Workflow draft`:真实 Studio 的空画布;添加第一个节点之前不能 Publish 或 Run。 5. `05 Import YAML - imported Workflow draft`:YAML 中的名称和节点类型进入同一套 Studio 画布。 6. `06 Template - populated Workflow draft`:模板创建独立草稿,并以连接节点显示。 -7. `07 Run - unified execution dialog`:确认修订、输入、连接和外部影响;底部说明本次 Run 会保存在 Activity。 -8. `08 Running draft - Studio canvas and Run console`:仍在同一个节点画布中显示运行状态,底部打开 Run console;Activity 记录已经存在。 +7. `07 Run - unified execution dialog`:确认修订、输入、连接和外部影响;底部文案表达保留本次 Run 的产品意图,生产实现仍需等待真实 Activity 观察结果。 +8. `08 Running draft - Studio canvas and Run console`:仍在同一个节点画布中显示运行状态,底部打开 Run console;画板中的 Activity 记录只表达目标状态,生产实现不得在权威查询返回前声称记录已存在。 9. `09 Activity - filtered by Workflow`:从 Activity 菜单进入,并用 Workflow 筛选历史。 10. `10 Activity - all retained Runs`:全局、最新优先的所有 Run 记录。 -11. `11 Run detail - immutable record`:Run 的修订、来源、耗时、用量、步骤时间线与关联记录。 +11. `11 Run detail - immutable record`:展示 Run 详情的信息层级;修订、来源、耗时、用量、步骤时间线与关联记录仅在真实详情响应提供对应字段时显示。 12. `12 Failed Run - recovery creates a new record`:失败解释和恢复;Retry 预览明确展示新旧 Run 的关系。 13. `13 Workflows and Activity - states`:空、加载、错误和移动端信息优先级。 14. `14 Settings - AI defaults`:正常态只保留 Preferred service 与 Default model 两个决策。 @@ -198,13 +211,17 @@ Workflows -> direct draft creation -> edit connected nodes in Workflow Studio -> Run - -> Activity record created immediately + -> Accepted / Running from a real stream or receipt + -> Activity record observed through the real API -> Run detail - -> Retry / Run again creates a linked record + -> Retry / Run again requests a new Run without mutating the source ``` ## 原型操作路径 +以下步骤只用于操作独立原型。步骤中的 `localStorage`、计时器、示例用户和 +示例记录不是 API 行为,也不是生产实现要求。 + 1. 打开 `prototype.html`,默认进入 Workflows。 2. 点击 `New workflow`,分别检查四种方式:描述会生成匹配节点;空白会进入画布空状态;导入会先校验 YAML;模板会先要求选择模板。 3. 在画布点击节点,右侧打开 Node configuration;`Add node` 从左侧打开 Node library;`Edit YAML` 从右侧打开 YAML 面板。 @@ -234,4 +251,16 @@ Settings 的功能来源仍然是当前 Aevatar Console,但信息架构已经 - `Account`:当前浏览器身份、User ID、roles、groups、会话到期时间、NyxID provider、scope、Sign in / Sign out 和 Manage service access;字段本身足够明确,不再重复配小节说明。 - `Advanced`:只保留一份 effective request values。Runtime mode 与 base URL 不再同时出现在只读输入和 raw values 两处。 -AI 默认值会保存在浏览器 `localStorage`。选择 exact connected service 时,模型列表会跟着服务切换。默认状态保持安静;只有产生修改后,sticky save bar 才出现。保存动作先进入 accepted / confirming saved values,再更新已保存 service。fallback、catalog unavailable 与 provider unavailable 只在异常状态出现。Account 的 Sign in、Sign out 与 service access 也有可见状态变化。 +独立原型会把 AI 默认值保存在浏览器 `localStorage`,仅用于演示重开页面后的交互连续性。生产实现必须通过真实 Settings API 读取、保存并观察 AI 默认值,不能读取这份原型存储。选择 exact connected service 时,模型列表会跟着服务切换。默认状态保持安静;只有产生修改后,sticky save bar 才出现。保存动作先进入 accepted / confirming saved values,再根据权威查询更新已保存 service。fallback、catalog unavailable 与 provider unavailable 只在异常状态出现。Account 的 Sign in、Sign out 与 service access 继续复用现有真实逻辑。 + +## 基线完整性校验 + +从仓库根目录运行: + +```bash +python3 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py +``` + +校验器会在临时目录运行导入的生成器,不修改仓库文件,并验证生成结果与 +主画板逐字节一致、SHA-256 与声明一致,以及 17 个 frame 的名称和顺序 +完整。任何失败都表示设计基线或声明已经漂移,必须在实现或评审前处理。 diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py new file mode 100644 index 0000000000..b7da1ea342 --- /dev/null +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Verify the immutable Workflow Activity vNext design baseline.""" + +from __future__ import annotations + +import hashlib +import json +import shutil +import subprocess +import tempfile +from pathlib import Path + + +BASELINE_DIR = Path(__file__).resolve().parent +BOARD_NAME = "aevatar-workflow-activity-vnext.excalidraw" +GENERATOR_NAME = "aevatar-workflow-activity-vnext.gen.py" +EXPECTED_SHA256 = "30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de" +EXPECTED_FRAMES = ( + "01 Workflows - catalogue", + "02 New workflow - direct creation", + "03 Describe - generated Workflow draft", + "04 Start blank - empty Workflow draft", + "05 Import YAML - imported Workflow draft", + "06 Template - populated Workflow draft", + "07 Run - unified execution dialog", + "08 Running draft - Studio canvas and Run console", + "09 Activity - filtered by Workflow", + "10 Activity - all retained Runs", + "11 Run detail - immutable record", + "12 Failed Run - recovery creates a new record", + "13 Workflows and Activity - states", + "14 Settings - AI defaults", + "15 Settings - save and recovery states", + "16 Settings - Account", + "17 Settings - Advanced and responsive", +) + + +def main() -> None: + board_path = BASELINE_DIR / BOARD_NAME + generator_path = BASELINE_DIR / GENERATOR_NAME + board_bytes = board_path.read_bytes() + + actual_sha256 = hashlib.sha256(board_bytes).hexdigest() + if actual_sha256 != EXPECTED_SHA256: + raise SystemExit( + f"design SHA-256 mismatch: expected {EXPECTED_SHA256}, got {actual_sha256}" + ) + + document = json.loads(board_bytes) + frame_names = tuple( + element["name"] + for element in document["elements"] + if element.get("type") == "frame" + ) + if frame_names != EXPECTED_FRAMES: + raise SystemExit( + "frame inventory mismatch:\n" + f"expected {EXPECTED_FRAMES!r}\n" + f"got {frame_names!r}" + ) + + with tempfile.TemporaryDirectory(prefix="aevatar-vnext-baseline-") as temp_dir: + generated_dir = Path(temp_dir) + generated_script = generated_dir / GENERATOR_NAME + shutil.copyfile(generator_path, generated_script) + subprocess.run(["python3", str(generated_script)], check=True, capture_output=True) + generated_bytes = (generated_dir / BOARD_NAME).read_bytes() + + if generated_bytes != board_bytes: + raise SystemExit("generator output does not match the committed Excalidraw") + + print(f"design SHA-256: {actual_sha256}") + print(f"frames: {len(frame_names)}/{len(EXPECTED_FRAMES)}") + print("generator output: byte-identical") + print("workflow activity vNext baseline: PASS") + + +if __name__ == "__main__": + main() diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md index 2d57453536..c5d8ec74ea 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md @@ -929,6 +929,7 @@ verification in that implementation task. For this documentation-only phase: ```bash +python3 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py bash tools/docs/lint.sh git diff --check ``` From 8bfac95df0afdc957fc080eb31da9afed2dd2415 Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Tue, 4 Aug 2026 18:58:41 +0800 Subject: [PATCH 005/167] Document vNext draft materialization paths --- .../workflow-activity-vnext/README.md | 2 +- ...26-08-04-workflow-activity-vnext-design.md | 82 ++++++++++++++----- ...8-04-workflow-activity-vnext-user-paths.md | 82 +++++++++++++++---- 3 files changed, 129 insertions(+), 37 deletions(-) diff --git a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md index 5aec505925..b832663271 100644 --- a/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md +++ b/apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/README.md @@ -121,7 +121,7 @@ failures rather than undocumented implementation choices. ## Imported Source -这个目录是根据 2026-08-03 会议结论重新整理的独立版本。`/Users/xiezixin/Downloads/mocks/` 下的原始画板、生成器、HTML 和 PNG 作为来源资产逐字节导入,没有修改。仓库额外增加的 `README.md` 和 `verify-baseline.py` 负责说明规范优先级并验证基线完整性,不属于原始原型资产。 +这个目录是根据 2026-08-03 会议结论重新整理的独立版本。用户提供的原始画板、生成器、HTML 和 PNG 作为来源资产逐字节导入,没有修改。仓库额外增加的 `README.md` 和 `verify-baseline.py` 负责说明规范优先级并验证基线完整性,不属于原始原型资产。仓库内副本是后续实现与评审的唯一可移植基准;本机来源路径不构成依赖。 ## 文件 diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md index c5d8ec74ea..d0d972e3f2 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md @@ -41,10 +41,9 @@ hierarchy, while the backend contract controls what the UI may claim, filter, persist, or execute. The frontend must degrade honestly rather than inventing data to make a mockup appear complete. -The source package was imported from -`/Users/xiezixin/Downloads/mocks/workflow-activity-vnext/`. The repository copy -is the reviewable branch artifact and must be used by later implementation -work; the Downloads path is provenance only. +The user-provided source package was imported byte-for-byte. The repository +copy is the reviewable, portable branch artifact and must be used by later +implementation work; no developer-local source path is a dependency. ## Production Data Truth Rule @@ -304,7 +303,7 @@ The Excalidraw frame order is the review order and acceptance checklist: | --- | --- | --- | | 01 Workflows - catalogue | Searchable, compact Workflow table with New workflow, Open, and conditional Run actions | Do not show Last Run, revision numbers, or callable state unless returned by an authoritative contract | | 02 New workflow - direct creation | Describe, Start blank, Import YAML, and Template create Workflow drafts directly | Every successful path persists through the draft API before navigating to the editor | -| 03 Describe - generated Workflow draft | Description generates connected nodes and opens the common editor | Generation is not persistence; create the returned document as a draft, then navigate using the returned `workflowId` | +| 03 Describe - generated Workflow draft | Description generates connected nodes and opens the common editor | Generation is not persistence; create the returned document as a draft, observe it until readable when creation returns `202`, then navigate using the returned `workflowId` | | 04 Start blank - empty Workflow draft | Empty connected-node canvas with Add node and Edit YAML | Publish and Run remain disabled until the current document passes real validation | | 05 Import YAML - imported Workflow draft | YAML is parsed and validated before draft creation | Parse errors stay in the creation surface and do not create partial drafts | | 06 Template - populated Workflow draft | A chosen template creates an independent editable draft | Templates are versioned frontend product content unless an authoritative catalogue already exists; they are never pretend backend records | @@ -336,7 +335,15 @@ observatory summary lacks a Workflow ID and names are neither unique nor stable. Consequently, the Excalidraw `Last Run` cell is omitted until an authoritative join key exists. -`Open` always uses the row's real `workflowId`. `Run` follows this policy: +`Open` always uses the row's real `workflowId` and preserves the row's source +kind. A draft row opens its authoritative draft. A committed-only row may load +through the existing committed fallback with `draftExists=false`; it is an +immutable source until the user explicitly saves a draft copy. That first +save uses draft creation, not draft update. The create response's +`workflowId` becomes canonical after materialization, and the editor replaces +the route if it differs from the committed source ID. + +`Run` follows this policy: - A validated draft may run through the reviewed scope draft-run endpoint. - A published Workflow may run only if an explicit callable @@ -359,10 +366,21 @@ All four creation choices end in the same persisted draft and same editor: template ID is frontend product content, never reused as the new `workflowId`. -Creation buttons enter a stable pending state, reject duplicate submission, -and navigate only after the create response returns the persisted identity. -On failure, user input remains editable and the page offers retry. There is no -optimistic route built from a locally generated Workflow ID. +Creation buttons enter a stable pending state and reject duplicate submission. +A scoped create may return either a materialized draft or a `202 Accepted` +receipt whose `readiness.readable` is `false` and whose stage is +`projection_pending`. For that receipt, retain the real `workflowId`, +`commandId`, and readiness message; show an observing state; and poll the +existing draft GET by exact scope and ID until the read model is readable. +Navigate only after the materialized response or a successful authoritative +GET returns the draft. + +A bounded observation timeout is delayed materialization, not create failure. +It keeps the accepted receipt and creation input available and offers another +readiness check for the same ID. It must not automatically repeat the create +POST, fabricate a readable draft, or route into a transient `404`. Non-`404` +query failures remain real errors. There is no optimistic route built from a +locally generated Workflow ID. ### Common Editor @@ -698,6 +716,21 @@ The current `src/shared/studio/api.ts` already exposes and `saveWorkflow`. Reuse those typed boundaries rather than issuing ad hoc fetches from components. +For a scoped create, `createWorkflowDraft` can return +`StudioWorkflowSaveResult.kind = "accepted"` with a +`StudioWorkflowDraftCreateAcceptedReceipt`. The receipt is command acceptance, +not a readable draft. Reuse the existing accepted-to-materialized behavior: +query `getWorkflowDraftFile(receipt.workflowId, scopeId)`, treat bounded `404` +responses as projection pending, and enter the editor only after the query +succeeds. Preserve the receipt on timeout so retry observes the same command +result rather than creating a duplicate draft. + +`getWorkflow` can fall back to a committed scope Workflow and marks it with +`draftExists=false`. `saveWorkflow` intentionally creates a new workspace +draft on its first save. The implementation must use the returned draft ID, +wait through the same accepted-to-materialized contract, and replace the +committed-only route identity if the created draft ID differs. + ### Generation And Editing ```text @@ -868,7 +901,10 @@ graph dependency unless an existing capability is demonstrably insufficient. - Implement the authoritative Workflow catalogue. - Implement Describe, blank, YAML import, and versioned template creation. -- Navigate only with the Workflow ID returned by draft persistence. +- Reconcile `202 projection_pending` receipts to a readable draft before + navigation, without resubmitting create on observation timeout. +- Open committed-only Workflows as explicit source state; first Save creates a + draft and replaces the route with the returned materialized draft ID. ### Phase 3: Common Editor And Run @@ -908,8 +944,13 @@ The expected high-value coverage is: - Activity adapter tests prove query encoding, scope handling, response decoding, unknown status preservation, detail/graph separation, and fork receipt decoding; -- a Workflows route test proves each creation path persists a draft before - editor navigation and retains input on failure; +- a Workflows route test proves each creation path handles both materialized + and `202 projection_pending` create results, enters the editor only after an + authoritative draft GET succeeds, retains the receipt/input on timeout, and + never resubmits create as an observation retry; +- a committed-only editor test proves first Save calls draft create rather + than update, waits for materialization, and replaces the route with the + returned draft ID without treating the committed source ID as the draft ID; - an editor/Run route test proves Accepted/Running does not claim Activity persistence and Observed appears only after an authoritative Run response; - Activity route tests cover loading, empty, error, server filter changes, @@ -956,11 +997,9 @@ meaningfully validates the changed surface; use package lint when cross-cutting scope makes a narrow command misleading. Before installing dependencies, starting the frontend, opening it for browser -QA, or running an integration-style command in a linked worktree, run: - -```bash -/usr/bin/python3 /Users/xiezixin/.codex/hooks/sync_worktree_env.py --cwd "$PWD" -``` +QA, or running an integration-style command in a linked worktree, follow the +active local `AGENTS.md` worktree environment synchronization instructions. +Do not copy a developer-specific hook path into shared implementation docs. Before OAuth browser verification, confirm without printing dotenv values that the actual origin exactly matches both `NYXID_REDIRECT_URI` and @@ -993,7 +1032,12 @@ are true: - The route `scopeId` is retained across navigation and sent only through scope-validating backend contracts. - A user can create a persisted draft with all four creation methods and enter - one common connected-node editor without creating a member. + one common connected-node editor without creating a member; a `202` + create receipt remains Accepted/Observing until the exact scoped draft GET + confirms readability. +- A committed-only Workflow remains a distinct source: first Save creates a + draft, observes materialization, and adopts the returned draft ID instead of + updating or assuming identity equality with the committed Workflow. - Workflow, member, definition actor, published service, and Run identities are never inferred from one another. - Run shows Accepted/Running separately from Activity observation and never diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md index 15aa916db5..62803535f8 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md @@ -163,7 +163,7 @@ flowchart TD | --- | --- | --- | --- | | UP-00 | Authenticate and enter the scoped workbench | 01, 13 | Existing callback returns to the original vNext URL and real scoped queries begin | | UP-01 | Browse and search Workflows | 01, 13 | Authoritative catalogue rows or a truthful empty/error state | -| UP-02 | Create by Describe | 02, 03 | Draft-create response returns the `workflowId` used by the editor route | +| UP-02 | Create by Describe | 02, 03 | Draft create returns a real `workflowId`, and the exact scoped draft GET confirms it is readable before editor navigation | | UP-03 | Start blank and add the first node | 02, 04 | Persisted draft opens; valid document state controls Save/Run/Publish | | UP-04 | Import YAML | 02, 05 | Server parse/validation succeeds before draft creation | | UP-05 | Create from Template | 02, 06 | Bundled template is copied into a newly persisted independent draft | @@ -267,6 +267,26 @@ successful empty response produces the New workflow empty state. - Do not use sample rows when one catalogue source fails. Preserve valid rows from the successful source and identify the failed source. +### Shared Draft Create Observation Contract + +UP-02 through UP-05 share one create-completion rule. A scoped draft create may +return a materialized draft or a `202 Accepted` receipt with a real +`workflowId`, command receipt, and `readiness.stage = projection_pending` while +`readiness.readable = false`. + +- A materialized draft can enter the editor immediately using its returned ID. +- An accepted receipt enters an explicit Accepted/Observing state and polls + `GET /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId`. +- A bounded `404` during that observation window means the projection is still + pending; it does not mean the create failed and it does not authorize a + second create request. +- Navigation occurs only after the exact scoped GET returns the readable draft. +- A timeout preserves the receipt, input, and returned ID. Retry checks the + same ID again. It never silently resubmits the create POST or fabricates a + local draft. +- Any non-`404` read failure is shown as its real authorization, decoding, or + network error and remains distinct from projection delay. + ## UP-02: Create A Workflow By Description **Intent:** Turn a natural-language automation goal into an editable Workflow @@ -292,14 +312,19 @@ draft. 5. Confirming creation calls `POST /api/workspace/workflow-drafts?scopeId=:scopeId`. 6. Only the draft-create response establishes the real `workflowId`. -7. The router opens that ID in the common connected-node editor. +7. If creation is accepted but not readable, the UI follows the shared create + observation contract for that exact scope and ID. +8. The router opens the common connected-node editor only after the draft is + materialized or the authoritative GET confirms readability. **Completion:** The editor loads the persisted draft and shows the generated nodes and edges. Generation success alone is not completion. **Recovery:** Generator, parser, validator, and draft-create failures preserve -the user's description and any safe generated preview. Retry repeats only the -failed explicit action. No local ID or demonstration draft is routed as saved. +the user's description and any safe generated preview. A create-observation +timeout preserves the accepted receipt and retries only the GET for the same +ID. Retry repeats only the failed explicit action. No local ID or demonstration +draft is routed as saved. ## UP-03: Start Blank And Add The First Node @@ -312,8 +337,10 @@ failed explicit action. No local ID or demonstration draft is routed as saved. 1. The user chooses `Start blank`. 2. The frontend creates a minimal, well-formed Workflow document as explicit frontend authoring content. -3. The draft-create API persists it and returns its `workflowId`. -4. The editor opens an empty connected-node canvas. +3. The draft-create API returns its authoritative `workflowId` and follows the + shared create observation contract when the draft is not readable yet. +4. The editor opens an empty connected-node canvas only after readability is + confirmed. 5. `Add node` opens the searchable Node library. 6. Choosing a node adds it to the shared document state. 7. Selecting the node opens Node configuration; `Edit YAML` opens the same @@ -341,8 +368,9 @@ insert a sample node and call the draft valid. needed. 4. Parse and validation feedback is shown against the YAML source. 5. Only a successful document can be submitted to the draft-create API. -6. The returned `workflowId` opens the common editor with parsed nodes and - edges. +6. The returned `workflowId` follows the shared create observation contract; + the common editor opens with parsed nodes and edges only after readability + is confirmed. **Completion:** The editor represents the same authoritative document in canvas and YAML modes. @@ -365,8 +393,10 @@ Workflow. real template API is introduced and reviewed later. 3. The user selects and previews a versioned template. 4. Its Workflow document is copied, parsed, and validated. -5. Draft creation persists a new resource and returns a new `workflowId`. -6. The common editor opens the copied nodes and edges. +5. Draft creation returns a new authoritative `workflowId` and follows the + shared create observation contract when projection is pending. +6. The common editor opens the copied nodes and edges only after readability + is confirmed. **Completion:** Editing the new draft cannot mutate the bundled template or another user's Workflow. @@ -388,6 +418,8 @@ model. ```text GET /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId PUT /api/workspace/workflow-drafts/:workflowId?scopeId=:scopeId +POST /api/workspace/workflow-drafts?scopeId=:scopeId +GET /api/scopes/:scopeId/workflows/:workflowId POST /api/editor/parse-yaml POST /api/editor/serialize-yaml POST /api/editor/validate @@ -396,15 +428,21 @@ POST /api/editor/normalize **Steps:** -1. The route loads the exact Workflow under the route scope. +1. The route loads the exact Workflow under the route scope and retains whether + the source is an existing draft or a committed-only Workflow. 2. Canvas, Node library, Node configuration, and YAML panel edit one document state. 3. A node or YAML change marks the draft dirty. 4. Validation exposes exact actionable problems and controls executable actions. -5. Save serializes and updates the authoritative draft. -6. Save success clears dirty state only after the real update succeeds. -7. Leaving while dirty prompts Save, Discard, or Stay. +5. Save serializes and updates an existing authoritative draft. +6. For a committed-only source (`draftExists=false`), first Save creates a new + draft instead of calling draft update. It follows the shared create + observation contract and replaces the route with the returned readable + draft ID, which may differ from the committed source ID. +7. Save success clears dirty state only after the real update succeeds or the + newly created draft becomes readable. +8. Leaving while dirty prompts Save, Discard, or Stay. **Publish boundary:** The Excalidraw retains a Publish control because the existing Studio supports publication/binding behavior. Publication must use @@ -413,7 +451,9 @@ confirmations. It is not a prerequisite for draft Run, and it must not invent a `publishedServiceId` after save acceptance. **Completion:** The saved document can be reloaded from the API with the -expected Workflow identity and content. +expected draft identity and content. For a committed-only source, that identity +is the create response's materialized draft ID, not an assumed reuse of the +committed Workflow ID. **Identity guard:** This path never creates a fake Team/member, never sends `workflowId` to a member endpoint, and never assumes publication makes the @@ -732,7 +772,8 @@ or completion evidence. | Checkpoint | The UI may say | Required evidence | The UI must not say | | --- | --- | --- | --- | | Workflow generated | Generated preview ready | Real generator response | Saved | -| Workflow created | Draft created | Draft-create response with `workflowId` | Created from a local ID | +| Workflow create accepted | Accepted/Observing | Real `202` receipt with `workflowId` and `projection_pending` readiness | Draft readable or editor ready | +| Workflow created | Draft created | Materialized create response or exact scoped draft GET succeeds | Created from a local ID or receipt alone | | Workflow saved | Saved | Real draft update success and clean document | Saved after timer-only feedback | | Run submitted | Submitting | User action in progress | Running | | Run accepted | Accepted/Running | Real SSE event or accepted receipt | Observed in Activity | @@ -751,6 +792,7 @@ or completion evidence. | Generator fails | User description | Generator error | Retry generation | | YAML parse fails | YAML input | Exact parse feedback | Edit and parse again | | Draft create fails | Creation inputs/preview | Create error | Retry create | +| Draft projection is delayed | Accepted receipt, returned ID, and creation inputs | Accepted/Observing or delayed readiness | Retry the exact draft GET; do not repeat create | | Draft save fails | Dirty document | Save error | Retry or continue editing | | Run submission fails | Input and document | Submission error | Retry explicit Run | | Run stream disconnects | Last real receipt/event | Disconnected or unknown status | Reconnect or open Activity | @@ -802,7 +844,13 @@ Every frame has a corresponding user path: switching uses the existing `ConsoleLanguageSwitch`/Umi locale state. - All four creation methods converge on a real persisted draft and the same connected-node editor. -- A real returned `workflowId` is required before editor navigation. +- A real returned `workflowId` and a successful materialized response or exact + scoped draft GET are required before editor navigation; a `202` receipt alone + is not readability. +- A create-observation timeout retries the same draft ID and never silently + resubmits draft creation. +- Opening a committed-only Workflow preserves `draftExists=false`; first Save + creates and observes a new draft, then adopts its returned ID. - Draft Run reaches Accepted/Running before it can reach Activity observation. - Activity observation requires a real observatory `runId` and `stateVersion`. - Workflow-specific Activity filtering uses only an exact returned From fad9adf6283a620c60b4b4afd377615653f999e9 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:05:24 +0800 Subject: [PATCH 006/167] Implement Workflow Activity vNext (#3189) * Implement Workflow Activity vNext * Render Workflow Activity vNext as standalone workbench * Refine Workflow Activity vNext settings layout * Hide internal workflow diagnostics from primary UI * Decode system-default LLM settings * Fix Workflow Activity table scrolling * Document workflow list filter semantics * Align Workflow and Activity filters * Fix Workflow Activity user path defects * Fix committed workflow editor fallback * Complete Workflow Activity functional paths * Reuse Workflow Studio canvas surface * Document NyxID visual alignment * Fix AI service selection * Align Workflow Activity with NyxID * Complete Workflow Activity vNext implementation * Fix workflow API error feedback * Fix console web type errors --------- Co-authored-by: Abigail Deng --- apps/aevatar-console-web/AGENTS.md | 39 + apps/aevatar-console-web/config/proxy.ts | 47 +- apps/aevatar-console-web/config/routes.ts | 307 +- ...-workflow-activity-vnext-implementation.md | 99 + ...w-activity-vnext-nyxid-visual-alignment.md | 131 + ...26-08-05-workflow-list-filter-semantics.md | 408 + ...kflow-activity-vnext-scoped-publication.md | 228 + ...5-workflow-list-filter-semantics-design.md | 135 + .../src/app.layout.test.ts | 129 +- apps/aevatar-console-web/src/app.tsx | 338 +- apps/aevatar-console-web/src/locales/en-US.ts | 188 +- .../src/locales/projectMessages.en-US.ts | 2 +- .../src/locales/projectMessages.zh-CN.ts | 2 +- .../workflowActivityVNextMessages.en-US.ts | 454 + .../workflowActivityVNextMessages.zh-CN.ts | 425 + apps/aevatar-console-web/src/locales/zh-CN.ts | 238 +- .../src/pages/runs/index.test.tsx | 916 +- .../src/pages/runs/index.tsx | 1160 ++- .../ScopeServiceRuntimeWorkbench.test.tsx | 686 ++ .../ScopeServiceRuntimeWorkbench.tsx | 2293 +++- .../components/StudioBuildPanels.test.tsx | 431 +- .../studio/components/StudioBuildPanels.tsx | 2346 +++-- .../StudioMemberInvokePanel.test.tsx | 335 +- .../components/StudioMemberInvokePanel.tsx | 366 +- .../bind/StudioMemberBindPanel.test.tsx | 262 +- .../components/bind/StudioMemberBindPanel.tsx | 1328 ++- .../src/pages/studio/index.test.tsx | 9278 +++++++++-------- .../src/pages/studio/index.tsx | 4588 ++++---- .../components/WorkflowStudioCanvas.tsx | 22 +- .../components/WorkflowStudioCanvasRegion.tsx | 49 + .../components/WorkflowStudioEmptyState.tsx | 35 +- .../WorkflowStudioExecutionPanel.test.tsx | 62 + .../WorkflowStudioExecutionPanel.tsx | 841 +- .../components/WorkflowStudioNodeLibrary.tsx | 114 +- .../components/WorkflowStudioYamlPanel.tsx | 269 +- .../hooks/useTeamMemberWorkflowStudio.ts | 1450 +-- .../index.test.tsx | 7217 +++++++------ .../team-member-workflow-studio/index.tsx | 179 +- .../src/pages/teams/detail.test.tsx | 3139 +++--- .../src/pages/teams/detail.tsx | 1559 +-- .../src/pages/teams/new.test.tsx | 100 +- .../src/pages/teams/new.tsx | 109 +- .../teams/tabs/TeamAutomationsTab.test.tsx | 1183 ++- .../pages/teams/tabs/TeamAutomationsTab.tsx | 2069 ++-- .../TableScrollRegion.tsx | 32 + .../TechnicalDetails.tsx | 17 + .../WorkflowActivityVNextShell.tsx | 200 + .../activity/ActivityPage.test.tsx | 238 + .../activity/ActivityPage.tsx | 409 + .../activity/RunDetailPage.test.tsx | 355 + .../activity/RunDetailPage.tsx | 834 ++ .../activity/runPresentation.ts | 47 + .../activity/runRecovery.test.ts | 54 + .../activity/runRecovery.ts | 38 + .../hooks/useConsoleLocation.ts | 18 + .../hooks/useDraftMaterialization.test.ts | 69 + .../hooks/useDraftMaterialization.ts | 117 + .../hooks/useRunObservation.test.ts | 123 + .../hooks/useRunObservation.ts | 153 + .../hooks/useWorkflowEditor.ts | 729 ++ .../hooks/useWorkflowPublication.test.ts | 377 + .../hooks/useWorkflowPublication.ts | 363 + .../workflow-activity-vnext/index.test.tsx | 3970 +++++++ .../pages/workflow-activity-vnext/index.tsx | 84 + .../workflow-activity-vnext/navigation.ts | 41 + .../settings/SettingsPage.tsx | 828 ++ .../pages/workflow-activity-vnext/styles.ts | 325 + .../workflows/NewWorkflowPage.tsx | 607 ++ .../workflows/WorkflowEditorPage.tsx | 1293 +++ .../workflows/WorkflowNodeInspector.tsx | 743 ++ .../workflows/WorkflowPublishDialog.test.tsx | 205 + .../workflows/WorkflowPublishDialog.tsx | 431 + .../workflows/WorkflowsPage.tsx | 614 ++ .../workflows/workflowCreation.ts | 69 + .../pages/workflows/WorkflowYamlViewer.tsx | 122 +- .../src/routesConfig.test.ts | 272 +- .../src/shared/api/http/client.test.ts | 35 + .../src/shared/api/http/client.ts | 37 +- .../src/shared/api/scopeRuntimeApi.test.ts | 661 +- .../src/shared/api/scopeRuntimeApi.ts | 652 +- .../src/shared/api/scopesApi.ts | 212 +- .../shared/api/workflowActivityApi.test.ts | 236 + .../src/shared/api/workflowActivityApi.ts | 525 + .../src/shared/config/proxyConfig.test.ts | 58 +- .../src/shared/models/workflowActivity.ts | 151 + .../src/shared/studio/api.test.ts | 165 +- .../src/shared/studio/api.ts | 2428 +++-- .../src/shared/ui/ConsoleToast.tsx | 55 + .../tests/reactQueryTestUtils.tsx | 5 +- docs/canon/frontend-design.md | 82 + 90 files changed, 44474 insertions(+), 20161 deletions(-) create mode 100644 apps/aevatar-console-web/docs/superpowers/plans/2026-08-04-workflow-activity-vnext-implementation.md create mode 100644 apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-activity-vnext-nyxid-visual-alignment.md create mode 100644 apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-list-filter-semantics.md create mode 100644 apps/aevatar-console-web/docs/superpowers/plans/2026-08-06-workflow-activity-vnext-scoped-publication.md create mode 100644 apps/aevatar-console-web/docs/superpowers/specs/2026-08-05-workflow-list-filter-semantics-design.md create mode 100644 apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts create mode 100644 apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts create mode 100644 apps/aevatar-console-web/src/pages/scopes/components/ScopeServiceRuntimeWorkbench.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/team-member-workflow-studio/components/WorkflowStudioCanvasRegion.tsx create mode 100644 apps/aevatar-console-web/src/pages/team-member-workflow-studio/components/WorkflowStudioExecutionPanel.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/TableScrollRegion.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/TechnicalDetails.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/WorkflowActivityVNextShell.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/ActivityPage.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/ActivityPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/RunDetailPage.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/RunDetailPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/runPresentation.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/runRecovery.test.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/activity/runRecovery.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useConsoleLocation.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useDraftMaterialization.test.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useDraftMaterialization.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useRunObservation.test.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useRunObservation.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.test.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/settings/SettingsPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowPublishDialog.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowPublishDialog.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowCreation.ts create mode 100644 apps/aevatar-console-web/src/shared/api/http/client.test.ts create mode 100644 apps/aevatar-console-web/src/shared/api/workflowActivityApi.test.ts create mode 100644 apps/aevatar-console-web/src/shared/api/workflowActivityApi.ts create mode 100644 apps/aevatar-console-web/src/shared/models/workflowActivity.ts create mode 100644 apps/aevatar-console-web/src/shared/ui/ConsoleToast.tsx diff --git a/apps/aevatar-console-web/AGENTS.md b/apps/aevatar-console-web/AGENTS.md index bc24c7ea26..b6c04eaad6 100644 --- a/apps/aevatar-console-web/AGENTS.md +++ b/apps/aevatar-console-web/AGENTS.md @@ -200,6 +200,17 @@ pnpm --dir apps/aevatar-console-web build magic values. - Treat the console as an operational tool: prioritize scannability, clear hierarchy, predictable navigation, and efficient repeated actions. +- Default user-facing surfaces must show only information needed to understand + the current task, result, or next action. Do not expose backend architecture, + transport, storage, or consistency terminology such as `read model`, + `projection`, `materialization`, `receipt`, raw actor/command/correlation + identifiers, state versions/watermarks, DTO or endpoint names, or query + sampling limits in page titles, descriptions, helper copy, primary tables, + empty states, or primary error messages. Preserve truthful loading, accepted, + delayed, and failed semantics in plain product language. When raw values are + genuinely useful for support or debugging, place them behind an explicit, + user-opened technical-details disclosure instead of making them the default + interface. - Preserve responsive behavior, keyboard access, focus visibility, semantic controls, and readable contrast. Verify dense real content and narrow mobile widths without overlap, clipping, or inaccessible actions. @@ -209,6 +220,34 @@ pnpm --dir apps/aevatar-console-web build - Use established icon libraries and control patterns. Add accessible names or tooltips for icon-only or unfamiliar actions. +### Action Feedback and Toasts + +- Use the shared `ConsoleToastProvider` and `useConsoleToast` from + `src/shared/ui/ConsoleToast.tsx` for transient user-action feedback. Do not + introduce new direct `antd` `message` or `notification` calls inside React + product surfaces. Non-React transport error boundaries retain their existing + handling unless the boundary receives a deliberate React-safe migration. +- A success toast is evidence of a completed user-visible action, not of a + click, request dispatch, `202 Accepted` response, local optimistic update, + or background observation still in progress. Show it only after the API + contract has reached the state the copy claims. +- Keep a toast short, localized, and action-oriented. Do not put endpoint + names, DTO fields, request IDs, raw backend errors, or recovery diagnostics + in it; expose those through the surface's existing technical-details path. +- Report transient API request failures through the shared error toast. Do not + add page-wide warning or error banners above otherwise usable content, and + do not render an error while another request required to classify the same + state is still pending. +- Use persistent inline state, alerts, or panels for loading, accepted, + observing, delayed, retryable, authorization, forbidden, and primary-content + failures where the user needs a durable next step. A toast must not be the + only evidence of a durable status or recovery action. +- Emit at most one toast for one user action. Avoid success toasts for local + form edits that still require an explicit page-level save. Migrate touched + user-feedback paths to the shared abstraction while preserving legacy + behavior outside the requested surface unless a deliberate migration is in + scope. + ## Change and Review Hygiene - Branch names use `/YYYY-MM-DD_`, where `` is one of diff --git a/apps/aevatar-console-web/config/proxy.ts b/apps/aevatar-console-web/config/proxy.ts index 7dcbb001e3..216495365a 100644 --- a/apps/aevatar-console-web/config/proxy.ts +++ b/apps/aevatar-console-web/config/proxy.ts @@ -10,8 +10,7 @@ * @doc https://umijs.org/docs/guides/proxy */ const apiTarget = process.env.AEVATAR_API_TARGET || 'http://127.0.0.1:5080'; -const studioApiTarget = - process.env.AEVATAR_STUDIO_API_TARGET || apiTarget; +const studioApiTarget = process.env.AEVATAR_STUDIO_API_TARGET || apiTarget; const preserveAuthHost = process.env.AEVATAR_PROXY_PRESERVE_AUTH_HOST; const buildProxyTarget = (target: string) => ({ @@ -35,8 +34,8 @@ const buildAuthProxyTarget = (target: string) => ? buildHostPreservingProxyTarget(target) : buildProxyTarget(target) : shouldPreserveHost(target) - ? buildHostPreservingProxyTarget(target) - : buildProxyTarget(target); + ? buildHostPreservingProxyTarget(target) + : buildProxyTarget(target); const studioProxyEntries = [ '/api/app', @@ -46,33 +45,29 @@ const studioProxyEntries = [ '/api/executions', '/api/roles', '/api/studio', + '/api/user-config', '/api/workspace', -].reduce>>((entries, path) => { - const proxyFactory = path === '/api/auth' - ? buildAuthProxyTarget - : buildProxyTarget; - entries[`^${path}$`] = proxyFactory(studioApiTarget); - entries[`${path}/`] = proxyFactory(studioApiTarget); - return entries; -}, {}); +].reduce>>( + (entries, path) => { + const proxyFactory = + path === '/api/auth' ? buildAuthProxyTarget : buildProxyTarget; + entries[`^${path}$`] = proxyFactory(studioApiTarget); + entries[`${path}/`] = proxyFactory(studioApiTarget); + return entries; + }, + {}, +); const studioScopeProxyEntries = { - '^/api/scopes/[^/]+/chat-history(?:/.*)?$': - buildProxyTarget(studioApiTarget), - '^/api/scopes/[^/]+/teams/[^/]+/invoke(?:/.*)?$': - buildProxyTarget(apiTarget), - '^/api/scopes/[^/]+/teams(?:/.*)?$': - buildProxyTarget(studioApiTarget), - '^/api/scopes/[^/]+/members$': - buildProxyTarget(studioApiTarget), - '^/api/scopes/[^/]+/members/[^/]+$': - buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/chat-history(?:/.*)?$': buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/teams/[^/]+/invoke(?:/.*)?$': buildProxyTarget(apiTarget), + '^/api/scopes/[^/]+/teams(?:/.*)?$': buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/members$': buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/members/[^/]+$': buildProxyTarget(studioApiTarget), '^/api/scopes/[^/]+/members/[^/]+/(?:binding(?:/.*)?|binding-runs(?:/.*)?|endpoints/[^/]+/contract)$': buildProxyTarget(studioApiTarget), - '^/api/scopes/[^/]+/scripts/draft-run$': - buildProxyTarget(studioApiTarget), - '^/api/scopes/[^/]+/workflow/draft-run$': - buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/scripts/draft-run$': buildProxyTarget(studioApiTarget), + '^/api/scopes/[^/]+/workflow/draft-run$': buildProxyTarget(studioApiTarget), '^/api/scopes/[^/]+/workflows:save-and-bind$': buildProxyTarget(studioApiTarget), '^/api/scripts/validate$': buildProxyTarget(studioApiTarget), diff --git a/apps/aevatar-console-web/config/routes.ts b/apps/aevatar-console-web/config/routes.ts index 9a556947d9..c000e8384e 100644 --- a/apps/aevatar-console-web/config/routes.ts +++ b/apps/aevatar-console-web/config/routes.ts @@ -12,272 +12,307 @@ */ export default [ { - path: "/login", - component: "./login", + path: '/login', + component: './login', layout: false, }, { - path: "/auth/callback", - component: "./auth/callback", + path: '/auth/callback', + component: './auth/callback', layout: false, }, { - path: "/overview", - redirect: "/scopes", + path: '/overview', + redirect: '/scopes', hideInMenu: true, }, { - path: "/chat", - name: "Chat", - component: "./chat", - menuGroupKey: "chat", + path: '/chat', + name: 'Chat', + component: './chat', + menuGroupKey: 'chat', hideInMenu: false, }, { - path: "/scopes", - name: "My Teams", - component: "./teams", - menuGroupKey: "teams", + path: '/scopes', + name: 'My Teams', + component: './teams', + menuGroupKey: 'teams', hideInMenu: false, }, { - path: "/scopes/:scopeId/teams/new", - name: "Create Team", - component: "./teams/new", + path: '/scopes/:scopeId/workflow-activity-vnext', + redirect: '/scopes/:scopeId/workflow-activity-vnext/workflows', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams", - name: "My Teams", - component: "./teams", + path: '/scopes/:scopeId/workflow-activity-vnext/workflows', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId/members/new/workflow", - name: "Team Member Workflow Studio", - component: "./team-member-workflow-studio", + path: '/scopes/:scopeId/workflow-activity-vnext/workflows/new', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId/members/:memberId/workflow", - name: "Team Member Workflow Studio", - component: "./team-member-workflow-studio", + path: '/scopes/:scopeId/workflow-activity-vnext/workflows/:workflowId', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId/members/:memberId/invoke", - name: "Team Member Invoke", - component: "./team-member-invoke", + path: '/scopes/:scopeId/workflow-activity-vnext/activity', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId/members/:memberId/runs", - name: "Team Member Published Runs", - component: "./runtime-published-runs", + path: '/scopes/:scopeId/workflow-activity-vnext/activity/:runId', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId/members/:memberId/automations", - component: "./teams/detail", + path: '/scopes/:scopeId/workflow-activity-vnext/settings', + component: './workflow-activity-vnext', hideInMenu: true, - parentKeys: ["/scopes"], }, { - path: "/scopes/:scopeId/teams/:teamId", - name: "Team Details", - component: "./teams/detail", + path: '/scopes/:scopeId/teams/new', + name: 'Create Team', + component: './teams/new', hideInMenu: true, - parentKeys: ["/scopes"], + parentKeys: ['/scopes'], }, { - path: "/scopes/assets", - component: "./scopes/assets", + path: '/scopes/:scopeId/teams', + name: 'My Teams', + component: './teams', hideInMenu: true, + parentKeys: ['/scopes'], }, { - path: "/scopes/files", - name: "Files", - component: "./scopes/files", - menuGroupKey: "build", + path: '/scopes/:scopeId/teams/:teamId/members/new/workflow', + name: 'Team Member Workflow Studio', + component: './team-member-workflow-studio', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/:scopeId/teams/:teamId/members/:memberId/workflow', + name: 'Team Member Workflow Studio', + component: './team-member-workflow-studio', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/:scopeId/teams/:teamId/members/:memberId/invoke', + name: 'Team Member Invoke', + component: './team-member-invoke', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/:scopeId/teams/:teamId/members/:memberId/runs', + name: 'Team Member Published Runs', + component: './runtime-published-runs', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/:scopeId/teams/:teamId/members/:memberId/automations', + component: './teams/detail', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/:scopeId/teams/:teamId', + name: 'Team Details', + component: './teams/detail', + hideInMenu: true, + parentKeys: ['/scopes'], + }, + { + path: '/scopes/assets', + component: './scopes/assets', + hideInMenu: true, + }, + { + path: '/scopes/files', + name: 'Files', + component: './scopes/files', + menuGroupKey: 'build', }, { - path: "/studio", - component: "./studio", + path: '/studio', + component: './studio', hideInMenu: true, }, { - path: "/runtime/workflows", - component: "./workflows", + path: '/runtime/workflows', + component: './workflows', hideInMenu: true, }, { - path: "/runtime/primitives", - name: "Connectors", - component: "./primitives", + path: '/runtime/primitives', + name: 'Connectors', + component: './primitives', hideInMenu: true, }, { - path: "/scopes/invoke", - component: "./scopes/invoke", + path: '/scopes/invoke', + component: './scopes/invoke', hideInMenu: true, }, { - path: "/runtime/runs", - name: "Event Stream", - component: "./runs", - menuGroupKey: "platform", + path: '/runtime/runs', + name: 'Event Stream', + component: './runs', + menuGroupKey: 'platform', }, { - path: "/runtime/mission-control", - name: "Mission Control", - component: "./MissionControl", + path: '/runtime/mission-control', + name: 'Mission Control', + component: './MissionControl', hideInMenu: true, }, { - path: "/runtime/mission-wall", - component: "./MissionWall", + path: '/runtime/mission-wall', + component: './MissionWall', hideInMenu: true, }, { - path: "/services", - name: "Services", - component: "./services", - menuGroupKey: "platform", + path: '/services', + name: 'Services', + component: './services', + menuGroupKey: 'platform', }, { - path: "/services/:serviceId", - component: "./services", + path: '/services/:serviceId', + component: './services', hideInMenu: true, - parentKeys: ["/services"], + parentKeys: ['/services'], }, { - path: "/governance", - name: "Governance", - component: "./governance", - menuGroupKey: "platform", + path: '/governance', + name: 'Governance', + component: './governance', + menuGroupKey: 'platform', }, { - path: "/governance/policies", - component: "./governance/policies", + path: '/governance/policies', + component: './governance/policies', hideInMenu: true, - parentKeys: ["/governance"], + parentKeys: ['/governance'], }, { - path: "/governance/bindings", - component: "./governance/bindings", + path: '/governance/bindings', + component: './governance/bindings', hideInMenu: true, - parentKeys: ["/governance"], + parentKeys: ['/governance'], }, { - path: "/governance/endpoints", - component: "./governance/endpoints", + path: '/governance/endpoints', + component: './governance/endpoints', hideInMenu: true, - parentKeys: ["/governance"], + parentKeys: ['/governance'], }, { - path: "/governance/activation", - component: "./governance/activation", + path: '/governance/activation', + component: './governance/activation', hideInMenu: true, - parentKeys: ["/governance"], + parentKeys: ['/governance'], }, { - path: "/deployments", - name: "Deployments", - component: "./Deployments", - menuGroupKey: "platform", + path: '/deployments', + name: 'Deployments', + component: './Deployments', + menuGroupKey: 'platform', }, { - path: "/runtime/explorer", - name: "Topology", - component: "./actors", - menuGroupKey: "platform", + path: '/runtime/explorer', + name: 'Topology', + component: './actors', + menuGroupKey: 'platform', }, { - path: "/runtime/explorer/detail", - component: "./actors/detail", + path: '/runtime/explorer/detail', + component: './actors/detail', hideInMenu: true, - parentKeys: ["/runtime/explorer"], + parentKeys: ['/runtime/explorer'], }, { - path: "/runtime/gagents", - name: "Members", - component: "./gagents", + path: '/runtime/gagents', + name: 'Members', + component: './gagents', hideInMenu: true, }, { - path: "/scopes/overview", - component: "./scopes/overview", + path: '/scopes/overview', + component: './scopes/overview', hideInMenu: true, }, { - path: "/settings", - name: "Settings", - component: "./settings", - menuGroupKey: "settings", + path: '/settings', + name: 'Settings', + component: './settings', + menuGroupKey: 'settings', }, { - path: "/scopes/workflows", - redirect: "/runtime/workflows", + path: '/scopes/workflows', + redirect: '/runtime/workflows', hideInMenu: true, }, { - path: "/scopes/scripts", - redirect: "/studio?tab=scripts", + path: '/scopes/scripts', + redirect: '/studio?tab=scripts', hideInMenu: true, }, { - path: "/governance/audit", - redirect: "/governance?view=changes", + path: '/governance/audit', + redirect: '/governance?view=changes', hideInMenu: true, }, { - path: "/workflows", - redirect: "/runtime/workflows", + path: '/workflows', + redirect: '/runtime/workflows', hideInMenu: true, }, { - path: "/primitives", - redirect: "/runtime/primitives", + path: '/primitives', + redirect: '/runtime/primitives', hideInMenu: true, }, { - path: "/runs", - redirect: "/runtime/runs", + path: '/runs', + redirect: '/runtime/runs', hideInMenu: true, }, { - path: "/actors", - redirect: "/runtime/explorer", + path: '/actors', + redirect: '/runtime/explorer', hideInMenu: true, }, { - path: "/gagents", - redirect: "/runtime/gagents", + path: '/gagents', + redirect: '/runtime/gagents', hideInMenu: true, }, { - path: "/mission-control", - redirect: "/runtime/mission-control", + path: '/mission-control', + redirect: '/runtime/mission-control', hideInMenu: true, }, { - path: "/mission-wall", - redirect: "/runtime/mission-wall", + path: '/mission-wall', + redirect: '/runtime/mission-wall', hideInMenu: true, }, { - path: "/", - redirect: "/scopes", + path: '/', + redirect: '/scopes', }, { - component: "404", + component: '404', layout: false, - path: "/*", + path: '/*', }, ]; diff --git a/apps/aevatar-console-web/docs/superpowers/plans/2026-08-04-workflow-activity-vnext-implementation.md b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-04-workflow-activity-vnext-implementation.md new file mode 100644 index 0000000000..627f4ae6ab --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-04-workflow-activity-vnext-implementation.md @@ -0,0 +1,99 @@ +# Workflow Activity vNext Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Deliver every approved Workflow Activity vNext user path under the isolated scoped route namespace using only authoritative backend data and existing authentication/localization behavior. + +**Architecture:** The route package owns page composition and transient interaction state. Existing Studio, scope, runtime, auth, and Settings adapters remain authoritative; one new typed adapter owns observatory and fork transport decoding. TanStack Query keys include the route scope and normalized filters, while accepted-to-readable and accepted-to-observed transitions use explicit receipt-bound state machines. + +**Tech Stack:** React 19, TypeScript, Umi Max, Ant Design, TanStack Query, XYFlow/GraphCanvas, Jest, Testing Library, Biome. + +--- + +### Task 1: Isolated routes and typed transport boundary + +**Files:** +- Modify: `config/routes.ts` +- Modify: `src/routesConfig.test.ts` +- Create: `src/shared/models/workflowActivity.ts` +- Create: `src/shared/api/workflowActivityApi.ts` +- Create: `src/shared/api/workflowActivityApi.test.ts` + +- [ ] Write route assertions for all seven hidden vNext routes, literal `workflows/new` ordering, namespace-only redirect, and unchanged legacy redirects; run the single route test and confirm RED. +- [ ] Add precise observatory summary/detail/graph, filter, and fork receipt models plus decoders that preserve unknown status values and reject malformed required identity/version fields. +- [ ] Write adapter tests for encoded scope/filter queries, independent detail/graph requests, 401/403/404 propagation, fork request identity, and receipt decoding; run and confirm RED before implementation. +- [ ] Implement the seven route records and typed adapter, then rerun only `src/routesConfig.test.ts` and `src/shared/api/workflowActivityApi.test.ts` to GREEN. + +### Task 2: Scoped shell, catalogue, and direct creation + +**Files:** +- Create: `src/pages/workflow-activity-vnext/index.tsx` +- Create: `src/pages/workflow-activity-vnext/WorkflowActivityVNextShell.tsx` +- Create: `src/pages/workflow-activity-vnext/navigation.ts` +- Create: `src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx` +- Create: `src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx` +- Create: `src/pages/workflow-activity-vnext/workflows/workflowCreation.ts` +- Create: `src/pages/workflow-activity-vnext/hooks/useDraftMaterialization.ts` +- Create: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [ ] Write route integration tests for authoritative catalogue loading, successful empty data, partial-source failure, search, and real-ID navigation; run the new page test and confirm RED. +- [ ] Write creation tests for Describe, blank, YAML import, and bundled versioned template paths. Cover materialized and `202 projection_pending` responses, bounded `404`, preserved receipt/input, retrying the same GET, and duplicate-submit prevention; confirm RED. +- [ ] Implement the local rail/mobile navigation with existing `ConsoleLanguageSwitch` and `ConsoleAuthActions`, two-source catalogue, four direct creation forms, and receipt-bound materialization hook. +- [ ] Rerun only the new route test after each user-path slice until UP-00 through UP-05 are GREEN. + +### Task 3: Common editor, first save, and draft execution + +**Files:** +- Create: `src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx` +- Create: `src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts` +- Create: `src/pages/workflow-activity-vnext/hooks/useDraftRun.ts` +- Reuse: `src/pages/team-member-workflow-studio/components/WorkflowStudioCanvas.tsx` +- Reuse: `src/pages/team-member-workflow-studio/components/WorkflowStudioNodeLibrary.tsx` +- Reuse: `src/pages/team-member-workflow-studio/components/WorkflowStudioNodeDetailPanel.tsx` +- Reuse: `src/pages/team-member-workflow-studio/components/WorkflowStudioYamlPanel.tsx` +- Reuse: `src/pages/team-member-workflow-studio/components/WorkflowStudioDraftRunPanel.tsx` + +- [ ] Write editor tests for exact draft loading, committed fallback, dirty state, YAML/canvas shared state, validation findings, existing-draft PUT, and committed-only first-save POST with returned-ID route replacement; confirm RED. +- [ ] Implement the editor hook and page using existing Studio graph/document helpers without member APIs or member identity. +- [ ] Write draft-run tests for real serialized YAML, stable submission, accepted/running state, disconnected/error state, no Activity completion claim, and general Open Activity when no trustworthy run ID exists; confirm RED. +- [ ] Implement `runtimeRunsApi.streamDraftRun` integration and authoritative stream presentation, then rerun the route test to GREEN for UP-06 and UP-07. + +### Task 4: Activity observation, ledger, detail, and recovery + +**Files:** +- Create: `src/pages/workflow-activity-vnext/activity/ActivityPage.tsx` +- Create: `src/pages/workflow-activity-vnext/activity/RunDetailPage.tsx` +- Create: `src/pages/workflow-activity-vnext/activity/runRecovery.ts` +- Create: `src/pages/workflow-activity-vnext/hooks/useRunObservation.ts` + +- [ ] Write tests for URL-backed server filters, workflow definition resolution, filter-unavailable fallback, loading/empty/error/unknown statuses, and recent-window wording; confirm RED. +- [ ] Implement the Activity query and ledger without name joins, local totals, revisions, duration, usage, outcome, or Needs-you filtering. +- [ ] Write tests for independent detail/graph loading, safe not-found, partial running detail, graph-only failure, failed-step eligibility, first-executable-step eligibility, immutable source, fork receipt display, and no `newRunActorId` detail navigation; confirm RED. +- [ ] Implement detail, graph, Retry, Run again, and bounded observation, then rerun the route test to GREEN for UP-08 through UP-12. + +### Task 5: Settings, identity, responsive behavior, and locale + +**Files:** +- Create: `src/pages/workflow-activity-vnext/settings/SettingsPage.tsx` +- Create: `src/pages/workflow-activity-vnext/styles.ts` +- Modify: `src/locales/en-US.ts` +- Modify: `src/locales/zh-CN.ts` +- Modify: `src/locales/projectMessages.en-US.ts` +- Modify: `src/locales/projectMessages.zh-CN.ts` +- Modify: `src/locales/catalog.test.ts` + +- [ ] Write tests for real LLM loading/dirty/accepted/observed/catalogue-unavailable/save-failure states, auth-me identity/expiry, existing auth actions, runtime loading/unavailable, and no localStorage authority; confirm RED. +- [ ] Implement AI Defaults by reusing `userLlmSelection.ts` and `observeUserLlmSave`, Account through existing session/actions, and Advanced through `getUserConfigRuntime`. +- [ ] Add every new message to both locale catalogues and verify catalogue parity with the single locale test. +- [ ] Add responsive Operational Automation Ledger styles and test keyboard-visible controls, semantic status, dialogs, long identities, and mobile section navigation; rerun only the new page and locale tests to GREEN for UP-13 through UP-16. + +### Task 6: Focused verification and delivery + +**Files:** +- Review all files changed relative to `origin/feat/2026-08-04_workflow-activity-vnext` + +- [ ] Run `frontend_change_scope.py --base origin/feat/2026-08-04_workflow-activity-vnext`, explicitly execute every changed/new Jest file and dependency-related tests, then run Biome only on reported static-check files. +- [ ] Because the user explicitly requested full TypeScript and production-build validation, run the package `tsc` and `build` with `CODEX_ALLOW_FULL_FRONTEND_VALIDATION=1`; do not run the complete Jest suite. +- [ ] Run the design baseline verifier and confirm the declared SHA, byte-identical generator output, and 17/17 frames. +- [ ] After the environment synchronizer succeeds, verify real authenticated desktop/tablet/mobile routes in-browser and capture screenshots. If the synchronizer/backend/OAuth environment remains unavailable, record the exact gap without mocks. +- [ ] Review the complete frontend-only diff, stage only this task's files, commit with an imperative message, push, and create a Draft implementation PR without modifying PR #3187 or enabling auto-merge. diff --git a/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-activity-vnext-nyxid-visual-alignment.md b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-activity-vnext-nyxid-visual-alignment.md new file mode 100644 index 0000000000..a9e153c316 --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-activity-vnext-nyxid-visual-alignment.md @@ -0,0 +1,131 @@ +# Workflow Activity vNext NyxID Visual Alignment Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Align every Workflow Activity vNext page with NyxID's measured layout, typography, density, table, form, and responsive patterns while preserving Aevatar colors and all existing product behavior. + +**Architecture:** Keep the current route and component ownership. Centralize the measured visual contract in `styles.ts`, make `WorkflowActivityVNextShell.tsx` own the 52px top bar and responsive navigation, and limit page edits to semantic class hooks or removal of conflicting inline styles. No API, identity, query, routing, locale, or workflow behavior changes are allowed. + +**Tech Stack:** React 19, TypeScript, Ant Design 6, CSS variables in the existing vNext style module, Jest/Testing Library for existing behavior coverage, Biome, and real-browser responsive verification. + +--- + +## Design baseline declaration + +```text +Design baseline: + apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/ +Primary design: + aevatar-workflow-activity-vnext.excalidraw +Design SHA-256: + 30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de +Contract specification: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-design.md +User paths: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-user-paths.md +Authentication and localization: + Existing Aevatar login, callback, session, returnTo, and Umi locale logic; + presentation may change, behavior may not. +Production data source: + Real APIs and API-acknowledged user actions only; no mock fallback. +Baseline integrity: + python3 apps/aevatar-console-web/docs/design-baselines/ + workflow-activity-vnext/verify-baseline.py +``` + +### Task 1: Lock the measured design contract + +**Files:** +- Modify: `docs/canon/frontend-design.md` +- Create: `apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-activity-vnext-nyxid-visual-alignment.md` + +- [x] Record the measured shell, typography, spacing, control, table, responsive, and accessibility rules. +- [x] Record the permitted Aevatar deviations: existing colors, AlibabaSans delivery, and the repository's 8px radius cap. +- [x] Update issue `#3194` with the measurement table, page inventory, and acceptance evidence. +- [x] Run `bash tools/docs/lint.sh` and `git diff --check`. +- [x] Commit the documentation as `Document NyxID visual alignment`. + +### Task 2: Align the shared shell and navigation + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/WorkflowActivityVNextShell.tsx` +- Modify: `src/pages/workflow-activity-vnext/styles.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [x] Preserve the existing semantic navigation and account/language components while moving their presentation into a 52px top bar. +- [x] Place the Aevatar brand and current breadcrumb in the top bar, start the 200px local rail below it, and render the page title inside the main content surface. +- [x] Replace the permanent mobile navigation strip with an accessible menu button and drawer using the same route builders. +- [x] Define shared spacing, type, radius, shadow, control, and table tokens in `.wa-vnext`. +- [x] Run the existing route integration test before and after the shell change to prove navigation, auth action reuse, and page behavior remain intact: + +```bash +pnpm exec jest --runInBand --runTestsByPath \ + src/pages/workflow-activity-vnext/index.test.tsx +``` + +### Task 3: Align Workflows and New workflow + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/styles.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [x] Keep Workflow search and source filter in one responsive toolbar. +- [x] Render the Workflow table with a 32px header, 53-60px rows, one clear Open action, and the branch's existing accessible secondary actions. +- [x] Remove the mobile card transformation and use a named local horizontal scroll region. +- [x] Replace New workflow inline presentation styles with shared classes: four equal-level creation choices, then one form container for the selected method. +- [x] Verify Describe, blank, import, and template retain their current authoritative creation behavior through the existing focused test file. + +### Task 4: Align the Workflow editor + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/styles.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [x] Keep Canvas and YAML as a compact segmented mode control and preserve the existing shared editor state. +- [x] Keep the canvas unframed/full-width inside the work surface; remove conflicting fixed layout values where shared responsive tokens can own sizing. +- [x] Preserve visible Save, Run, Publish availability and the current node library/detail/YAML behavior. +- [x] At mobile width, keep editor actions reachable, allow horizontal action scrolling only inside the toolbar, and preserve the existing accessible node panel behavior. + +### Task 5: Align Activity and Run detail + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/activity/ActivityPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/activity/RunDetailPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/styles.ts` +- Test: `src/pages/workflow-activity-vnext/activity/ActivityPage.test.tsx` +- Test: `src/pages/workflow-activity-vnext/activity/RunDetailPage.test.tsx` + +- [x] Apply the NyxID ledger density to Activity while preserving status, source, updated time, server filters, and the single detail target. +- [x] Keep mobile Activity as a semantic table in a bounded horizontal scroller rather than a card layout. +- [x] Move Run status and recovery actions close to the page title; use a 32px underline tab strip and single-layer detail sections. +- [x] Keep Steps and Diagnostics tables semantic and locally scrollable; preserve independent graph failure and immutable source behavior. +- [x] Run both focused behavior tests after the visual-only edits. + +### Task 6: Align Settings + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/settings/SettingsPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/styles.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [x] Replace the desktop secondary side navigation with a 32px underline tab strip that remains horizontally scrollable on mobile. +- [x] Render each section in one bordered 8px container with a compact section header and 32-36px controls. +- [x] Preserve authoritative LLM clean/dirty/accepted/observed states, current account actions, and read-only advanced values. +- [x] Keep dirty actions at the bottom of the owning container and sticky only when needed; ensure they do not cover mobile inputs. + +### Task 7: Focused verification and delivery + +**Files:** +- Review every file changed by Tasks 1-6. + +- [x] Run the frontend scope analyzer with the task start commit as base and use only its changed source/test/static-check files. +- [x] Run directly changed tests and dependency-related Jest tests only. Do not run the full frontend suite. +- [x] Run Biome with explicit changed frontend file paths. Skip package-wide typecheck because no repository-native affected typecheck target exists; GitHub CI owns full type verification. +- [x] Run `bash tools/ci/test_stability_guards.sh`, `bash tools/docs/lint.sh`, baseline verification, and `git diff --check`. +- [x] Verify real pages at `1440x900`, `834x1112`, and `390x844`, including local table scroll and page-level overflow checks. +- [x] Review `git diff`, stage only this task's files, commit `Align Workflow Activity with NyxID`, push the existing feature branch, update issue `#3194`, and update PR `#3189` with exact focused commands and the GitHub CI delegation statement. diff --git a/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-list-filter-semantics.md b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-list-filter-semantics.md new file mode 100644 index 0000000000..8d6575dd76 --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-05-workflow-list-filter-semantics.md @@ -0,0 +1,408 @@ +# Workflow List Filter Semantics Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Align Workflow and Activity list filtering without inventing unsupported Workflow states. + +**Architecture:** Keep resource-specific filter semantics while sharing one toolbar interaction model. Workflows derives Draft membership only from the real scoped draft API; Activity keeps its real Run API filters, and both pages synchronize list state with the URL. + +**Tech Stack:** React 19, TypeScript, TanStack Query, Ant Design, Umi locale, Jest, Testing Library, Biome. + +--- + +## Task 1: Specify Workflow filter behavior in tests + +**Files:** + +- Modify: `src/pages/workflow-activity-vnext/index.test.tsx` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [ ] Replace the pathname-only test location with a pathname-plus-search snapshot so `useConsoleLocation()` sees copied Workflow URLs: + +```tsx +let mockLocation = '/scopes/scope-alpha/workflow-activity-vnext/workflows'; + +const readMockUrl = () => new URL(mockLocation, 'http://console.local'); + +useLocation: () => ({ + hash: '', + pathname: readMockUrl().pathname, + search: readMockUrl().search, +}), + +getLocationSnapshot: () => + `${readMockUrl().pathname}${readMockUrl().search}`, +``` + +- [ ] Add focused tests with distinct identities (`wf-draft-alpha`, `wf-committed-beta`) proving: + +```tsx +mockLocation = + '/scopes/scope-alpha/workflow-activity-vnext/workflows?q=support&view=drafts'; + +expect(await screen.findByText('Support triage')).toBeInTheDocument(); +expect(screen.queryByText('Invoice review')).not.toBeInTheDocument(); +expect(screen.getByRole('searchbox', { name: 'Search workflows' })).toHaveValue( + 'support', +); +expect(screen.getByRole('combobox', { name: 'Workflow view' })).toHaveTextContent( + 'Drafts', +); +``` + +```tsx +fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Workflow view' })); +expect(await screen.findByRole('option', { name: 'All workflows' })).toBeVisible(); +expect(screen.getByRole('option', { name: 'Drafts' })).toBeVisible(); +expect(screen.queryByRole('option', { name: 'Committed' })).not.toBeInTheDocument(); +expect(screen.queryByRole('option', { name: 'Published' })).not.toBeInTheDocument(); +expect(screen.queryByRole('option', { name: 'Failing' })).not.toBeInTheDocument(); +``` + +```tsx +fireEvent.change(screen.getByRole('searchbox', { name: 'Search workflows' }), { + target: { value: 'invoice' }, +}); +await waitFor(() => + expect(history.replace).toHaveBeenLastCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/workflows?q=invoice', + ), +); +``` + +```tsx +mockStudioApi.listWorkflowDrafts.mockRejectedValue(new Error('draft source down')); +mockLocation = + '/scopes/scope-alpha/workflow-activity-vnext/workflows?view=drafts'; +expect(await screen.findByText('Draft workflows unavailable')).toBeInTheDocument(); +expect(screen.getByRole('button', { name: 'Retry workflows' })).toBeEnabled(); +expect(screen.queryByText('No workflows yet')).not.toBeInTheDocument(); +``` + +- [ ] Run the Workflow test and confirm RED because the view select, URL-backed search/view, clear action, and draft-unavailable state do not exist yet: + +```bash +pnpm exec jest src/pages/workflow-activity-vnext/index.test.tsx --runInBand +``` + +Expected: the newly added assertions fail for missing `Workflow view`, missing URL restoration, or missing draft-unavailable state; unrelated existing tests remain runnable. + +## Task 2: Implement the Workflow view filter and honest draft failure + +**Files:** + +- Modify: `src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx` +- Modify: `src/locales/workflowActivityVNextMessages.en-US.ts` +- Modify: `src/locales/workflowActivityVNextMessages.zh-CN.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` +- Test: `src/locales/catalog.test.ts` + +- [ ] Import `Select`, `useConsoleLocation`, and move Refresh into the shell header next to New workflow: + +```tsx +headerActions={ + + + + +} +``` + +- [ ] Restore `query` and `view` from `location.search`, synchronize browser navigation into state, and serialize only non-default values: + +```tsx +type WorkflowView = 'all' | 'drafts'; + +function readWorkflowView(params: URLSearchParams): WorkflowView { + return params.get('view') === 'drafts' ? 'drafts' : 'all'; +} + +const location = useConsoleLocation(); +const initialParams = React.useMemo( + () => new URLSearchParams(location.search), + [location.search], +); +const [query, setQuery] = React.useState(initialParams.get('q') ?? ''); +const [view, setView] = React.useState( + readWorkflowView(initialParams), +); + +React.useEffect(() => { + const params = new URLSearchParams(location.search); + setQuery(params.get('q') ?? ''); + setView(readWorkflowView(params)); +}, [location.search]); + +React.useEffect(() => { + const params = new URLSearchParams(); + if (query.trim()) params.set('q', query.trim()); + if (view === 'drafts') params.set('view', 'drafts'); + const suffix = params.toString(); + history.replace(`${location.pathname}${suffix ? `?${suffix}` : ''}`); +}, [location.pathname, query, view]); +``` + +- [ ] Build draft membership exclusively from exact API-returned Workflow IDs and apply it before text search: + +```tsx +const draftWorkflowIds = React.useMemo( + () => new Set((drafts.data ?? []).map((item) => item.workflowId)), + [drafts.data], +); + +return [...merged.values()] + .filter((item) => view !== 'drafts' || draftWorkflowIds.has(item.workflowId)) + .filter((item) => { + const normalized = query.trim().toLowerCase(); + return ( + !normalized || + [item.name, item.description, item.workflowId].some((value) => + value.toLowerCase().includes(normalized), + ) + ); + }) + .sort(/* retain current updated-at ordering */); +``` + +- [ ] Add the two-option view Select. Disable only Drafts when the draft source failed; do not add Committed, Published, Failing, Ready, or inferred lifecycle values: + +```tsx + + + {/* page-specific Select controls */} + +``` + +- [ ] Add stable dimensions and mobile full-width behavior without changing other vNext tables or the shell: + +```css +.wa-vnext__toolbar-search { flex: 0 1 360px; max-width: 100%; width: 360px; } +.wa-vnext__toolbar-filters { justify-content: flex-end; } +.wa-vnext__toolbar-filters .ant-select { min-width: 160px; } + +@media (max-width: 600px) { + .wa-vnext__toolbar-search { flex-basis: auto; width: 100%; } + .wa-vnext__toolbar-filters { display: grid; grid-template-columns: 1fr; width: 100%; } + .wa-vnext__toolbar-filters .ant-select, + .wa-vnext__toolbar-filters .ant-space-item, + .wa-vnext__toolbar-filters .ant-btn { width: 100%; } +} +``` + +- [ ] Run the two page tests after the markup/style refactor: + +```bash +pnpm exec jest \ + src/pages/workflow-activity-vnext/index.test.tsx \ + src/pages/workflow-activity-vnext/activity/ActivityPage.test.tsx \ + --runInBand +``` + +Expected: both page test files pass. + +## Task 5: Focused verification, browser evidence, and delivery + +**Files:** + +- Verify only the files changed by Tasks 1-4 and this plan. + +- [ ] Run the exact selected test set: + +```bash +pnpm exec jest \ + src/pages/workflow-activity-vnext/index.test.tsx \ + src/pages/workflow-activity-vnext/activity/ActivityPage.test.tsx \ + src/locales/catalog.test.ts \ + --runInBand +``` + +- [ ] Run dependency-selected tests: + +```bash +pnpm exec jest --findRelatedTests \ + src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx \ + src/pages/workflow-activity-vnext/activity/ActivityPage.tsx \ + src/pages/workflow-activity-vnext/styles.ts \ + src/locales/workflowActivityVNextMessages.en-US.ts \ + src/locales/workflowActivityVNextMessages.zh-CN.ts \ + --runInBand +``` + +- [ ] Compute the frontend-only change scope: + +```bash +python3 /Users/abigaildeng/.codex/skills/frontend-incremental-pr/scripts/frontend_change_scope.py \ + --repo . --base HEAD +``` + +- [ ] Run Biome only on the analyzer's exact changed frontend files, for example: + +```bash +pnpm exec biome check \ + src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx \ + src/pages/workflow-activity-vnext/activity/ActivityPage.tsx \ + src/pages/workflow-activity-vnext/index.test.tsx \ + src/pages/workflow-activity-vnext/activity/ActivityPage.test.tsx \ + src/pages/workflow-activity-vnext/styles.ts \ + src/locales/workflowActivityVNextMessages.en-US.ts \ + src/locales/workflowActivityVNextMessages.zh-CN.ts +``` + +- [ ] Run whitespace validation: + +```bash +git diff --check +``` + +- [ ] Do not run the full frontend test suite, package-wide lint, package-wide typecheck, or production build locally. Record that GitHub CI owns full verification under the machine-wide `frontend-incremental-pr` policy. + +- [ ] Verify the already-running real frontend at `http://localhost:5173` without mock data at desktop `1440x900`, tablet `834x1112`, and mobile `390x844`. Capture screenshots proving the resource-specific filters, header Refresh placement, URL restoration, responsive full-width controls, and lack of horizontal overflow. Record any real remote API or authentication gap instead of synthesizing success. + +- [ ] Review `git diff --stat`, `git diff`, and `git status --short`. Confirm every changed path is inside `apps/aevatar-console-web/`, no legacy route/auth/menu/API adapter changed, and no unsupported Workflow state or identity conversion was introduced. + +- [ ] Stage only this task's files, commit with: + +```bash +git commit -m "Align Workflow and Activity filters" +``` + +- [ ] Push `feat/2026-08-04_workflow-activity-vnext-implementation` and update Draft PR #3189 with exact focused commands/results plus the CI-delegated full checks. Keep PR #3189 Draft. Do not change PR #3187, enable auto-merge, or merge either PR. diff --git a/apps/aevatar-console-web/docs/superpowers/plans/2026-08-06-workflow-activity-vnext-scoped-publication.md b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-06-workflow-activity-vnext-scoped-publication.md new file mode 100644 index 0000000000..e22c26fdd8 --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/plans/2026-08-06-workflow-activity-vnext-scoped-publication.md @@ -0,0 +1,228 @@ +# Workflow Activity vNext Scoped Publication Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Let an operator publish a saved, valid vNext Workflow to one explicitly selected real scope service, then observe the accepted update through exact workflow and service-revision reads. + +**Architecture:** A vNext-local publication hook owns the accepted receipt and bounded observation state. A focused dialog loads only real scope services, requires a deliberate service selection, and collects any real external-request confirmations before calling the existing typed `studioApi.saveAndBindWorkflow` adapter. The editor remains the owner of document validation and serialization; no Team/member API, inferred service identity, mock data, or browser-storage state is introduced. + +**Tech Stack:** React 19, TypeScript, Ant Design, TanStack Query, existing Studio/scope-runtime adapters, Jest, Testing Library, Biome. + +**Design baseline:** + +```text +Design baseline: + apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/ +Primary design: + aevatar-workflow-activity-vnext.excalidraw +Design SHA-256: + 30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de +Contract specification: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-design.md +User paths: + apps/aevatar-console-web/docs/superpowers/specs/ + 2026-08-04-workflow-activity-vnext-user-paths.md +Production data source: + Real APIs and API-acknowledged user actions only; no mock fallback. +``` + +--- + +### Task 1: Receipt-Bound Publication Observation + +**Files:** +- Create: `src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.ts` +- Create: `src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.test.ts` + +- [ ] **Step 1: Write failing observation tests** + +Cover these public behaviors with distinct fixtures: + +```ts +it('observes only the accepted workflow, service, and revision once the active serving revision is published', async () => { + const result = await observeWorkflowPublication({ + receipt: { scopeId: 'scope-alpha', workflowId: 'wf-alpha', revisionId: 'rev-alpha', serviceId: 'svc-alpha' }, + readWorkflow: async () => readableWorkflowDetail('scope-alpha', 'wf-alpha'), + readRevisions: async () => revisionCatalog('scope-alpha', 'svc-alpha', publishedActiveRevision('rev-alpha')), + delaysMs: [0], + }); + + expect(result.kind).toBe('observed'); +}); + +it('treats receipt-bound workflow 404 and 409 plus a missing revision as observation delay without creating another publish request', async () => { + const result = await observeWorkflowPublication({ + receipt: { scopeId: 'scope-alpha', workflowId: 'wf-alpha', revisionId: 'rev-alpha', serviceId: 'svc-alpha' }, + readWorkflow: async () => { throw httpStatusError(409); }, + readRevisions: async () => revisionCatalog('scope-alpha', 'svc-alpha', null), + delaysMs: [0], + }); + + expect(result).toEqual({ kind: 'delayed' }); +}); +``` + +- [ ] **Step 2: Run the new observation test to verify it fails** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web exec jest --runInBand --runTestsByPath src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.test.ts +``` + +Expected: FAIL because `observeWorkflowPublication` and its receipt-bound observation behavior do not exist. + +- [ ] **Step 3: Implement the minimal observation hook** + +Export a `WorkflowPublicationReceipt` with only API-returned `scopeId`, `workflowId`, `revisionId`, and `serviceId`. `observeWorkflowPublication` must query the exact `scopesApi.getWorkflowDetail` and `scopeRuntimeApi.getServiceRevisions` identities in parallel on each bounded attempt. Return `observed` only when: + +```ts +workflow.available === true && +workflow.scopeId === receipt.scopeId && +workflow.workflow?.workflowId === receipt.workflowId && +catalog.scopeId === receipt.scopeId && +catalog.serviceId === receipt.serviceId && +catalog.activeServingRevisionId === receipt.revisionId && +revision.revisionId === receipt.revisionId && +revision.implementationKind === 'workflow' && +normalize(revision.status) === 'published' && +revision.isActiveServing && +revision.isServingTarget && +revision.allocationWeight > 0 && +normalize(revision.servingState) === 'active' +``` + +Treat workflow `404`/`409`, service catalog `404`, and a catalog lacking the exact revision as eventual-consistency observation states. Map `401` and `403` distinctly. Treat `PreparationFailed`, a nonempty service failure reason, and `Retired` as terminal failure. The hook retry calls only the exact read functions and never calls `saveAndBindWorkflow`. + +- [ ] **Step 4: Run the observation test to verify it passes** + +Run the same Jest command from Step 2. + +Expected: PASS with no timer-driven artificial success state. + +### Task 2: Explicit Target Selection And External-Request Review + +**Files:** +- Create: `src/pages/workflow-activity-vnext/workflows/WorkflowPublishDialog.tsx` +- Modify: `src/locales/workflowActivityVNextMessages.en-US.ts` +- Modify: `src/locales/workflowActivityVNextMessages.zh-CN.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [ ] **Step 1: Write failing route-integration tests** + +Add tests that render the real vNext editor through its route owner and prove: + +```ts +it('requires an explicitly selected real scope service before publishing a saved workflow', async () => { + // Open Publish, wait for real service rows, and assert the primary submit action is disabled. + // Select 'Service alpha', submit, and assert saveAndBindWorkflow receives serviceId: 'svc-alpha'. +}); + +it('keeps publish accepted while the exact workflow and service revision are still being observed', async () => { + // Return 202 data, hold or delay the exact GETs, and assert no success toast or ready claim. +}); + +it('retries only observation reads after a delayed publication', async () => { + // Make the receipt-bound reads return 404/409, click Check again, and assert one POST total. +}); + +it('renders unauthorized and forbidden publication observation states distinctly', async () => { + // Exercise 401 and 403 from an exact observation read through the rendered alert. +}); +``` + +- [ ] **Step 2: Run only the named new route tests to verify they fail** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web exec jest --runInBand --runTestsByPath src/pages/workflow-activity-vnext/index.test.tsx --testNamePattern 'explicitly selected real scope service|publish accepted|retries only observation reads|unauthorized and forbidden publication' +``` + +Expected: FAIL because the editor's current Publish action is disabled and no publication dialog or observation UI exists. + +- [ ] **Step 3: Implement the focused dialog** + +The dialog must load `scopeRuntimeApi.listServices(scopeId, { take: 200 })` only while open. It begins with no selection, uses real service display names, and has distinct loading, empty, failure, retry, unauthorized, and forbidden content. It must never choose or submit a default service on the user's behalf. + +On submission, use an exact editor-generated publication snapshot and call `studioApi.previewExplicitRequests`. When preview items exist, render an in-dialog review using only relevant user decisions: method/path, risk, and whether approval is required. Keep call-site IDs and request digests out of the default UI; use them only to construct the confirmation payload. A user cancellation returns to the target-selection state without making a publish POST. + +- [ ] **Step 4: Run the named route tests to verify they pass** + +Run the command from Step 2. + +Expected: PASS; each test uses a different `workflowId`, `serviceId`, and `revisionId` fixture identity. + +### Task 3: Editor Integration And Honest User Feedback + +**Files:** +- Modify: `src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts` +- Modify: `src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx` +- Modify: `src/pages/workflow-activity-vnext/workflows/WorkflowPublishDialog.tsx` +- Modify: `src/locales/workflowActivityVNextMessages.en-US.ts` +- Modify: `src/locales/workflowActivityVNextMessages.zh-CN.ts` +- Test: `src/pages/workflow-activity-vnext/index.test.tsx` + +- [ ] **Step 1: Add the smallest editor publication preparation boundary** + +Expose a function from `useWorkflowEditor` that parses and serializes the current document only when it is saved, valid, and a real draft. It returns the exact draft `workflowId`, display name, and serialized YAML, or an actionable validation failure. It must not fabricate a revision, modify the current draft, or save silently. + +- [ ] **Step 2: Wire the dialog to the existing typed adapters** + +Use `createWorkflowRevisionIdentityCandidate()` only as the required request identity candidate. Pass the selected service ID exactly to `studioApi.saveAndBindWorkflow`; capture only API-returned `workflowId`, `revisionId`, and `binding.serviceId` as the observation receipt. Reject a missing binding service ID, scope mismatch, or returned target mismatch as a visible publish failure rather than substituting any identity. + +- [ ] **Step 3: Render persistent state and one completed-action toast** + +Keep the receipt-bound state in the editor while it is mounted: + +```text +Ready -> Reviewing -> Submitting -> Accepted -> Observing -> Published + | | + | +-> Delayed -> Check again (GET only) + +-> Failed / Unauthorized / Forbidden +``` + +Use inline alerts for every nonterminal phase. Emit one localized `ConsoleToast` success only after exact workflow and active service-revision evidence reaches `Published`; do not toast for a click, preview request, or `202 Accepted` response. Never claim full invocation readiness because no public HTTP contract proves it. + +- [ ] **Step 4: Run the focused route and hook tests to verify the integrated flow passes** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web exec jest --runInBand --runTestsByPath src/pages/workflow-activity-vnext/index.test.tsx src/pages/workflow-activity-vnext/hooks/useWorkflowPublication.test.ts +``` + +Expected: PASS. + +### Task 4: Focused Verification And Delivery + +**Files:** +- Review only the current task's vNext hook, dialog, editor, locale, test, and plan files. + +- [ ] **Step 1: Re-run the frontend scope analyzer** + +Run: + +```bash +python3 /Users/abigaildeng/.codex/skills/frontend-incremental-pr/scripts/frontend_change_scope.py --repo . --base origin/feat/2026-08-04_workflow-activity-vnext +``` + +- [ ] **Step 2: Run dependency-related tests and changed-file static checks** + +Run explicit changed/new Jest tests plus `pnpm exec jest --findRelatedTests` only for changed shared source files when the analyzer identifies a direct consumer graph. Run `pnpm exec biome check` only with the analyzer's `staticCheckFiles` that belong to this task. + +- [ ] **Step 3: Verify baseline and diff hygiene** + +Run: + +```bash +python3 apps/aevatar-console-web/docs/design-baselines/workflow-activity-vnext/verify-baseline.py +git diff --check -- apps/aevatar-console-web +``` + +Expected: declared SHA, byte-identical generator output, and 17/17 frame inventory pass; no whitespace errors. + +- [ ] **Step 4: Review and deliver only task-owned changes** + +Review the complete diff. Stage only the publication task's frontend files, commit with an imperative message, push the implementation branch, and create or update the implementation Draft PR. Do not modify, ready, merge, or auto-merge PR #3187. Full frontend suite, package-wide lint, package-wide TypeScript, and production build are delegated to GitHub CI under the personal incremental frontend policy. diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-08-05-workflow-list-filter-semantics-design.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-05-workflow-list-filter-semantics-design.md new file mode 100644 index 0000000000..80b46a64cc --- /dev/null +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-08-05-workflow-list-filter-semantics-design.md @@ -0,0 +1,135 @@ +# Workflow List Filter Semantics Design + +## Status + +Approved in conversation on 2026-08-05. This document records the product +semantics for aligning the Workflow and Activity list toolbars before the +frontend implementation changes. + +This design supplements, but does not modify, the Workflow Activity vNext +design baseline. Real backend responses remain authoritative over prototype +labels and demonstration data. + +## Mismatch + +The UI currently makes the two list pages appear inconsistent: Activity has +categorical filters while Workflows exposes only search and refresh. Copying +Activity's filters into Workflows would still be wrong because the pages own +different resources and facts. + +- Activity lists Runs. Its status and source are authoritative Run API fields. +- Workflows merges the scoped Workflow list with the scoped draft list. + `draft` and `committed` describe frontend merge inputs, not two mutually + exclusive product lifecycle states. +- A Workflow can have both a committed source and a draft. The frontend must + not present `Committed` as the opposite of `Draft`. +- The current backend does not provide the published identity, last-Run + outcome, or other facts required to implement the prototype's `Published` + and `Failing` Workflow filters honestly. + +The semantic owner of Workflow filtering is therefore the Workflow catalogue, +using only facts returned by its existing scoped Workflow and draft APIs. Run +status and source remain owned by Activity. + +## Decision + +Both pages use the same toolbar grammar: + +- search on the left; +- resource-specific categorical filters on the right; +- refresh in the page header rather than mixed into the filter group; +- active search and filter values represented in the URL; +- an honest filtered-empty state with a clear-filters action. + +Activity keeps its existing Run status and Run source filters. + +Workflows adds one select with two options: + +| Option | Product meaning | Evidence | +| --- | --- | --- | +| `All workflows` | Every row produced by the existing merge of scoped Workflows and scoped drafts | Successful results from the two existing list APIs | +| `Drafts` | Workflows whose exact `workflowId` is present in `studioApi.listWorkflowDrafts(scopeId)` | A real draft summary returned by that API | + +The Workflows filter must not include `Committed`, `Published`, `Failing`, +`Ready`, or similar values until a real contract supplies an unambiguous, +user-facing fact for that option. + +## Data And URL Behavior + +The existing Workflow merge remains authoritative and unchanged: + +1. Load the scoped committed Workflow summaries. +2. Load the scoped draft summaries. +3. Merge by exact `workflowId` without inferring identity from names, prefixes, + route position, member IDs, or service IDs. +4. When `view=drafts`, retain only merged rows whose exact `workflowId` was + returned by the draft API. +5. Apply the text search to the selected view and keep the existing updated-at + ordering. + +The Workflows URL uses: + +- `q=` for non-empty search text; +- `view=drafts` for the Drafts view; +- no `view` parameter for All workflows. + +Activity adds its non-empty search text as `q=` while preserving its +existing `status`, `origin`, `definition`, and `workflowFilter` parameters. +Empty values are omitted. Reloading, browser navigation, or returning to a +copied URL restores the same visible search and filters. + +## Loading, Failure, And Empty States + +- While either Workflow source is loading, retain the existing loading state. +- If the draft source fails, the Drafts option is disabled because the + frontend cannot know which Workflows have drafts. +- A draft-source failure does not hide successfully returned scoped Workflow + rows in All workflows. +- If the user was already viewing Drafts when the draft source fails, show an + unavailable state with Retry rather than an empty result. +- A successful filtered result with zero rows shows `No matching workflows` + and a `Clear filters` action. +- Clearing filters removes `q` and `view` while preserving the scoped route. +- No mock rows, localStorage state, timer results, or successful fallback may + stand in for either API. + +## Presentation And Accessibility + +- Workflows and Activity use the same toolbar spacing, search width, select + height, responsive wrapping, and mobile full-width controls. +- Selects retain visible accessible names through existing localized labels. +- The Drafts option is a normal menu option, not a status badge or lifecycle + claim. +- Keyboard, touch, and screen-reader behavior continue through Ant Design's + existing Input and Select controls. +- All new copy is added to both the `en-US` and `zh-CN` catalogues. + +## Test Contract + +Focused tests must prove that: + +- Workflows filters Drafts by exact draft API membership while All workflows + still contains committed-only and draft-backed rows. +- Workflows never renders unsupported `Committed`, `Published`, or `Failing` + filter options. +- Workflows search and view values restore from and write to the URL. +- Activity search joins its existing URL-backed filters without dropping + `status`, `origin`, `definition`, or `workflowFilter`. +- A failed draft source disables or makes the Drafts view unavailable instead + of presenting a false empty state. +- Clearing filters restores the unfiltered list and scoped URL. +- Both locale catalogues contain every new message key. + +Browser verification uses the running frontend with the real remote backend at +desktop, tablet, and mobile widths. If a remote endpoint is unavailable, the +unavailable state is recorded as a verification gap; test fixtures are not +used to fabricate browser evidence. + +## Non-Goals + +- No backend endpoint, DTO, projection, identity, or persistence change. +- No change to existing Workflow, Run, Settings, Studio, Team, member, login, + callback, redirect, session, menu, or locale route behavior. +- No attempt to implement prototype-only Published or Failing filters. +- No identity conversion among `workflowId`, `memberId`, + `definitionActorId`, or `publishedServiceId`. diff --git a/apps/aevatar-console-web/src/app.layout.test.ts b/apps/aevatar-console-web/src/app.layout.test.ts index 8f1f79d89b..ec2fa339d5 100644 --- a/apps/aevatar-console-web/src/app.layout.test.ts +++ b/apps/aevatar-console-web/src/app.layout.test.ts @@ -1,16 +1,22 @@ -import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { getLocale, setLocale } from "@umijs/max"; -import React from "react"; -import defaultSettings from "../config/defaultSettings"; -import { layout } from "./app"; - -describe("layout menu collapse behavior", () => { +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; +import { getLocale, setLocale } from '@umijs/max'; +import React from 'react'; +import defaultSettings from '../config/defaultSettings'; +import { layout } from './app'; + +describe('layout menu collapse behavior', () => { beforeEach(() => { - setLocale("en-US", false); - window.history.replaceState({}, "", "/scopes"); + setLocale('en-US', false); + window.history.replaceState({}, '', '/scopes'); }); - it("keeps grouped navigation titles hidden in collapsed mode", () => { + it('keeps grouped navigation titles hidden in collapsed mode', () => { const runtimeLayout = layout({ initialState: { auth: {} as never, @@ -22,15 +28,15 @@ describe("layout menu collapse behavior", () => { collapsedWidth: 40, collapsedShowGroupTitle: false, collapsedShowTitle: false, - type: "group", + type: 'group', }); }); - it("collapses the global menu for Studio create-member intent", () => { + it('collapses the global menu for Studio create-member intent', () => { window.history.replaceState( {}, - "", - "/studio?tab=studio&intent=create-member", + '', + '/studio?tab=studio&intent=create-member', ); const runtimeLayout = layout({ @@ -44,8 +50,8 @@ describe("layout menu collapse behavior", () => { expect(runtimeLayout.collapsed).toBe(true); }); - it("leaves the global menu uncontrolled for ordinary Studio entry", () => { - window.history.replaceState({}, "", "/studio?tab=studio"); + it('leaves the global menu uncontrolled for ordinary Studio entry', () => { + window.history.replaceState({}, '', '/studio?tab=studio'); const runtimeLayout = layout({ initialState: { @@ -58,8 +64,8 @@ describe("layout menu collapse behavior", () => { expect(runtimeLayout.collapsed).toBeUndefined(); }); - it("hides console chrome for the fullscreen Mission Wall route", () => { - window.history.replaceState({}, "", "/runtime/mission-wall"); + it('hides console chrome for the fullscreen Mission Wall route', () => { + window.history.replaceState({}, '', '/runtime/mission-wall'); const runtimeLayout = layout({ initialState: { @@ -75,18 +81,51 @@ describe("layout menu collapse behavior", () => { | undefined; expect(runtimeLayout.headerRender).toBe(false); - expect(menuRender?.({}, React.createElement("nav"))).toBe(false); + expect(menuRender?.({}, React.createElement('nav'))).toBe(false); expect(actionsRender?.({}, {})).toEqual([]); expect(runtimeLayout.contentStyle).toMatchObject({ - background: "#09110f", - height: "100vh", - overflow: "hidden", + background: '#09110f', + height: '100vh', + overflow: 'hidden', padding: 0, }); }); - it("updates the controlled global menu collapse state after SPA route changes", () => { - window.history.replaceState({}, "", "/scopes/scope-a/teams"); + it('renders Workflow Activity vNext without the global console chrome', () => { + window.history.replaceState( + {}, + '', + '/scopes/scope-a/workflow-activity-vnext/workflows/wf-a', + ); + + const runtimeLayout = layout({ + initialState: { + auth: {} as never, + settings: defaultSettings, + }, + }); + const menuRender = runtimeLayout.menuRender as + | ((props: unknown, defaultDom: unknown) => React.ReactNode) + | undefined; + const actionsRender = runtimeLayout.actionsRender as + | ((props: unknown, dom: unknown) => React.ReactNode[]) + | undefined; + + expect(runtimeLayout.headerRender).toBe(false); + expect(menuRender?.({}, React.createElement('nav'))).toBe(false); + expect(actionsRender?.({}, {})).toEqual([]); + expect(runtimeLayout.contentStyle).toMatchObject({ + background: '#ffffff', + height: 'auto', + inset: 0, + overflow: 'hidden', + padding: 0, + position: 'fixed', + }); + }); + + it('updates the controlled global menu collapse state after SPA route changes', () => { + window.history.replaceState({}, '', '/scopes/scope-a/teams'); const teamsLayout = layout({ initialState: { auth: {} as never, @@ -94,7 +133,7 @@ describe("layout menu collapse behavior", () => { }, }); - window.history.pushState({}, "", "/studio?tab=studio&intent=create-member"); + window.history.pushState({}, '', '/studio?tab=studio&intent=create-member'); const studioLayout = layout({ initialState: { auth: {} as never, @@ -106,7 +145,7 @@ describe("layout menu collapse behavior", () => { expect(studioLayout.collapsed).toBe(true); }); - it("renders a global language switch in the layout actions", async () => { + it('renders a global language switch in the layout actions', async () => { const runtimeLayout = layout({ initialState: { auth: {} as never, @@ -117,24 +156,18 @@ describe("layout menu collapse behavior", () => { | ((props: unknown, dom: unknown) => React.ReactNode[]) | undefined; - render( - React.createElement( - React.Fragment, - null, - actionsRender?.({}, {}), - ), - ); + render(React.createElement(React.Fragment, null, actionsRender?.({}, {}))); - fireEvent.click(screen.getByRole("button", { name: "Switch language" })); - fireEvent.click(await screen.findByText("中文")); + fireEvent.click(screen.getByRole('button', { name: 'Switch language' })); + fireEvent.click(await screen.findByText('中文')); await waitFor(() => { - expect(getLocale()).toBe("zh-CN"); + expect(getLocale()).toBe('zh-CN'); }); }); - it("keeps page content in sync when the locale changes without a reload", async () => { - window.history.replaceState({}, "", "/studio"); + it('keeps page content in sync when the locale changes without a reload', async () => { + window.history.replaceState({}, '', '/studio'); const runtimeLayout = layout({ initialState: { auth: {} as never, @@ -149,34 +182,32 @@ describe("layout menu collapse behavior", () => { React.createElement( React.Fragment, null, - childrenRender?.( - React.createElement(LocalizedRuntimeProbe), - ), + childrenRender?.(React.createElement(LocalizedRuntimeProbe)), ), ); - expect(screen.getByText("My AI teams")).toBeTruthy(); + expect(screen.getByText('My AI teams')).toBeTruthy(); act(() => { - setLocale("zh-CN", false); + setLocale('zh-CN', false); }); await waitFor(() => { - expect(screen.getByText("我的 AI 团队")).toBeTruthy(); + expect(screen.getByText('我的 AI 团队')).toBeTruthy(); }); - expect(screen.queryByText("My AI teams")).toBeNull(); + expect(screen.queryByText('My AI teams')).toBeNull(); }); }); const LocalizedRuntimeProbe: React.FC = () => { - const { useIntl } = require("@umijs/max") as typeof import("@umijs/max"); + const { useIntl } = require('@umijs/max') as typeof import('@umijs/max'); const intl = useIntl(); return React.createElement( - "div", + 'div', null, intl.formatMessage({ - id: "teams.home.title", - }), + id: 'teams.home.title', + }), ); }; diff --git a/apps/aevatar-console-web/src/app.tsx b/apps/aevatar-console-web/src/app.tsx index c8263aa0ec..1c12ba0ffc 100644 --- a/apps/aevatar-console-web/src/app.tsx +++ b/apps/aevatar-console-web/src/app.tsx @@ -1,61 +1,70 @@ -import { ProConfigProvider } from "@ant-design/pro-components"; -import { QueryClientProvider } from "@tanstack/react-query"; -import { Badge, ConfigProvider } from "antd"; -import { getLocale, useIntl } from "@umijs/max"; -import React from "react"; -import MainLayout from "@/layouts/MainLayout"; -import { history } from "./shared/navigation/history"; -import { CONSOLE_HOME_ROUTE } from "@/shared/navigation/consoleHome"; -import BrandLogo from "@/components/BrandLogo"; -import defaultSettings from "../config/defaultSettings"; -import { errorConfig } from "./requestErrorConfig"; +import { ProConfigProvider } from '@ant-design/pro-components'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { getLocale, useIntl } from '@umijs/max'; +import { Badge, ConfigProvider } from 'antd'; +import React from 'react'; +import BrandLogo from '@/components/BrandLogo'; +import MainLayout from '@/layouts/MainLayout'; +import { buildMissionSnapshotFromRuntime } from '@/pages/MissionControl/runtimeAdapter'; +import { readMissionControlRouteContext } from '@/pages/MissionControl/services/api'; +import { runtimeActorsApi } from '@/shared/api/runtimeActorsApi'; +import { runtimeRunsApi } from '@/shared/api/runtimeRunsApi'; +import { + normalizeConsoleLocale, + resolveAntdLocale, + resolveProIntl, +} from '@/shared/i18n/localeProvider'; +import { CONSOLE_HOME_ROUTE } from '@/shared/navigation/consoleHome'; +import { loadRecentRuns } from '@/shared/runs/recentRuns'; +import { AevatarPageLoading } from '@/shared/ui/AevatarLoading'; +import { aevatarThemeConfig } from '@/shared/ui/aevatarWorkbench'; +import { ConsoleHeaderActions } from '@/shared/ui/ConsoleHeaderActions'; +import { ConsoleToastProvider } from '@/shared/ui/ConsoleToast'; +import defaultSettings from '../config/defaultSettings'; +import { errorConfig } from './requestErrorConfig'; import { ensureActiveAuthSession, hasRestorableAuthSession, -} from "./shared/auth/client"; -import { getNyxIDRuntimeConfig } from "./shared/auth/config"; +} from './shared/auth/client'; +import { getNyxIDRuntimeConfig } from './shared/auth/config'; +import { ProtectedRouteRedirectGate } from './shared/auth/ProtectedRouteRedirectGate'; import { buildAuthInitialState, - loadRestorableAuthSession, loadStoredAuthSession, sanitizeReturnTo, -} from "./shared/auth/session"; -import { ProtectedRouteRedirectGate } from "./shared/auth/ProtectedRouteRedirectGate"; +} from './shared/auth/session'; +import { history } from './shared/navigation/history'; import { getNavigationGroupOrder, type NavigationGroup, -} from "./shared/navigation/navigationGroups"; -import { getNavigationSelectedKeys } from "./shared/navigation/navigationMenuSelection"; +} from './shared/navigation/navigationGroups'; import { groupNavigationMenuItems, type NavigationMenuItem, -} from "./shared/navigation/navigationMenuGrouping"; -import { runtimeActorsApi } from "@/shared/api/runtimeActorsApi"; -import { runtimeRunsApi } from "@/shared/api/runtimeRunsApi"; -import { buildMissionSnapshotFromRuntime } from "@/pages/MissionControl/runtimeAdapter"; -import { readMissionControlRouteContext } from "@/pages/MissionControl/services/api"; -import { loadRecentRuns } from "@/shared/runs/recentRuns"; -import { queryClient } from "./shared/query/queryClient"; -import { aevatarThemeConfig } from "@/shared/ui/aevatarWorkbench"; -import { ConsoleHeaderActions } from "@/shared/ui/ConsoleHeaderActions"; -import { - normalizeConsoleLocale, - resolveAntdLocale, - resolveProIntl, -} from "@/shared/i18n/localeProvider"; -import { AevatarPageLoading } from "@/shared/ui/AevatarLoading"; +} from './shared/navigation/navigationMenuGrouping'; +import { getNavigationSelectedKeys } from './shared/navigation/navigationMenuSelection'; +import { queryClient } from './shared/query/queryClient'; -const PUBLIC_ROUTES = new Set(["/login", "/auth/callback"]); +const PUBLIC_ROUTES = new Set(['/login', '/auth/callback']); const DEFAULT_PROTECTED_ROUTE = CONSOLE_HOME_ROUTE; -const FULLSCREEN_DISPLAY_ROUTES = new Set(["/runtime/mission-wall"]); +const FULLSCREEN_DISPLAY_ROUTES = new Set(['/runtime/mission-wall']); +const WORKFLOW_ACTIVITY_VNEXT_ROUTE = + /^\/scopes\/[^/]+\/workflow-activity-vnext(?:\/|$)/; const STUDIO_HOST_ROUTES = new Set([ - "/studio", - "/scopes/:scopeId/teams/:teamId/members/new/workflow", - "/scopes/:scopeId/teams/:teamId/members/:memberId/workflow", + '/studio', + '/scopes/:scopeId/teams/:teamId/members/new/workflow', + '/scopes/:scopeId/teams/:teamId/members/:memberId/workflow', ]); function isFullscreenDisplayRoute(pathname: string): boolean { - return FULLSCREEN_DISPLAY_ROUTES.has(pathname); + return ( + FULLSCREEN_DISPLAY_ROUTES.has(pathname) || + WORKFLOW_ACTIVITY_VNEXT_ROUTE.test(pathname) + ); +} + +function isWorkflowActivityVNextRoute(pathname: string): boolean { + return WORKFLOW_ACTIVITY_VNEXT_ROUTE.test(pathname); } function isStudioHostRoute(pathname: string): boolean { @@ -63,19 +72,20 @@ function isStudioHostRoute(pathname: string): boolean { return true; } - return ( - /^\/scopes\/[^/]+\/teams\/[^/]+\/members\/(?:new|[^/]+)\/workflow$/.test( - pathname, - ) + return /^\/scopes\/[^/]+\/teams\/[^/]+\/members\/(?:new|[^/]+)\/workflow$/.test( + pathname, ); } -function shouldDefaultCollapseLayout(pathname: string, search: string): boolean { +function shouldDefaultCollapseLayout( + pathname: string, + search: string, +): boolean { if (!isStudioHostRoute(pathname)) { return false; } - return new URLSearchParams(search).get("intent") === "create-member"; + return new URLSearchParams(search).get('intent') === 'create-member'; } function shouldCollapseLayout(pathname: string, search: string): boolean { @@ -90,7 +100,7 @@ function buildLoginRoute(returnTo: string): string { } function getCurrentReturnTo(pathname: string): string { - return pathname === "/" + return pathname === '/' ? DEFAULT_PROTECTED_ROUTE : `${pathname}${window.location.search}${window.location.hash}`; } @@ -141,20 +151,21 @@ type ConsoleRuntimeProvidersProps = { search: string; }; -const LIVE_OPS_ATTENTION_BADGE_KEY = "live.attention"; +const LIVE_OPS_ATTENTION_BADGE_KEY = 'live.attention'; const LIVE_OPS_ATTENTION_MAX_CANDIDATES = 6; const LIVE_OPS_ATTENTION_MAX_AGE_MS = 12 * 60 * 60 * 1000; const LIVE_OPS_ATTENTION_REFRESH_MS = 30_000; -const NAVIGATION_GROUP_ORDER: readonly NavigationGroup[] = getNavigationGroupOrder(); +const NAVIGATION_GROUP_ORDER: readonly NavigationGroup[] = + getNavigationGroupOrder(); const NAVIGATION_MENU_MESSAGE_IDS: Readonly> = { - "/chat": "nav.items.chat", - "/scopes": "nav.items.myTeams", - "/runtime/runs": "nav.items.eventStream", - "/services": "nav.items.services", - "/governance": "nav.items.governance", - "/deployments": "nav.items.deployments", - "/runtime/explorer": "nav.items.topology", - "/settings": "nav.items.settings", + '/chat': 'nav.items.chat', + '/scopes': 'nav.items.myTeams', + '/runtime/runs': 'nav.items.eventStream', + '/services': 'nav.items.services', + '/governance': 'nav.items.governance', + '/deployments': 'nav.items.deployments', + '/runtime/explorer': 'nav.items.topology', + '/settings': 'nav.items.settings', }; const LIVE_OPS_DEFAULT_ATTENTION_SNAPSHOT: LiveOpsAttentionSnapshot = { hasPendingAttention: false, @@ -164,11 +175,11 @@ const liveOpsAttentionListeners = new Set<() => void>(); let liveOpsAttentionSnapshot = LIVE_OPS_DEFAULT_ATTENTION_SNAPSHOT; const navigationGroupLabelStyle: React.CSSProperties = { - color: "#667085", - display: "inline-flex", + color: '#667085', + display: 'inline-flex', fontSize: 14, fontWeight: 700, - lineHeight: "22px", + lineHeight: '22px', }; const LocalizedNavigationText: React.FC<{ @@ -177,7 +188,7 @@ const LocalizedNavigationText: React.FC<{ }> = ({ defaultLabel, messageId }) => { const intl = useIntl(); const defaultMessage = - typeof defaultLabel === "string" ? defaultLabel : undefined; + typeof defaultLabel === 'string' ? defaultLabel : undefined; return ( <> @@ -225,11 +236,13 @@ function setLiveOpsAttentionSnapshot(next: LiveOpsAttentionSnapshot): void { } liveOpsAttentionSnapshot = next; - liveOpsAttentionListeners.forEach((listener) => listener()); + liveOpsAttentionListeners.forEach((listener) => { + listener(); + }); } function buildLiveOpsAttentionCandidateKey( - candidate: LiveOpsAttentionCandidate + candidate: LiveOpsAttentionCandidate, ): string { const actorId = trimOptional(candidate.actorId); if (actorId) { @@ -237,16 +250,16 @@ function buildLiveOpsAttentionCandidateKey( } return [ - "run", - trimOptional(candidate.scopeId) || "", - trimOptional(candidate.serviceId) || "", - trimOptional(candidate.runId) || "", - ].join(":"); + 'run', + trimOptional(candidate.scopeId) || '', + trimOptional(candidate.serviceId) || '', + trimOptional(candidate.runId) || '', + ].join(':'); } function collectLiveOpsAttentionCandidates( pathname: string, - search: string + search: string, ): LiveOpsAttentionCandidate[] { const nowMs = Date.now(); const deduped = new Map(); @@ -260,7 +273,7 @@ function collectLiveOpsAttentionCandidates( continue; } - if (entry.status === "finished" || entry.status === "error") { + if (entry.status === 'finished' || entry.status === 'error') { continue; } @@ -280,7 +293,7 @@ function collectLiveOpsAttentionCandidates( } } - if (pathname === "/runtime/mission-control") { + if (pathname === '/runtime/mission-control') { const context = readMissionControlRouteContext(search); const candidate: LiveOpsAttentionCandidate = { actorId: trimOptional(context.actorId), @@ -297,11 +310,14 @@ function collectLiveOpsAttentionCandidates( } } - return Array.from(deduped.values()).slice(0, LIVE_OPS_ATTENTION_MAX_CANDIDATES); + return Array.from(deduped.values()).slice( + 0, + LIVE_OPS_ATTENTION_MAX_CANDIDATES, + ); } async function resolveLiveOpsAttentionActorId( - candidate: LiveOpsAttentionCandidate + candidate: LiveOpsAttentionCandidate, ): Promise { const actorId = trimOptional(candidate.actorId); if (actorId) { @@ -325,7 +341,7 @@ async function resolveLiveOpsAttentionActorId( } async function runNeedsLiveOpsAttention( - candidate: LiveOpsAttentionCandidate + candidate: LiveOpsAttentionCandidate, ): Promise { const actorId = await resolveLiveOpsAttentionActorId(candidate); if (!actorId) { @@ -337,7 +353,7 @@ async function runNeedsLiveOpsAttention( const [graph, timeline] = await Promise.all([ runtimeActorsApi.getActorGraphEnriched(actorId, { depth: 4, - direction: "Both", + direction: 'Both', take: 120, }), runtimeActorsApi.getActorTimeline(actorId, { @@ -346,7 +362,7 @@ async function runNeedsLiveOpsAttention( ]); const snapshot = buildMissionSnapshotFromRuntime({ - connectionStatus: "degraded", + connectionStatus: 'degraded', nowMs: fetchedAtMs, recentEvents: [], routeContext: { @@ -363,15 +379,15 @@ async function runNeedsLiveOpsAttention( }, session: { runId: trimOptional(candidate.runId), - status: "running", + status: 'running', }, }, }); return ( snapshot.intervention?.required === true && - (snapshot.intervention.kind === "human_approval" || - snapshot.intervention.kind === "human_input") + (snapshot.intervention.kind === 'human_approval' || + snapshot.intervention.kind === 'human_input') ); } catch { return false; @@ -380,7 +396,7 @@ async function runNeedsLiveOpsAttention( async function loadLiveOpsAttentionSnapshot( pathname: string, - search: string + search: string, ): Promise { const candidates = collectLiveOpsAttentionCandidates(pathname, search); if (candidates.length === 0) { @@ -388,10 +404,10 @@ async function loadLiveOpsAttentionSnapshot( } const results = await Promise.allSettled( - candidates.map((candidate) => runNeedsLiveOpsAttention(candidate)) + candidates.map((candidate) => runNeedsLiveOpsAttention(candidate)), ); const pendingCount = results.reduce((count, result) => { - if (result.status === "fulfilled" && result.value) { + if (result.status === 'fulfilled' && result.value) { return count + 1; } @@ -412,7 +428,7 @@ const NavigationMenuLabel: React.FC<{ const snapshot = React.useSyncExternalStore( subscribeLiveOpsAttention, getLiveOpsAttentionSnapshot, - getLiveOpsAttentionSnapshot + getLiveOpsAttentionSnapshot, ); const showCountBadge = badgeKey === LIVE_OPS_ATTENTION_BADGE_KEY && snapshot.pendingCount > 0; @@ -420,18 +436,18 @@ const NavigationMenuLabel: React.FC<{ return ( {label} @@ -450,10 +466,10 @@ const NavigationMenuLabel: React.FC<{

@@ -96,7 +99,7 @@ export default function ChannelServicePicker({ ) : t( 'channels.connect.servicesEmpty', - 'No services are available with your current NyxID authorization. You can connect this bot without service access.', + 'No services are available with your current NyxID authorization.', )} ) : ( @@ -119,12 +122,12 @@ export default function ChannelServicePicker({ 0 && !allSelected} - disabled={disabled || !selectableIds.length} + disabled={disabled || !optionalIds.length} onChange={(event) => onChange( event.target.checked ? [...new Set([...selectedIds, ...selectableIds])] - : selectedIds.filter((id) => !selectableIds.includes(id)), + : selectedIds.filter((id) => !optionalIds.includes(id)), ) } > @@ -138,6 +141,7 @@ export default function ChannelServicePicker({ visible.map((service) => { const selected = selectedIds.includes(service.id); const unavailable = !service.active || !service.allowed; + const required = requiredIds.includes(service.id); return (

onChange( event.target.checked @@ -159,6 +165,9 @@ export default function ChannelServicePicker({ {service.slug} + {required + ? ` (${t('channels.connect.serviceRequired', 'Required')})` + : null} @@ -186,14 +195,31 @@ export default function ChannelServicePicker({
)} - {!usesDefaults ? ( -

- {t( - 'channels.connect.selectionHelp', - 'Only selected services will be available to this bot.', - )} -

+ {!loading && !failed && missingRequiredSlugs.length ? ( +
+

+ {t( + 'channels.connect.requiredServicesMissing', + 'Required services unavailable: {services}. Check your NyxID access, then retry.', + { services: missingRequiredSlugs.join(', ') }, + )} +

+ +
) : null} +

+ {replacesDefaults + ? t( + 'channels.edit.replaceDefaults', + 'Saving replaces NyxID default access with the selected services.', + ) + : t( + 'channels.connect.selectionHelp', + 'Only selected services will be available to this bot.', + )} +

); }
); -const ScopeServiceRuntimeWorkbench: React.FC = ({ +const ScopeServiceRuntimeWorkbench: React.FC< + ScopeServiceRuntimeWorkbenchProps +> = ({ scopeId, services, selectedServiceId, @@ -213,37 +552,52 @@ const ScopeServiceRuntimeWorkbench: React.FC preferredServiceId, onSelectionChange, }) => { - const [messageApi, messageContextHolder] = message.useMessage(); const queryClient = useQueryClient(); - const [activeTab, setActiveTab] = useState("overview"); + const [activeTab, setActiveTab] = useState('overview'); const [bindingEditorState, setBindingEditorState] = useState(null); const [bindingEditorDraft, setBindingEditorDraft] = useState(createEmptyBindingDraft()); + const [bindingEditorValidationErrors, setBindingEditorValidationErrors] = + useState({}); const [bindingEditorSubmitting, setBindingEditorSubmitting] = useState(false); - const [selectedRevisionId, setSelectedRevisionId] = useState(""); + const [runtimeActionStates, setRuntimeActionStates] = useState< + ReadonlyMap + >(() => new Map()); + const [selectedRevisionId, setSelectedRevisionId] = useState(''); const [selectedRunAuditTarget, setSelectedRunAuditTarget] = useState(null); - const [retiringBindingId, setRetiringBindingId] = useState(""); + const [retiringBinding, setRetiringBinding] = + useState(null); + const runtimeActionRequestId = useRef(0); + const runtimeActionStatesRef = useRef>( + new Map(), + ); const [internalSelectedServiceId, setInternalSelectedServiceId] = useState( - () => initialServiceId?.trim() || preferredServiceId?.trim() || "", + () => initialServiceId?.trim() || preferredServiceId?.trim() || '', ); const [internalSelectedEndpointId, setInternalSelectedEndpointId] = - useState(""); + useState(''); const isControlledSelection = - typeof selectedServiceId === "string" && - typeof selectedEndpointId === "string" && - typeof onSelectService === "function"; + typeof selectedServiceId === 'string' && + typeof selectedEndpointId === 'string' && + typeof onSelectService === 'function'; const resolvedSelectedServiceId = isControlledSelection - ? selectedServiceId || "" + ? selectedServiceId || '' : internalSelectedServiceId; const resolvedSelectedEndpointId = isControlledSelection - ? selectedEndpointId || "" + ? selectedEndpointId || '' : internalSelectedEndpointId; const selectedService = - services.find((service) => service.serviceId === resolvedSelectedServiceId) ?? - null; + services.find( + (service) => service.serviceId === resolvedSelectedServiceId, + ) ?? null; + const selectedServiceRuntimeId = selectedService?.serviceId || ''; + const selectedRuntimeActionIdentity = { + scopeId, + serviceId: selectedServiceRuntimeId, + }; useEffect(() => { if (isControlledSelection) { @@ -251,29 +605,33 @@ const ScopeServiceRuntimeWorkbench: React.FC } if (!services.length) { - setInternalSelectedServiceId(""); + setInternalSelectedServiceId(''); return; } if ( resolvedSelectedServiceId && - services.some((service) => service.serviceId === resolvedSelectedServiceId) + services.some( + (service) => service.serviceId === resolvedSelectedServiceId, + ) ) { return; } - const normalizedInitialServiceId = initialServiceId?.trim() || ""; + const normalizedInitialServiceId = initialServiceId?.trim() || ''; if ( normalizedInitialServiceId && - services.some((service) => service.serviceId === normalizedInitialServiceId) + services.some( + (service) => service.serviceId === normalizedInitialServiceId, + ) ) { setInternalSelectedServiceId(normalizedInitialServiceId); return; } - const normalizedPreferredServiceId = preferredServiceId?.trim() || ""; + const normalizedPreferredServiceId = preferredServiceId?.trim() || ''; setInternalSelectedServiceId( - normalizedPreferredServiceId || services[0]?.serviceId || "", + normalizedPreferredServiceId || services[0]?.serviceId || '', ); }, [ initialServiceId, @@ -289,7 +647,7 @@ const ScopeServiceRuntimeWorkbench: React.FC } if (!selectedService) { - setInternalSelectedEndpointId(""); + setInternalSelectedEndpointId(''); return; } @@ -302,12 +660,10 @@ const ScopeServiceRuntimeWorkbench: React.FC return; } - setInternalSelectedEndpointId(selectedService.endpoints[0]?.endpointId || ""); - }, [ - isControlledSelection, - resolvedSelectedEndpointId, - selectedService, - ]); + setInternalSelectedEndpointId( + selectedService.endpoints[0]?.endpointId || '', + ); + }, [isControlledSelection, resolvedSelectedEndpointId, selectedService]); useEffect(() => { if (isControlledSelection) { @@ -332,7 +688,7 @@ const ScopeServiceRuntimeWorkbench: React.FC } setInternalSelectedServiceId(serviceId); - setInternalSelectedEndpointId(""); + setInternalSelectedEndpointId(''); }; const handleUseEndpoint = (serviceId: string, endpointId: string) => { @@ -346,32 +702,41 @@ const ScopeServiceRuntimeWorkbench: React.FC const bindingsQuery = useQuery({ enabled: Boolean(scopeId && selectedService?.serviceId), - queryKey: ["scope-runtime", "bindings", scopeId, selectedService?.serviceId], + queryKey: scopeRuntimeBindingsQueryKey(scopeId, selectedServiceRuntimeId), queryFn: () => - scopeRuntimeApi.getServiceBindings(scopeId, selectedService?.serviceId || ""), + scopeRuntimeApi.getServiceBindingCatalogSnapshot( + scopeId, + selectedServiceRuntimeId, + ), }); const revisionsQuery = useQuery({ enabled: Boolean(scopeId && selectedService?.serviceId), - queryKey: ["scope-runtime", "revisions", scopeId, selectedService?.serviceId], + queryKey: scopeRuntimeRevisionsQueryKey(scopeId, selectedServiceRuntimeId), queryFn: () => - scopeRuntimeApi.getServiceRevisions(scopeId, selectedService?.serviceId || ""), + scopeRuntimeApi.getServiceRevisions(scopeId, selectedServiceRuntimeId), }); const runsQuery = useQuery({ enabled: Boolean(scopeId && selectedService?.serviceId), - queryKey: ["scope-runtime", "runs", scopeId, selectedService?.serviceId], + queryKey: ['scope-runtime', 'runs', scopeId, selectedService?.serviceId], queryFn: () => - scopeRuntimeApi.listServiceRuns(scopeId, selectedService?.serviceId || "", { - take: 12, - }), + scopeRuntimeApi.listServiceRuns( + scopeId, + selectedService?.serviceId || '', + { + take: 12, + }, + ), }); const selectedRevisionQuery = useQuery({ - enabled: Boolean(scopeId && selectedService?.serviceId && selectedRevisionId), + enabled: Boolean( + scopeId && selectedService?.serviceId && selectedRevisionId, + ), queryKey: [ - "scope-runtime", - "revision", + 'scope-runtime', + 'revision', scopeId, selectedService?.serviceId, selectedRevisionId, @@ -379,7 +744,7 @@ const ScopeServiceRuntimeWorkbench: React.FC queryFn: () => scopeRuntimeApi.getServiceRevision( scopeId, - selectedService?.serviceId || "", + selectedService?.serviceId || '', selectedRevisionId, ), }); @@ -391,8 +756,8 @@ const ScopeServiceRuntimeWorkbench: React.FC selectedRunAuditTarget?.runId.trim(), ), queryKey: [ - "scope-runtime", - "run-audit", + 'scope-runtime', + 'run-audit', scopeId, selectedService?.serviceId, selectedRunAuditTarget?.runId, @@ -401,55 +766,276 @@ const ScopeServiceRuntimeWorkbench: React.FC queryFn: () => scopeRuntimeApi.getServiceRunAudit( scopeId, - selectedService?.serviceId || "", - selectedRunAuditTarget?.runId || "", + selectedService?.serviceId || '', + selectedRunAuditTarget?.runId || '', { actorId: selectedRunAuditTarget?.actorId || undefined, }, ), }); + const isRuntimeActionCurrent = (action: RuntimeActionState) => { + return ( + runtimeActionStatesRef.current.get(runtimeActionIdentityKey(action)) + ?.requestId === action.requestId + ); + }; + + const hasBlockingRuntimeAction = (identity: RuntimeActionIdentity) => { + const action = runtimeActionStatesRef.current.get( + runtimeActionIdentityKey(identity), + ); + return Boolean( + action && + isRuntimeActionCurrent(action) && + runtimeActionBlocksWrites(action.status), + ); + }; + + const createRuntimeAction = ( + location: RuntimeActionLocation, + target: RuntimeActionTarget, + identity: RuntimeActionIdentity, + ): RuntimeActionState | null => { + if (hasBlockingRuntimeAction(identity)) { + return null; + } + + const requestId = runtimeActionRequestId.current + 1; + const action = { + location, + requestId, + scopeId: identity.scopeId, + serviceId: identity.serviceId, + status: 'submitting' as const, + target, + }; + runtimeActionRequestId.current = requestId; + runtimeActionStatesRef.current.set( + runtimeActionIdentityKey(action), + action, + ); + setRuntimeActionStates(new Map(runtimeActionStatesRef.current)); + return action; + }; + + const updateRuntimeActionState = (nextState: RuntimeActionState) => { + if (isRuntimeActionCurrent(nextState)) { + runtimeActionStatesRef.current.set( + runtimeActionIdentityKey(nextState), + nextState, + ); + setRuntimeActionStates(new Map(runtimeActionStatesRef.current)); + } + }; + + const dismissRuntimeAction = (action: RuntimeActionState) => { + if (!isRuntimeActionCurrent(action)) { + return; + } + runtimeActionStatesRef.current.delete(runtimeActionIdentityKey(action)); + setRuntimeActionStates(new Map(runtimeActionStatesRef.current)); + }; + + const writesBlockedByRuntimeAction = hasBlockingRuntimeAction( + selectedRuntimeActionIdentity, + ); + + const invalidateBindingViews = async (action: RuntimeActionState) => { + if (!isRuntimeActionCurrent(action)) { + return false; + } + + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: scopeRuntimeBindingsQueryKey( + action.scopeId, + action.serviceId, + ), + refetchType: 'none', + }), + queryClient.invalidateQueries({ + queryKey: ['scopes', 'invoke', 'services', action.scopeId], + }), + ]); + + return isRuntimeActionCurrent(action); + }; + + const invalidateRevisionViews = async (action: RuntimeActionState) => { + if (!isRuntimeActionCurrent(action)) { + return false; + } + + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: scopeRuntimeRevisionsQueryKey( + action.scopeId, + action.serviceId, + ), + refetchType: 'none', + }), + queryClient.invalidateQueries({ + queryKey: ['scopes', 'invoke', 'services', action.scopeId], + }), + ]); + + return isRuntimeActionCurrent(action); + }; + + const refreshBindingAction = async ( + action: RuntimeActionState, + status: 'observing' | 'refreshing', + ) => { + if (!isRuntimeActionCurrent(action)) { + return; + } + + updateRuntimeActionState({ ...action, status }); + try { + const bindings = await queryClient.fetchQuery({ + queryFn: () => + scopeRuntimeApi.getServiceBindingCatalogSnapshot( + action.scopeId, + action.serviceId, + ), + queryKey: scopeRuntimeBindingsQueryKey( + action.scopeId, + action.serviceId, + ), + staleTime: 0, + }); + if (!isRuntimeActionCurrent(action)) { + return; + } + + if (bindings.kind === 'not_materialized') { + updateRuntimeActionState({ ...action, status: 'delayed' }); + return; + } + + updateRuntimeActionState({ + ...action, + status: isRuntimeActionObserved( + action.target, + bindings.snapshot.bindings, + [], + ) + ? 'observed' + : 'delayed', + }); + } catch { + updateRuntimeActionState({ ...action, status: 'failed' }); + } + }; + + const refreshRevisionAction = async ( + action: RuntimeActionState, + status: 'observing' | 'refreshing', + ) => { + if (!isRuntimeActionCurrent(action)) { + return; + } + + updateRuntimeActionState({ ...action, status }); + try { + const revisions = await queryClient.fetchQuery({ + queryFn: () => + scopeRuntimeApi.getServiceRevisions(action.scopeId, action.serviceId), + queryKey: scopeRuntimeRevisionsQueryKey( + action.scopeId, + action.serviceId, + ), + staleTime: 0, + }); + if (!isRuntimeActionCurrent(action)) { + return; + } + + updateRuntimeActionState({ + ...action, + status: isRuntimeActionObserved(action.target, [], revisions.revisions) + ? 'observed' + : 'delayed', + }); + } catch { + updateRuntimeActionState({ ...action, status: 'failed' }); + } + }; + + const handleRefreshRuntimeAction = (action: RuntimeActionState) => { + if (!isRuntimeActionCurrent(action)) { + return; + } + + if (action.target.kind === 'revision-retired') { + void refreshRevisionAction(action, 'refreshing'); + return; + } + + void refreshBindingAction(action, 'refreshing'); + }; + const retireRevisionMutation = useMutation({ - mutationFn: (revisionId: string) => + mutationFn: ({ + scopeId: actionScopeId, + serviceId, + revisionId, + }: { + readonly scopeId: string; + readonly serviceId: string; + readonly revisionId: string; + }) => scopeRuntimeApi.retireServiceRevision( - scopeId, - selectedService?.serviceId || "", + actionScopeId, + serviceId, revisionId, ), - onSuccess: async (result) => { - await Promise.all([ - queryClient.invalidateQueries({ - queryKey: ["scope-runtime", "revisions", scopeId, selectedService?.serviceId], - }), - queryClient.invalidateQueries({ - queryKey: ["scopes", "invoke", "services", scopeId], - }), - ]); - messageApi.success( - t( - "pages.scopes.scopeserviceruntimeworkbench.revision.accepted.for.retirement", - "Revision was accepted for retirement.", - ), - ); + onMutate: ({ scopeId: actionScopeId, revisionId, serviceId }) => + createRuntimeAction( + 'revisions', + { + kind: 'revision-retired', + revisionId, + }, + { scopeId: actionScopeId, serviceId }, + ), + onSuccess: async (_result, _variables, action) => { + if (!action || !isRuntimeActionCurrent(action)) { + return; + } + + const acceptedAction = { ...action, status: 'accepted' as const }; + updateRuntimeActionState(acceptedAction); + if (await invalidateRevisionViews(acceptedAction)) { + await refreshRevisionAction(acceptedAction, 'observing'); + } }, - onError: (error) => { - messageApi.error( - error instanceof Error ? error.message : "Failed to retire revision.", - ); + onError: (_error, _variables, action) => { + if (action) { + updateRuntimeActionState({ ...action, status: 'failed' }); + } }, }); useEffect(() => { setBindingEditorState(null); setBindingEditorDraft(createEmptyBindingDraft()); + setBindingEditorValidationErrors({}); + setBindingEditorSubmitting(false); setSelectedRunAuditTarget(null); - setRetiringBindingId(""); - }, [selectedService?.serviceId]); + setRetiringBinding(null); + }, [scopeId, selectedService?.serviceId]); + + useEffect(() => { + runtimeActionStatesRef.current.clear(); + setRuntimeActionStates(new Map()); + }, [scopeId]); useEffect(() => { const revisions = revisionsQuery.data?.revisions ?? []; if (!revisions.length) { - setSelectedRevisionId(""); + setSelectedRevisionId(''); return; } @@ -463,13 +1049,14 @@ const ScopeServiceRuntimeWorkbench: React.FC setSelectedRevisionId( getScopeServiceCurrentRevision(revisionsQuery.data)?.revisionId || revisions[0]?.revisionId || - "", + '', ); }, [revisionsQuery.data, selectedRevisionId]); const selectedBindingTargetService = services.find( - (service) => service.serviceId === bindingEditorDraft.targetServiceId.trim(), + (service) => + service.serviceId === bindingEditorDraft.targetServiceId.trim(), ) ?? null; const bindingTargetEndpointOptions = ( @@ -477,32 +1064,61 @@ const ScopeServiceRuntimeWorkbench: React.FC ).map((endpoint) => ({ label: getUserFacingIdentifierLabel( endpoint.displayName || endpoint.endpointId, - t("pages.scopes.scopeserviceruntimeworkbench.endpoint", "Endpoint"), + t('pages.scopes.scopeserviceruntimeworkbench.endpoint', 'Endpoint'), ), value: endpoint.endpointId, })); - const bindingList = bindingsQuery.data?.bindings ?? []; + const bindingList = + bindingsQuery.data?.kind === 'available' + ? bindingsQuery.data.snapshot.bindings + : []; const revisionList = revisionsQuery.data?.revisions ?? []; const currentRevision = selectedRevisionQuery.data || - revisionList.find((revision) => revision.revisionId === selectedRevisionId) || + revisionList.find( + (revision) => revision.revisionId === selectedRevisionId, + ) || getScopeServiceCurrentRevision(revisionsQuery.data); const recentRuns = runsQuery.data?.runs ?? []; const auditTimeline = selectedRunAuditQuery.data?.audit.timeline ?? []; const auditSteps = selectedRunAuditQuery.data?.audit.steps ?? []; const auditSummary = selectedRunAuditQuery.data?.audit.summary; + const visibleRuntimeAction = runtimeActionStates.get( + runtimeActionIdentityKey({ + scopeId, + serviceId: selectedServiceRuntimeId, + }), + ); + const isRetiringBinding = (bindingId: string) => + retiringBinding?.scopeId === scopeId && + retiringBinding.serviceId === selectedServiceRuntimeId && + retiringBinding.bindingId === bindingId; + const clearRetiringBinding = (action: RuntimeActionState) => { + if (action.target.kind !== 'binding-retired') { + return; + } + const bindingId = action.target.bindingId; + + setRetiringBinding((current) => + current?.scopeId === action.scopeId && + current.serviceId === action.serviceId && + current.bindingId === bindingId + ? null + : current, + ); + }; const bindingCards = bindingList.length ? ( -
+
{bindingList.map((binding) => (
{getUserFacingIdentifierLabel( binding.displayName || binding.bindingId, - t("pages.scopes.scopeserviceruntimeworkbench.binding", "Binding"), + t( + 'pages.scopes.scopeserviceruntimeworkbench.binding', + 'Binding', + ), )} - {t("pages.scopes.scopeserviceruntimeworkbench.target", "Target")} + {t('pages.scopes.scopeserviceruntimeworkbench.target', 'Target')} {binding.serviceRef - ? t("pages.scopes.scopeserviceruntimeworkbench.service.target", "Service target") + ? t( + 'pages.scopes.scopeserviceruntimeworkbench.service.target', + 'Service target', + ) : describeScopeServiceBindingTarget(binding)} - {t("pages.scopes.scopeserviceruntimeworkbench.policies", "Policies")}{" "} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.policies', + 'Policies', + )}{' '} {binding.policyIds.length > 0 - ? t("pages.scopes.scopeserviceruntimeworkbench.policy.count", "{value1} policies", { - value1: binding.policyIds.length, - }) - : "none"} + ? t( + 'pages.scopes.scopeserviceruntimeworkbench.policy.count', + '{value1} policies', + { + value1: binding.policyIds.length, + }, + ) + : 'none'} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.edit.binding', + 'Edit binding', + )} + + {t('pages.scopes.scopeserviceruntimeworkbench.retire', 'Retire')} +
))}
) : ( ); const revisionCards = revisionList.length ? ( -
+
{revisionList.map((revision) => { const isSelected = revision.revisionId === selectedRevisionId; return ( @@ -605,11 +1273,11 @@ const ScopeServiceRuntimeWorkbench: React.FC key={revision.revisionId} style={{ border: isSelected - ? "1px solid var(--ant-color-primary)" - : "1px solid var(--ant-color-border-secondary)", + ? '1px solid var(--ant-color-primary)' + : '1px solid var(--ant-color-border-secondary)', borderRadius: 12, - display: "flex", - flexDirection: "column", + display: 'flex', + flexDirection: 'column', gap: 10, padding: 12, }} @@ -622,7 +1290,7 @@ const ScopeServiceRuntimeWorkbench: React.FC ) : null} {revision.isActiveServing ? ( ) : null} - {revision.retiredAt ? : null} + {revision.retiredAt ? ( + + ) : null} - {describeStudioMemberBindingRevisionTarget(revision)} ·{" "} + {describeStudioMemberBindingRevisionTarget(revision)} ·{' '} {describeStudioMemberBindingRevisionContext(revision) || - t("pages.scopes.scopeserviceruntimeworkbench.no.detail", "No detail")} + t( + 'pages.scopes.scopeserviceruntimeworkbench.no.detail', + 'No detail', + )} - {t("pages.scopes.scopeserviceruntimeworkbench.serving", "Serving")}{revision.servingState || revision.status} {t("pages.scopes.scopeserviceruntimeworkbench.published", "· Published")}{" "} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.serving', + 'Serving', + )} + {revision.servingState || revision.status}{' '} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.published', + '· Published', + )}{' '} {formatDateTime(revision.publishedAt)} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.retire.revision', + 'Retire revision', + )} +
); @@ -679,13 +1394,16 @@ const ScopeServiceRuntimeWorkbench: React.FC
) : ( ); const runCards = recentRuns.length ? ( -
+
{recentRuns.map((run) => (
) : ( ); const tabItems = [ { - key: "overview", - label: "Overview", + key: 'overview', + label: 'Overview', children: selectedService ? ( -
+
@@ -772,39 +1508,60 @@ const ScopeServiceRuntimeWorkbench: React.FC icon={} onClick={() => history.push( - buildScopedServiceCatalogHref(scopeId, selectedService.serviceId), + buildScopedServiceCatalogHref( + scopeId, + selectedService.serviceId, + ), ) } > - {t("pages.scopes.scopeserviceruntimeworkbench.open.services", "Open Services")} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.open.services', + 'Open Services', + )} + + {t( + 'pages.scopes.scopeserviceruntimeworkbench.review.runs', + 'Review runs', + )} + + {t( + 'pages.scopes.scopeserviceruntimeworkbench.review.bindings', + 'Review bindings', + )} +
-
+
{selectedService.endpoints.length > 0 ? ( selectedService.endpoints.map((endpoint) => (
{getUserFacingIdentifierLabel( endpoint.displayName || endpoint.endpointId, - t("pages.scopes.scopeserviceruntimeworkbench.endpoint", "Endpoint"), + t( + 'pages.scopes.scopeserviceruntimeworkbench.endpoint', + 'Endpoint', + ), )} {endpoint.description || t( - "pages.scopes.scopeserviceruntimeworkbench.no.endpoint.description", - "No endpoint description." + 'pages.scopes.scopeserviceruntimeworkbench.no.endpoint.description', + 'No endpoint description.', )} - {t("pages.scopes.scopeserviceruntimeworkbench.request", "Request")}{endpoint.requestTypeUrl || "n/a"} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.request', + 'Request', + )} + {endpoint.requestTypeUrl || 'n/a'}
)) ) : ( )} @@ -867,30 +1640,55 @@ const ScopeServiceRuntimeWorkbench: React.FC
) : ( - + ), }, { - key: "bindings", - label: t("pages.scopes.scopeserviceruntimeworkbench.bindings.count", "Bindings ({count})", { - count: bindingList.length, - }), + key: 'bindings', + label: t( + 'pages.scopes.scopeserviceruntimeworkbench.bindings.count', + 'Bindings ({count})', + { + count: bindingList.length, + }, + ), children: selectedService ? ( -
+
} onClick={() => { + if (hasBlockingRuntimeAction(selectedRuntimeActionIdentity)) { + return; + } + setBindingEditorDraft(createEmptyBindingDraft()); - setBindingEditorState({ mode: "create" }); + setBindingEditorValidationErrors({}); + setBindingEditorState({ mode: 'create' }); }} type="primary" > - {t("pages.scopes.scopeserviceruntimeworkbench.add.binding", "Add binding")} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.add.binding', + 'Add binding', + )} + } - title={t("pages.scopes.scopeserviceruntimeworkbench.dependency.surface", "Dependency Surface")} - titleHelp={t("pages.scopes.scopeserviceruntimeworkbench.workspace.bindings.describe.which.services", "Workspace bindings describe which services, connectors, or secrets this published service is allowed to depend on inside the project.")} + title={t( + 'pages.scopes.scopeserviceruntimeworkbench.dependency.surface', + 'Dependency Surface', + )} + titleHelp={t( + 'pages.scopes.scopeserviceruntimeworkbench.workspace.bindings.describe.which.services', + 'Workspace bindings describe which services, connectors, or secrets this published service is allowed to depend on inside the project.', + )} > {bindingsQuery.error ? ( title={ bindingsQuery.error instanceof Error ? bindingsQuery.error.message - : "Failed to load default route revisions." + : 'Failed to load default route revisions.' } type="error" /> ) : bindingsQuery.isLoading ? ( - + + ) : bindingsQuery.data?.kind === 'not_materialized' && + visibleRuntimeAction?.location !== 'bindings' ? ( + void bindingsQuery.refetch()} + size="small" + > + {t( + 'pages.scopes.scopeserviceruntimeworkbench.action.refresh', + 'Refresh', + )} + + } + description={t( + 'pages.scopes.scopeserviceruntimeworkbench.bindings.preparing.description', + 'Refresh to see when bindings are ready.', + )} + showIcon + title={t( + 'pages.scopes.scopeserviceruntimeworkbench.bindings.preparing.title', + 'Bindings are still being prepared', + )} + type="info" + /> ) : ( bindingCards )}
) : ( - + ), }, { - key: "revisions", - label: t("pages.scopes.scopeserviceruntimeworkbench.revisions.count", "Revisions ({count})", { - count: revisionList.length, - }), + key: 'revisions', + label: t( + 'pages.scopes.scopeserviceruntimeworkbench.revisions.count', + 'Revisions ({count})', + { + count: revisionList.length, + }, + ), children: selectedService ? ( -
+
{revisionsQuery.error ? ( title={ revisionsQuery.error instanceof Error ? revisionsQuery.error.message - : "Failed to load revisions." + : 'Failed to load revisions.' } type="error" /> ) : revisionsQuery.isLoading ? ( - + ) : ( revisionCards )} @@ -943,21 +1792,33 @@ const ScopeServiceRuntimeWorkbench: React.FC {currentRevision ? (
/>
@@ -988,7 +1851,10 @@ const ScopeServiceRuntimeWorkbench: React.FC currentRevision, )} showIcon - title={t("pages.scopes.scopeserviceruntimeworkbench.revision.detail", "Revision detail")} + title={t( + 'pages.scopes.scopeserviceruntimeworkbench.revision.detail', + 'Revision detail', + )} type="info" /> ) : null} @@ -996,19 +1862,34 @@ const ScopeServiceRuntimeWorkbench: React.FC ) : null}
) : ( - + ), }, { - key: "runs", - label: t("pages.scopes.scopeserviceruntimeworkbench.runs.count", "Runs ({count})", { - count: recentRuns.length, - }), + key: 'runs', + label: t( + 'pages.scopes.scopeserviceruntimeworkbench.runs.count', + 'Runs ({count})', + { + count: recentRuns.length, + }, + ), children: selectedService ? ( -
+
{runsQuery.error ? ( title={ runsQuery.error instanceof Error ? runsQuery.error.message - : "Failed to load runs." + : 'Failed to load runs.' } type="error" /> ) : runsQuery.isLoading ? ( - + ) : ( runCards )} @@ -1029,8 +1915,14 @@ const ScopeServiceRuntimeWorkbench: React.FC {selectedRunAuditTarget ? ( {selectedRunAuditQuery.error ? ( title={ selectedRunAuditQuery.error instanceof Error ? selectedRunAuditQuery.error.message - : "Failed to load run audit." + : 'Failed to load run audit.' } type="error" /> ) : selectedRunAuditQuery.isLoading ? ( - + ) : selectedRunAuditQuery.data ? ( -
+
value={`${auditSummary?.completedSteps ?? 0}/${auditSummary?.totalSteps ?? 0}`} />
@@ -1074,7 +1977,10 @@ const ScopeServiceRuntimeWorkbench: React.FC ) : null} @@ -1082,44 +1988,64 @@ const ScopeServiceRuntimeWorkbench: React.FC ) : null}
- + {auditTimeline.length > 0 ? (
- {auditTimeline.slice(0, 8).map((event, index) => ( + {auditTimeline.slice(0, 8).map((event) => (
- {event.stage || event.eventType || "event"} + {event.stage || event.eventType || 'event'} {event.message || - t("pages.scopes.scopeserviceruntimeworkbench.no.message", "No message")} + t( + 'pages.scopes.scopeserviceruntimeworkbench.no.message', + 'No message', + )} {formatDateTime(event.timestamp)} @@ -1129,17 +2055,25 @@ const ScopeServiceRuntimeWorkbench: React.FC
) : ( )} - + {auditSteps.length > 0 ? (
@@ -1147,10 +2081,11 @@ const ScopeServiceRuntimeWorkbench: React.FC
{step.stepId} - {step.stepType || "step"} ·{" "} - {step.targetRole || "unassigned"} + {step.stepType || 'step'} ·{' '} + {step.targetRole || 'unassigned'} {step.outputPreview || step.error || t( - "pages.scopes.scopeserviceruntimeworkbench.no.step.preview", - "No step preview." + 'pages.scopes.scopeserviceruntimeworkbench.no.step.preview', + 'No step preview.', )}
@@ -1175,7 +2110,10 @@ const ScopeServiceRuntimeWorkbench: React.FC
) : ( )} @@ -1187,32 +2125,42 @@ const ScopeServiceRuntimeWorkbench: React.FC ) : null}
) : ( - + ), }, ]; return ( <> - {messageContextHolder} -
+
{services.length > 0 ? ( -
+
{services.map((service) => (
{getUserFacingIdentifierLabel( service.displayName || service.serviceId, - t("pages.scopes.scopeserviceruntimeworkbench.service", "Service"), + t( + 'pages.scopes.scopeserviceruntimeworkbench.service', + 'Service', + ), )} - {service.endpoints.length}{" "} - {t("pages.scopes.scopeserviceruntimeworkbench.endpoints", "endpoints")}{" "} - ·{" "} - {service.activeServingRevisionId || service.defaultServingRevisionId + {service.endpoints.length}{' '} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.endpoints', + 'endpoints', + )}{' '} + ·{' '} + {service.activeServingRevisionId || + service.defaultServingRevisionId ? t( - "pages.scopes.scopeserviceruntimeworkbench.serving.version.ready", - "serving version ready", + 'pages.scopes.scopeserviceruntimeworkbench.serving.version.ready', + 'serving version ready', ) - : "n/a"} + : 'n/a'} + {t( + 'pages.scopes.scopeserviceruntimeworkbench.open.services.2', + 'Open Services', + )} +
))}
) : ( )} + {visibleRuntimeAction && + (visibleRuntimeAction.location !== 'binding-editor' || + !bindingEditorState) ? ( + dismissRuntimeAction(visibleRuntimeAction)} + onRefresh={() => handleRefreshRuntimeAction(visibleRuntimeAction)} + /> + ) : null} + {selectedService ? (
{ + if (bindingEditorSubmitting) { + return; + } + setBindingEditorState(null); setBindingEditorDraft(createEmptyBindingDraft()); + setBindingEditorValidationErrors({}); + if (visibleRuntimeAction?.location === 'binding-editor') { + updateRuntimeActionState({ + ...visibleRuntimeAction, + location: 'bindings', + }); + } }} onOk={async () => { - if (!selectedService) { + if ( + !selectedService || + bindingEditorSubmitting || + hasBlockingRuntimeAction(selectedRuntimeActionIdentity) + ) { return; } const payload = buildBindingPayload(bindingEditorDraft); - if (!payload.bindingId) { - messageApi.error("Binding id is required."); - return; - } - - if (payload.bindingKind === "service" && !payload.service?.serviceId) { - messageApi.error("Select a target service for a service binding."); - return; - } - - if ( - payload.bindingKind === "connector" && - (!payload.connector?.connectorType || !payload.connector.connectorId) - ) { - messageApi.error("Connector type and connector id are required."); + const validationErrors = getBindingEditorValidationErrors(payload); + setBindingEditorValidationErrors(validationErrors); + if (Object.keys(validationErrors).length > 0) { return; } - if (payload.bindingKind === "secret" && !payload.secret?.secretName) { - messageApi.error("Secret name is required."); + const action = createRuntimeAction( + 'bindings', + { + bindingId: payload.bindingId, + kind: 'binding-payload', + payload, + }, + { scopeId, serviceId: selectedService.serviceId }, + ); + if (!action) { return; } - setBindingEditorSubmitting(true); try { - if (bindingEditorState?.mode === "edit" && bindingEditorState.bindingId) { + if ( + bindingEditorState?.mode === 'edit' && + bindingEditorState.bindingId + ) { await scopeRuntimeApi.updateServiceBinding( scopeId, selectedService.serviceId, @@ -1338,165 +2347,421 @@ const ScopeServiceRuntimeWorkbench: React.FC payload, ); } - await queryClient.invalidateQueries({ - queryKey: [ - "scope-runtime", - "bindings", - scopeId, - selectedService.serviceId, - ], + } catch { + updateRuntimeActionState({ + ...action, + location: 'binding-editor', + status: 'failed', }); - messageApi.success( - bindingEditorState?.mode === "edit" - ? t( - "pages.scopes.scopeserviceruntimeworkbench.binding.updated", - "Binding was updated.", - ) - : t( - "pages.scopes.scopeserviceruntimeworkbench.binding.created", - "Binding was created.", - ), - ); - setBindingEditorState(null); - setBindingEditorDraft(createEmptyBindingDraft()); - } catch (error) { - messageApi.error( - error instanceof Error ? error.message : "Failed to save binding.", - ); + setBindingEditorSubmitting(false); + return; + } + + if (!isRuntimeActionCurrent(action)) { + setBindingEditorSubmitting(false); + return; + } + + const acceptedAction = { ...action, status: 'accepted' as const }; + updateRuntimeActionState(acceptedAction); + setBindingEditorState(null); + setBindingEditorDraft(createEmptyBindingDraft()); + setBindingEditorValidationErrors({}); + try { + if (await invalidateBindingViews(acceptedAction)) { + await refreshBindingAction(acceptedAction, 'observing'); + } + } catch { + updateRuntimeActionState({ ...action, status: 'failed' }); } finally { setBindingEditorSubmitting(false); } }} open={Boolean(bindingEditorState)} title={ - bindingEditorState?.mode === "edit" - ? t("pages.scopes.scopeserviceruntimeworkbench.edit.binding", "Edit binding") - : "Create default route" + bindingEditorState?.mode === 'edit' + ? t( + 'pages.scopes.scopeserviceruntimeworkbench.edit.binding', + 'Edit binding', + ) + : t( + 'pages.scopes.scopeserviceruntimeworkbench.create.binding', + 'Create binding', + ) } > - - - setBindingEditorDraft((current) => ({ - ...current, - bindingId: event.target.value, - })) - } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.binding.id", "binding id")} - value={bindingEditorDraft.bindingId} - /> - - setBindingEditorDraft((current) => ({ - ...current, - displayName: event.target.value, - })) +
+ {visibleRuntimeAction?.location === 'binding-editor' ? ( + dismissRuntimeAction(visibleRuntimeAction)} + onRefresh={() => handleRefreshRuntimeAction(visibleRuntimeAction)} + /> + ) : null} + + {bindingEditorValidationErrors.bindingId} + + ) : null } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.display.name", "display name")} - value={bindingEditorDraft.displayName} - /> - { + setBindingEditorDraft((current) => ({ + ...current, + bindingId: event.target.value, + })); + setBindingEditorValidationErrors((current) => ({ + ...current, + bindingId: undefined, + })); + }} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.binding.id', + 'binding id', + )} + value={bindingEditorDraft.bindingId} + /> + + + + setBindingEditorDraft((current) => ({ + ...current, + displayName: event.target.value, + })) + } + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.display.name', + 'display name', + )} + value={bindingEditorDraft.displayName} + /> + + + - setBindingEditorDraft((current) => ({ - ...current, - targetEndpointId: "", - targetServiceId: value, - })) + + {bindingEditorValidationErrors.targetServiceId} + + ) : null } - options={services - .filter((service) => service.serviceId !== selectedService?.serviceId) - .map((service) => ({ - label: getUserFacingIdentifierLabel( - service.displayName || service.serviceId, - t("pages.scopes.scopeserviceruntimeworkbench.service", "Service"), - ), - value: service.serviceId, - }))} - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.target.service", "target service")} - value={bindingEditorDraft.targetServiceId || undefined} - /> - { + setBindingEditorDraft((current) => ({ + ...current, + targetEndpointId: '', + targetServiceId: value, + })); + setBindingEditorValidationErrors((current) => ({ + ...current, + targetServiceId: undefined, + })); + }} + options={services + .filter( + (service) => + service.serviceId !== selectedService?.serviceId, + ) + .map((service) => ({ + label: getUserFacingIdentifierLabel( + service.displayName || service.serviceId, + t( + 'pages.scopes.scopeserviceruntimeworkbench.service', + 'Service', + ), + ), + value: service.serviceId, + }))} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.target.service', + 'target service', + )} + value={bindingEditorDraft.targetServiceId || undefined} + /> + + + - setBindingEditorDraft((current) => ({ - ...current, - connectorType: event.target.value, - })) + + {bindingEditorValidationErrors.connectorType} + + ) : null } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.connector.type", "connector type")} - value={bindingEditorDraft.connectorType} - /> + htmlFor="scope-runtime-connector-type" + label={t( + 'pages.scopes.scopeserviceruntimeworkbench.connector.type.label', + 'Connector type', + )} + validateStatus={ + bindingEditorValidationErrors.connectorType + ? 'error' + : undefined + } + > + { + setBindingEditorDraft((current) => ({ + ...current, + connectorType: event.target.value, + })); + setBindingEditorValidationErrors((current) => ({ + ...current, + connectorType: undefined, + })); + }} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.connector.type', + 'connector type', + )} + value={bindingEditorDraft.connectorType} + /> + + + {bindingEditorValidationErrors.connectorId} + + ) : null + } + htmlFor="scope-runtime-connector-id" + label={t( + 'pages.scopes.scopeserviceruntimeworkbench.connector.id.label', + 'Connector ID', + )} + validateStatus={ + bindingEditorValidationErrors.connectorId + ? 'error' + : undefined + } + > + { + setBindingEditorDraft((current) => ({ + ...current, + connectorId: event.target.value, + })); + setBindingEditorValidationErrors((current) => ({ + ...current, + connectorId: undefined, + })); + }} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.connector.id', + 'connector id', + )} + value={bindingEditorDraft.connectorId} + /> + + + ) : null} + {bindingEditorDraft.bindingKind === 'secret' ? ( + + {bindingEditorValidationErrors.secretName} + + ) : null + } + htmlFor="scope-runtime-secret-name" + label={t( + 'pages.scopes.scopeserviceruntimeworkbench.secret.name.label', + 'Secret name', + )} + validateStatus={ + bindingEditorValidationErrors.secretName ? 'error' : undefined + } + > + aria-describedby={ + bindingEditorValidationErrors.secretName + ? 'scope-runtime-secret-name-error' + : undefined + } + aria-invalid={Boolean(bindingEditorValidationErrors.secretName)} + id="scope-runtime-secret-name" + onChange={(event) => { setBindingEditorDraft((current) => ({ ...current, - connectorId: event.target.value, - })) - } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.connector.id", "connector id")} - value={bindingEditorDraft.connectorId} + secretName: event.target.value, + })); + setBindingEditorValidationErrors((current) => ({ + ...current, + secretName: undefined, + })); + }} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.secret.name', + 'secret name', + )} + value={bindingEditorDraft.secretName} /> - + ) : null} - {bindingEditorDraft.bindingKind === "secret" ? ( - + setBindingEditorDraft((current) => ({ ...current, - secretName: event.target.value, + policyIdsText: event.target.value, })) } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.secret.name", "secret name")} - value={bindingEditorDraft.secretName} + placeholder={t( + 'pages.scopes.scopeserviceruntimeworkbench.policy.ids.separated.by.commas', + 'policy ids, separated by commas', + )} + rows={3} + value={bindingEditorDraft.policyIdsText} /> - ) : null} - - setBindingEditorDraft((current) => ({ - ...current, - policyIdsText: event.target.value, - })) - } - placeholder={t("pages.scopes.scopeserviceruntimeworkbench.policy.ids.separated.by.commas", "policy ids, separated by commas")} - rows={3} - value={bindingEditorDraft.policyIdsText} - /> - + +
); @@ -1512,57 +2777,71 @@ const RunSummaryCard: React.FC<{
- {formatDateTime(run.lastUpdatedAt || run.boundAt || run.bindingUpdatedAt)} + {formatDateTime( + run.lastUpdatedAt || run.boundAt || run.bindingUpdatedAt, + )} - {t("pages.scopes.scopeserviceruntimeworkbench.workflow", "Workflow")} + {t('pages.scopes.scopeserviceruntimeworkbench.workflow', 'Workflow')} {getUserFacingIdentifierLabel( run.workflowName, - t("pages.scopes.scopeserviceruntimeworkbench.not.available", "n/a"), + t('pages.scopes.scopeserviceruntimeworkbench.not.available', 'n/a'), )} - {t("pages.scopes.scopeserviceruntimeworkbench.updated", "Updated")} + {t('pages.scopes.scopeserviceruntimeworkbench.updated', 'Updated')} {formatDateTime(run.lastUpdatedAt)} {run.lastError || run.lastOutput || t( - "pages.scopes.scopeserviceruntimeworkbench.no.output.snapshot.captured", - "No output snapshot has been captured yet." + 'pages.scopes.scopeserviceruntimeworkbench.no.output.snapshot.captured', + 'No output snapshot has been captured yet.', )} - + {t('pages.scopes.scopeserviceruntimeworkbench.runtime', 'Runtime')} + + {t('pages.scopes.scopeserviceruntimeworkbench.open.runs', 'Open Runs')} +
); diff --git a/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.test.tsx b/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.test.tsx index 233933f65d..ba8c85b995 100644 --- a/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.test.tsx +++ b/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.test.tsx @@ -1,11 +1,17 @@ import { AGUIEventType } from '@aevatar-react-sdk/types'; -import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'; +import { + act, + fireEvent, + render, + screen, + waitFor, + within, +} from '@testing-library/react'; import { setLocale } from '@umijs/max'; import * as React from 'react'; import { parseBackendSSEStream } from '@/shared/agui/sseFrameNormalizer'; import { runtimeGAgentApi } from '@/shared/api/runtimeGAgentApi'; import { runtimeRunsApi } from '@/shared/api/runtimeRunsApi'; -import { scriptsApi } from '@/shared/studio/scriptsApi'; import { applyStepInspectorDraft, connectStepToTarget, @@ -17,13 +23,25 @@ import { buildStudioGraphElements, buildStudioWorkflowLayout, } from '@/shared/studio/graph'; +import { scriptsApi } from '@/shared/studio/scriptsApi'; import { + STUDIO_WORKFLOW_YAML_AUTHORING_DELETION_TRACKING, StudioGAgentBuildPanel, StudioScriptBuildPanel, StudioWorkflowBuildPanel, - STUDIO_WORKFLOW_YAML_AUTHORING_DELETION_TRACKING, } from './StudioBuildPanels'; +const mockConsoleToast = { + error: jest.fn(), + info: jest.fn(), + success: jest.fn(), + warning: jest.fn(), +}; + +jest.mock('@/shared/ui/ConsoleToast', () => ({ + useConsoleToast: () => mockConsoleToast, +})); + type WorkflowPrimitiveParameterTestDescriptor = { readonly name: string; readonly type: string; @@ -149,7 +167,10 @@ jest.mock('@/modules/studio/scripts/ScriptCodeEditor', () => ({ value = '', onChange, }: { - readonly focusTarget?: { readonly filePath: string; readonly startLineNumber: number } | null; + readonly focusTarget?: { + readonly filePath: string; + readonly startLineNumber: number; + } | null; readonly value?: string; readonly onChange?: (nextValue: string) => void; }) => ( @@ -160,7 +181,9 @@ jest.mock('@/modules/studio/scripts/ScriptCodeEditor', () => ({ onChange={(event) => onChange?.(event.target.value)} />
- {focusTarget ? `${focusTarget.filePath}:${focusTarget.startLineNumber}` : ''} + {focusTarget + ? `${focusTarget.filePath}:${focusTarget.startLineNumber}` + : ''}
), @@ -283,6 +306,7 @@ function WorkflowBuildHarness({ initialDocumentOverride, onApplyStepDraftOverride, onContinueToBind, + onInsertStepOverride, onSaveDraft, runtimePrimitivesOverride, }: { @@ -290,19 +314,23 @@ function WorkflowBuildHarness({ readonly initialDocumentOverride?: WorkflowBuildHarnessDocument; readonly onApplyStepDraftOverride?: (draft: any) => Promise; readonly onContinueToBind: jest.Mock; + readonly onInsertStepOverride?: (stepType: string) => Promise; readonly onSaveDraft: jest.Mock; readonly runtimePrimitivesOverride?: readonly WorkflowPrimitiveTestDescriptor[]; }) { const documentSeed = initialDocumentOverride ?? initialDocument; - const [document, setDocument] = React.useState(() => cloneValue(documentSeed)); + const [document, setDocument] = React.useState(() => + cloneValue(documentSeed), + ); const [draftYaml, setDraftYaml] = React.useState(() => buildWorkflowYaml(documentSeed), ); - const [selectedGraphNodeId, setSelectedGraphNodeId] = React.useState( - 'step:draft_step', - ); + const [selectedGraphNodeId, setSelectedGraphNodeId] = + React.useState('step:draft_step'); const [layout, setLayout] = React.useState(null); - const [runPrompt, setRunPrompt] = React.useState('Please triage the refund request.'); + const [runPrompt, setRunPrompt] = React.useState( + 'Please triage the refund request.', + ); const workflowGraph = React.useMemo( () => buildStudioGraphElements(document, layout), [document, layout], @@ -327,7 +355,9 @@ function WorkflowBuildHarness({ return ( [draftYaml])} + buildWorkflowYamls={ + buildWorkflowYamlsOverride ?? (async () => [draftYaml]) + } canSaveWorkflow draftYaml={draftYaml} dryRunModelLabel="gpt-5.4-mini" @@ -337,17 +367,26 @@ function WorkflowBuildHarness({ ? onApplyStepDraftOverride : async (draft) => { const currentStepId = selectedGraphNodeId.replace(/^step:/, ''); - const result = applyStepInspectorDraft(document, currentStepId, draft); - await commitDocument(result.document as WorkflowBuildHarnessDocument, { - nextSelectedNodeId: result.nodeId, - }); + const result = applyStepInspectorDraft( + document, + currentStepId, + draft, + ); + await commitDocument( + result.document as WorkflowBuildHarnessDocument, + { + nextSelectedNodeId: result.nodeId, + }, + ); } } onAutoLayout={() => setLayout(null)} onConnectNodes={(sourceNodeId: string, targetNodeId: string) => { const sourceStepId = sourceNodeId.replace(/^step:/, ''); const targetStepId = targetNodeId.replace(/^step:/, ''); - const sourceStep = document.steps.find((step) => step.id === sourceStepId); + const sourceStep = document.steps.find( + (step) => step.id === sourceStepId, + ); const result = connectStepToTarget( document, sourceStepId, @@ -362,15 +401,21 @@ function WorkflowBuildHarness({ }); }} onContinueToBind={() => onContinueToBind(draftYaml)} - onInsertStep={async (stepType: string) => { - const result = insertStepByType(document, stepType, { - afterStepId: selectedGraphNodeId.replace(/^step:/, ''), - targetRoleId: 'assistant', - }); - await commitDocument(result.document as WorkflowBuildHarnessDocument, { - nextSelectedNodeId: result.nodeId, - }); - }} + onInsertStep={ + onInsertStepOverride ?? + (async (stepType: string) => { + const result = insertStepByType(document, stepType, { + afterStepId: selectedGraphNodeId.replace(/^step:/, ''), + targetRoleId: 'assistant', + }); + await commitDocument( + result.document as WorkflowBuildHarnessDocument, + { + nextSelectedNodeId: result.nodeId, + }, + ); + }) + } onNodeLayoutChange={(nodes) => { setLayout( buildStudioWorkflowLayout( @@ -405,59 +450,61 @@ function WorkflowBuildHarness({ onRunPromptChange={setRunPrompt} onSaveDraft={(pendingDraft) => onSaveDraft(pendingDraft ?? draftYaml)} onSetDraftYaml={setDraftYaml} - runtimePrimitives={runtimePrimitivesOverride ?? [ - { - name: 'llm_call', - aliases: [], - description: 'Call the LLM.', - category: 'ai', - parameters: [ - { - name: 'prompt_prefix', - type: 'string', - required: false, - description: 'Prompt prefix', - default: '', - enumValues: [], - }, - ], - exampleWorkflows: ['workflow-demo'], - }, - { - name: 'human_approval', - aliases: [], - description: 'Pause for approval.', - category: 'human', - parameters: [ - { - name: 'reviewer', - type: 'string', - required: false, - description: 'Reviewer', - default: '', - enumValues: [], - }, - ], - exampleWorkflows: ['workflow-demo'], - }, - { - name: 'connector_call', - aliases: [], - description: 'Call an external connector.', - category: 'integration', - parameters: [ - { - name: 'connector', - type: 'string', - required: true, - description: 'Connector name', - default: '', - enumValues: [], - }, - ], - exampleWorkflows: ['workflow-demo'], - }, - ]} + runtimePrimitives={ + runtimePrimitivesOverride ?? [ + { + name: 'llm_call', + aliases: [], + description: 'Call the LLM.', + category: 'ai', + parameters: [ + { + name: 'prompt_prefix', + type: 'string', + required: false, + description: 'Prompt prefix', + default: '', + enumValues: [], + }, + ], + exampleWorkflows: ['workflow-demo'], + }, + { + name: 'human_approval', + aliases: [], + description: 'Pause for approval.', + category: 'human', + parameters: [ + { + name: 'reviewer', + type: 'string', + required: false, + description: 'Reviewer', + default: '', + enumValues: [], + }, + ], + exampleWorkflows: ['workflow-demo'], + }, + { + name: 'connector_call', + aliases: [], + description: 'Call an external connector.', + category: 'integration', + parameters: [ + { + name: 'connector', + type: 'string', + required: true, + description: 'Connector name', + default: '', + enumValues: [], + }, + ], + exampleWorkflows: ['workflow-demo'], + }, + ] + } runPrompt={runPrompt} savePending={false} saveNotice={null} @@ -557,6 +604,33 @@ describe('StudioWorkflowBuildPanel', () => { }); }); + it('uses a generic shared toast when adding a workflow step fails', async () => { + const insertStep = jest.fn, [string]>(async () => { + throw new Error('The workflow adapter rejected this node.'); + }); + + render( + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Add step' })); + fireEvent.click(await screen.findByRole('button', { name: /llm_call/i })); + + await waitFor(() => { + expect(insertStep).toHaveBeenCalledWith('llm_call'); + expect(mockConsoleToast.error).toHaveBeenCalledWith( + 'Could not complete the workflow action. Review the details and try again.', + ); + }); + expect( + screen.getByText('The workflow adapter rejected this node.'), + ).toBeInTheDocument(); + }); + it('keeps the Script bind CTA stable and uses the dry-run panel as the run entry', () => { const handleContinueToBind = jest.fn(); @@ -576,8 +650,12 @@ describe('StudioWorkflowBuildPanel', () => { ); expect(screen.getByText('Ready to bind')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeEnabled(); - expect(screen.queryByRole('button', { name: 'Dry-run' })).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeEnabled(); + expect( + screen.queryByRole('button', { name: 'Dry-run' }), + ).not.toBeInTheDocument(); expect(screen.getAllByRole('button', { name: 'Run' })).toHaveLength(1); fireEvent.change(screen.getByLabelText('Mock script code editor'), { @@ -587,7 +665,9 @@ describe('StudioWorkflowBuildPanel', () => { }); expect(screen.getByText('Validate current source')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeDisabled(); expect(screen.getByRole('button', { name: 'Save script' })).toBeDisabled(); }); @@ -616,20 +696,42 @@ describe('StudioWorkflowBuildPanel', () => { ), ).toBeInTheDocument(); expect( - screen.getByText('No script is selected yet. Start a script draft to open the editor.'), + screen.getByText( + 'No script is selected yet. Start a script draft to open the editor.', + ), ).toBeInTheDocument(); expect(screen.queryByLabelText('Script ID')).not.toBeInTheDocument(); - expect(screen.queryByLabelText('Script lifecycle status')).not.toBeInTheDocument(); - expect(screen.queryByLabelText('Script dry run input')).not.toBeInTheDocument(); - expect(screen.queryByText(/Validation, save, bind/i)).not.toBeInTheDocument(); + expect( + screen.queryByLabelText('Script lifecycle status'), + ).not.toBeInTheDocument(); + expect( + screen.queryByLabelText('Script dry run input'), + ).not.toBeInTheDocument(); + expect( + screen.queryByText(/Validation, save, bind/i), + ).not.toBeInTheDocument(); expect(screen.queryByText(/dry-run controls/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/Bind becomes available/i)).not.toBeInTheDocument(); - expect(screen.queryByText('Create a script before running it')).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Validate' })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Save script' })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Run' })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Load sample input' })).not.toBeInTheDocument(); - expect(screen.queryByRole('button', { name: 'Continue to Bind' })).not.toBeInTheDocument(); + expect( + screen.queryByText(/Bind becomes available/i), + ).not.toBeInTheDocument(); + expect( + screen.queryByText('Create a script before running it'), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Validate' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Save script' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Run' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Load sample input' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Continue to Bind' }), + ).not.toBeInTheDocument(); expect(screen.queryByText('Promotion')).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Add script' })); @@ -674,7 +776,8 @@ describe('StudioWorkflowBuildPanel', () => { expect(screen.getByText('Script draft')).toBeInTheDocument(); expect(screen.queryByText('orders-script (draft)')).not.toBeInTheDocument(); expect( - (screen.getByLabelText('Mock script code editor') as HTMLTextAreaElement).value, + (screen.getByLabelText('Mock script code editor') as HTMLTextAreaElement) + .value, ).toContain('DraftBehavior'); expect(screen.getByText('Validate current source')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Save script' })).toBeDisabled(); @@ -710,7 +813,9 @@ describe('StudioWorkflowBuildPanel', () => { expect(handleDraftSaved).toHaveBeenCalledWith('orders-script'); }); await waitFor(() => { - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeEnabled(); }); expect(screen.getByText('Ready to bind')).toBeInTheDocument(); }); @@ -801,10 +906,14 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Run' })); - expect(await screen.findByLabelText('Script dry run facts')).toBeInTheDocument(); + expect( + await screen.findByLabelText('Script dry run facts'), + ).toBeInTheDocument(); expect(screen.queryByText('run-script-1')).not.toBeInTheDocument(); expect(screen.queryByText('runtime-run')).not.toBeInTheDocument(); - expect(screen.getByText('type.googleapis.com/AppScriptCommand')).toBeInTheDocument(); + expect( + screen.getByText('type.googleapis.com/AppScriptCommand'), + ).toBeInTheDocument(); }); it('keeps save observation pending honest and exposes catalog refresh', async () => { @@ -852,14 +961,18 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Validate' })); await waitFor( () => { - expect(screen.getByRole('button', { name: 'Save script' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Save script' }), + ).toBeEnabled(); }, { timeout: 3000 }, ); fireEvent.click(screen.getByRole('button', { name: 'Save script' })); expect(await screen.findByText(/Waiting for catalog/)).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeDisabled(); fireEvent.click(screen.getByRole('button', { name: 'Refresh catalog' })); expect(handleRefreshScripts).toHaveBeenCalled(); }); @@ -908,15 +1021,21 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Validate' })); await waitFor( () => { - expect(screen.getByRole('button', { name: 'Save script' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Save script' }), + ).toBeEnabled(); }, { timeout: 3000 }, ); fireEvent.click(screen.getByRole('button', { name: 'Save script' })); - expect(await screen.findByText('Catalog rejected the revision.')).toBeInTheDocument(); + expect( + await screen.findByText('Catalog rejected the revision.'), + ).toBeInTheDocument(); expect(screen.getByText('Save needs attention')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeDisabled(); }); it('polls save observation until a pending Script save is applied', async () => { @@ -982,7 +1101,9 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Validate' })); await waitFor( () => { - expect(screen.getByRole('button', { name: 'Save script' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Save script' }), + ).toBeEnabled(); }, { timeout: 3000 }, ); @@ -993,7 +1114,9 @@ describe('StudioWorkflowBuildPanel', () => { () => { expect(mockedScriptsApi.observeSaveScript).toHaveBeenCalledTimes(2); expect(handleDraftSaved).toHaveBeenCalledWith('orders-script'); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeEnabled(); }, { timeout: 2500 }, ); @@ -1087,10 +1210,14 @@ describe('StudioWorkflowBuildPanel', () => { }); await waitFor(() => { - expect(screen.getByRole('button', { name: 'Save script' })).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'Save script' }), + ).toBeDisabled(); }); expect(screen.queryByText(/Save applied/)).toBeNull(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeDisabled(); }); it('edits a multi-file package and entry settings', () => { @@ -1117,10 +1244,14 @@ describe('StudioWorkflowBuildPanel', () => { expect(screen.getByLabelText('Script package tree')).toBeInTheDocument(); fireEvent.click(screen.getByText('Advanced package')); fireEvent.click(screen.getByRole('button', { name: 'Add C#' })); - expect(screen.getByRole('button', { name: /Support\.cs/ })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /Support\.cs/ }), + ).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Rename' })); - expect(screen.getByRole('button', { name: /SupportRenamed\.cs/ })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /SupportRenamed\.cs/ }), + ).toBeInTheDocument(); fireEvent.change(screen.getByLabelText('Entry behavior type'), { target: { @@ -1185,18 +1316,24 @@ describe('StudioWorkflowBuildPanel', () => { expect(await screen.findByText('DAG Canvas')).toBeInTheDocument(); expect(screen.getByTestId('workflow-stage-actions')).toBeInTheDocument(); - const workflowEditorWorkspace = screen.getByTestId('workflow-editor-workspace'); + const workflowEditorWorkspace = screen.getByTestId( + 'workflow-editor-workspace', + ); const workflowDryRunPanel = screen.getByTestId('workflow-dry-run-panel'); expect(workflowEditorWorkspace).toBeInTheDocument(); expect(workflowDryRunPanel).toBeInTheDocument(); - expect(within(workflowEditorWorkspace).queryByText('Dry-run')).not.toBeInTheDocument(); + expect( + within(workflowEditorWorkspace).queryByText('Dry-run'), + ).not.toBeInTheDocument(); expect( workflowEditorWorkspace.compareDocumentPosition(workflowDryRunPanel) & Node.DOCUMENT_POSITION_FOLLOWING, ).toBeTruthy(); fireEvent.click(screen.getByRole('button', { name: 'Add step' })); - expect(await screen.findByTestId('workflow-step-type-picker')).toBeInTheDocument(); + expect( + await screen.findByTestId('workflow-step-type-picker'), + ).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: /llm_call/i })); await waitFor(() => { @@ -1232,9 +1369,15 @@ describe('StudioWorkflowBuildPanel', () => { (screen.getByLabelText('Define YAML') as HTMLTextAreaElement).value, ).toContain('review_step'); }); - expect(screen.getByTestId('workflow-step-detail-panel')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Save draft' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeInTheDocument(); + expect( + screen.getByTestId('workflow-step-detail-panel'), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Save draft' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Save draft' })); @@ -1253,15 +1396,15 @@ describe('StudioWorkflowBuildPanel', () => { 'nyxid.route_preference': '/api/v1/proxy/s/openai', }, prompt: 'Please triage the refund request.', - workflowYamls: [ - expect.stringContaining('name: workflow-demo'), - ], + workflowYamls: [expect.stringContaining('name: workflow-demo')], }), expect.any(AbortSignal), ); }); - expect(await screen.findByText('workflow draft output')).toBeInTheDocument(); + expect( + await screen.findByText('workflow draft output'), + ).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: 'Continue to Bind' })); @@ -1322,7 +1465,9 @@ describe('StudioWorkflowBuildPanel', () => { expect(screen.queryByLabelText('Parameter prompt')).not.toBeInTheDocument(); expect(screen.queryByText('prompt_prefix')).not.toBeInTheDocument(); expect( - screen.getByText(/Instruction added before each workflow run input reaches the LLM/i), + screen.getByText( + /Instruction added before each workflow run input reaches the LLM/i, + ), ).toBeInTheDocument(); fireEvent.change(promptPrefixInput, { @@ -1505,7 +1650,9 @@ describe('StudioWorkflowBuildPanel', () => { />, ); - fireEvent.click(await screen.findByRole('button', { name: 'approve_step' })); + fireEvent.click( + await screen.findByRole('button', { name: 'approve_step' }), + ); const promptInput = await screen.findByLabelText('Parameter prompt'); fireEvent.change(promptInput, { target: { @@ -1606,8 +1753,12 @@ describe('StudioWorkflowBuildPanel', () => { }, }); - expect(screen.getByText('Unexpected end of JSON input')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Apply changes' })).toBeDisabled(); + expect( + screen.getByText('Unexpected end of JSON input'), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Apply changes' }), + ).toBeDisabled(); expect(screen.getByRole('button', { name: 'Save draft' })).toBeDisabled(); expect(screen.getByRole('button', { name: 'Run' })).toBeDisabled(); @@ -1646,14 +1797,22 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Run' })); - expect(await screen.findByText('final workflow answer')).toBeInTheDocument(); + expect( + await screen.findByText('final workflow answer'), + ).toBeInTheDocument(); const outputSection = screen.getByText('Output'); const outputPanel = outputSection.parentElement; expect(outputPanel).not.toBeNull(); - expect(within(outputPanel as HTMLElement).getByText('final workflow answer')).toBeInTheDocument(); - expect(within(outputPanel as HTMLElement).queryByText(/runId:/i)).not.toBeInTheDocument(); - expect(within(outputPanel as HTMLElement).queryByText(/actorId:/i)).not.toBeInTheDocument(); + expect( + within(outputPanel as HTMLElement).getByText('final workflow answer'), + ).toBeInTheDocument(); + expect( + within(outputPanel as HTMLElement).queryByText(/runId:/i), + ).not.toBeInTheDocument(); + expect( + within(outputPanel as HTMLElement).queryByText(/actorId:/i), + ).not.toBeInTheDocument(); expect(screen.queryByText('Run summary')).not.toBeInTheDocument(); const debugDetailsToggle = await screen.findByText('Debug details'); @@ -1692,18 +1851,24 @@ describe('StudioWorkflowBuildPanel', () => { fireEvent.click(screen.getByRole('button', { name: 'Run' })); - expect(await screen.findByText('second node final answer')).toBeInTheDocument(); + expect( + await screen.findByText('second node final answer'), + ).toBeInTheDocument(); const outputSection = screen.getByText('Output'); const outputPanel = outputSection.parentElement; expect(outputPanel).not.toBeNull(); - expect(within(outputPanel as HTMLElement).queryByText('first node answer')).toBeNull(); - expect(within(outputPanel as HTMLElement).getByText('second node final answer')).toBeInTheDocument(); + expect( + within(outputPanel as HTMLElement).queryByText('first node answer'), + ).toBeNull(); + expect( + within(outputPanel as HTMLElement).getByText('second node final answer'), + ).toBeInTheDocument(); }); it('shows a friendly provider guidance message when draft run backend rejects the route', async () => { mockedRuntimeRunsApi.streamDraftRun.mockRejectedValueOnce( new Error( - "Service request failed. Status: 400 (Bad Request) | NyxID response: {\"message\":\"Bad request: Provider 'openai' not connected. Connect at /providers.\"}", + 'Service request failed. Status: 400 (Bad Request) | NyxID response: {"message":"Bad request: Provider \'openai\' not connected. Connect at /providers."}', ), ); @@ -1723,7 +1888,9 @@ describe('StudioWorkflowBuildPanel', () => { it('keeps a GAgent draft-run failure inside Build with a recovery path', async () => { mockedRuntimeGAgentApi.streamDraftRun.mockRejectedValueOnce( - new Error('GAgent draft run timed out before the backend returned any event.'), + new Error( + 'GAgent draft run timed out before the backend returned any event.', + ), ); render( @@ -1756,7 +1923,9 @@ describe('StudioWorkflowBuildPanel', () => { 'This only failed the Build dry-run. Adjust the prompt or tools and retry, or continue to Bind when the member definition is ready to publish.', ), ).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Continue to Bind' })).toBeEnabled(); + expect( + screen.getByRole('button', { name: 'Continue to Bind' }), + ).toBeEnabled(); }); it('locks apply changes while the step mutation is pending', async () => { diff --git a/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.tsx b/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.tsx index 8c2a55da08..b0f413ac98 100644 --- a/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.tsx +++ b/apps/aevatar-console-web/src/pages/studio/components/StudioBuildPanels.tsx @@ -1,22 +1,16 @@ import { parseCustomEvent } from '@aevatar-react-sdk/agui'; import { + type AGUIEvent, AGUIEventType, CustomEventName, - type AGUIEvent, } from '@aevatar-react-sdk/types'; -import { - CheckCircleOutlined, - CodeOutlined, - PlayCircleOutlined, - RobotOutlined, - ApartmentOutlined, -} from '@ant-design/icons'; +import { CheckCircleOutlined, PlayCircleOutlined } from '@ant-design/icons'; +import type { Node } from '@xyflow/react'; import { Alert, Button, Empty, Input, - message, Radio, Select, Space, @@ -24,18 +18,34 @@ import { Typography, } from 'antd'; import React from 'react'; -import type { Node } from '@xyflow/react'; -import GraphCanvas from '@/shared/graphs/GraphCanvas'; +import ScriptCodeEditor, { + type ScriptEditorFocusTarget, + type ScriptEditorMarker, +} from '@/modules/studio/scripts/ScriptCodeEditor'; import { parseRunContextData } from '@/shared/agui/customEventData'; import { parseBackendSSEStream } from '@/shared/agui/sseFrameNormalizer'; import { runtimeGAgentApi } from '@/shared/api/runtimeGAgentApi'; import { runtimeRunsApi } from '@/shared/api/runtimeRunsApi'; +import GraphCanvas from '@/shared/graphs/GraphCanvas'; +import { t } from '@/shared/i18n/messages'; import { - buildRuntimeGAgentKindValue, buildRuntimeGAgentKindLabel, + buildRuntimeGAgentKindValue, type RuntimeGAgentKindDescriptor, } from '@/shared/models/runtime/gagents'; import type { WorkflowPrimitiveDescriptor } from '@/shared/models/runtime/query'; +import { + createStepInspectorDraft, + type StudioStepInspectorDraft, +} from '@/shared/studio/document'; +import type { StudioGraphStep } from '@/shared/studio/graph'; +import { + buildNodeConfigFields, + findNodeConfigPrimitiveDescriptor, + formatNodeConfigFieldCopy, + updateNodeConfigFieldParametersText, + validateNodeConfigParametersText, +} from '@/shared/studio/nodeConfigFields'; import { addPackageFile, createSingleSourcePackage, @@ -46,21 +56,9 @@ import { renamePackageFile, serializePersistedSource, setEntrySourcePath, - updatePackageFileContent, updateEntryBehaviorTypeName, + updatePackageFileContent, } from '@/shared/studio/scriptPackage'; -import { - createStepInspectorDraft, - type StudioStepInspectorDraft, -} from '@/shared/studio/document'; -import { - buildNodeConfigFields, - findNodeConfigPrimitiveDescriptor, - formatNodeConfigFieldCopy, - updateNodeConfigFieldParametersText, - validateNodeConfigParametersText, - type NodeConfigField, -} from '@/shared/studio/nodeConfigFields'; import { scriptsApi } from '@/shared/studio/scriptsApi'; import type { DraftRunResult, @@ -72,18 +70,13 @@ import type { ScriptValidationDiagnostic, ScriptValidationResult, } from '@/shared/studio/scriptsModels'; -import type { StudioGraphStep } from '@/shared/studio/graph'; +import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { describeError } from '@/shared/ui/errorText'; import { AEVATAR_INTERACTIVE_BUTTON_CLASS, AEVATAR_INTERACTIVE_CHIP_CLASS, joinInteractiveClassNames, } from '@/shared/ui/interactionStandards'; -import ScriptCodeEditor, { - type ScriptEditorFocusTarget, - type ScriptEditorMarker, -} from '@/modules/studio/scripts/ScriptCodeEditor'; -import { t } from "@/shared/i18n/messages"; const buildWorkbenchGridStyle: React.CSSProperties = { display: 'grid', @@ -101,14 +94,6 @@ const scriptLaunchpadGridStyle: React.CSSProperties = { minWidth: 0, }; -const buildWorkbenchPrimaryColumnStyle: React.CSSProperties = { - alignSelf: 'start', - display: 'grid', - gap: 16, - minHeight: 0, - minWidth: 0, -}; - const workflowWorkbenchLayoutStyle: React.CSSProperties = { display: 'grid', gap: 16, @@ -119,12 +104,7 @@ const workflowWorkbenchLayoutStyle: React.CSSProperties = { const workflowEditingSurfaceHeight = 'clamp(560px, calc(100vh - 320px), 760px)'; const SCRIPT_SAVE_OBSERVATION_POLL_DELAYS_MS = [ - 1000, - 2000, - 3000, - 5000, - 5000, - 5000, + 1000, 2000, 3000, 5000, 5000, 5000, ] as const; const SCRIPT_STARTER_SOURCE = `using System; @@ -537,16 +517,33 @@ const GAGENT_DRAFT_RUN_TIMEOUT_MS = 30_000; const GAGENT_DRAFT_RUN_CLIENT_TIMEOUT_MS = GAGENT_DRAFT_RUN_TIMEOUT_MS + 5_000; function createGAgentDraftRunTimeoutError(): Error { - return new Error('GAgent draft run timed out before the backend returned any event.'); + return new Error( + 'GAgent draft run timed out before the backend returned any event.', + ); } function getRunDebugLines(state: DraftRunState): string[] { return [ - state.runId.trim() ? t("pages.studio.studiobuildpanels.current.run.ready", "current run: ready") : '', - state.actorId.trim() ? t("pages.studio.studiobuildpanels.runtime.actor.ready", "runtime actor: ready") : '', - state.commandId.trim() ? t("pages.studio.studiobuildpanels.command.accepted", "command: accepted") : '', + state.runId.trim() + ? t( + 'pages.studio.studiobuildpanels.current.run.ready', + 'current run: ready', + ) + : '', + state.actorId.trim() + ? t( + 'pages.studio.studiobuildpanels.runtime.actor.ready', + 'runtime actor: ready', + ) + : '', + state.commandId.trim() + ? t( + 'pages.studio.studiobuildpanels.command.accepted', + 'command: accepted', + ) + : '', state.events.length > 0 - ? t("pages.studio.studiobuildpanels.events.count", "events: {count}", { + ? t('pages.studio.studiobuildpanels.events.count', 'events: {count}', { count: state.events.length, }) : '', @@ -567,14 +564,23 @@ function renderRunOutput(state: DraftRunState): string { } if (state.status === 'running') { - return t("pages.studio.studiobuildpanels.waiting.for.assistant.output", "Waiting for assistant output..."); + return t( + 'pages.studio.studiobuildpanels.waiting.for.assistant.output', + 'Waiting for assistant output...', + ); } if (state.status === 'success' && getRunDebugLines(state).length > 0) { - return t("pages.studio.studiobuildpanels.run.completed.but.no", "Run completed, but no assistant output was returned."); + return t( + 'pages.studio.studiobuildpanels.run.completed.but.no', + 'Run completed, but no assistant output was returned.', + ); } - return t("pages.studio.studiobuildpanels.run.the.current.draft", "Run the current draft to inspect the assistant output here."); + return t( + 'pages.studio.studiobuildpanels.run.the.current.draft', + 'Run the current draft to inspect the assistant output here.', + ); } function renderRunSummary(state: DraftRunState): string { @@ -583,15 +589,24 @@ function renderRunSummary(state: DraftRunState): string { function getGAgentDraftRunRecoveryText(state: DraftRunState): string { if (state.status === 'running') { - return t("pages.studio.studiobuildpanels.draft.run.waiting.backend.events", "Draft run is still waiting for backend events. Keep the Build definition visible while this request completes."); + return t( + 'pages.studio.studiobuildpanels.draft.run.waiting.backend.events', + 'Draft run is still waiting for backend events. Keep the Build definition visible while this request completes.', + ); } if (state.status === 'error') { - return t("pages.studio.studiobuildpanels.build.dry.run.failed.recovery", "This only failed the Build dry-run. Adjust the prompt or tools and retry, or continue to Bind when the member definition is ready to publish."); + return t( + 'pages.studio.studiobuildpanels.build.dry.run.failed.recovery', + 'This only failed the Build dry-run. Adjust the prompt or tools and retry, or continue to Bind when the member definition is ready to publish.', + ); } if (state.status === 'success') { - return t("pages.studio.studiobuildpanels.draft.run.finished.continue.bind", "Draft run finished. Continue to Bind when you are ready to publish the callable member contract."); + return t( + 'pages.studio.studiobuildpanels.draft.run.finished.continue.bind', + 'Draft run finished. Continue to Bind when you are ready to publish the callable member contract.', + ); } return ''; @@ -607,7 +622,8 @@ function extractRunFinishedOutput(result: unknown): string { } const record = result as Record; - const candidate = record.output ?? record.Output ?? record.message ?? record.text; + const candidate = + record.output ?? record.Output ?? record.message ?? record.text; return typeof candidate === 'string' ? candidate : ''; } @@ -632,7 +648,7 @@ function areStepInspectorDraftsEqual( async function consumeAguiDraftRun( response: Response, signal: AbortSignal, - onChange: React.Dispatch>, + onChange: React.Dispatch>, ): Promise { for await (const event of parseBackendSSEStream(response, { signal })) { if (signal.aborted) { @@ -753,7 +769,9 @@ function mapScriptMarkers( })); } -function formatScriptDiagnosticLocation(diagnostic: ScriptValidationDiagnostic): string { +function formatScriptDiagnosticLocation( + diagnostic: ScriptValidationDiagnostic, +): string { const filePath = diagnostic.filePath || 'source'; if (!diagnostic.startLine || !diagnostic.startColumn) { return filePath; @@ -796,19 +814,32 @@ function ScriptLeaveDialog(props: { return (
- {t("pages.studio.studiobuildpanels.leave.script.build.2", "Leave Script Build?")} + {t( + 'pages.studio.studiobuildpanels.leave.script.build.2', + 'Leave Script Build?', + )} + - {t("pages.studio.studiobuildpanels.build.source.editor", "The current script draft is not saved. Leaving Build will discard unsaved changes in the source editor.")} + {t( + 'pages.studio.studiobuildpanels.build.source.editor', + 'The current script draft is not saved. Leaving Build will discard unsaved changes in the source editor.', + )} + - + + {t('pages.studio.studiobuildpanels.copy.2', 'Leave page')} +
); @@ -865,13 +896,13 @@ export type StudioWorkflowBuildPanelProps = { readonly onDeleteWorkflowNodes: (nodeIds: string[]) => Promise | void; readonly onAutoLayout: () => void; readonly onConnectNodes: (sourceNodeId: string, targetNodeId: string) => void; - readonly onNodeLayoutChange: ( - nodes: Node[], - ) => void; + readonly onNodeLayoutChange: (nodes: Node[]) => void; readonly onContinueToBind: () => void; }; -export const StudioWorkflowBuildPanel: React.FC = ({ +export const StudioWorkflowBuildPanel: React.FC< + StudioWorkflowBuildPanelProps +> = ({ draftYaml, onSetDraftYaml, onSaveDraft, @@ -903,14 +934,19 @@ export const StudioWorkflowBuildPanel: React.FC = onNodeLayoutChange, onContinueToBind, }) => { + const toast = useConsoleToast(); + const workflowActionFailure = t( + 'pages.studio.studiobuildpanels.workflowActionFailed', + 'Could not complete the workflow action. Review the details and try again.', + ); const panelRef = React.useRef(null); const [viewMode, setViewMode] = React.useState<'canvas' | 'yaml'>('canvas'); - const [runState, setRunState] = React.useState(IDLE_DRAFT_RUN_STATE); + const [runState, setRunState] = + React.useState(IDLE_DRAFT_RUN_STATE); const [workflowRunError, setWorkflowRunError] = React.useState(''); const [stepTypePickerOpen, setStepTypePickerOpen] = React.useState(false); - const [stepDraft, setStepDraft] = React.useState( - null, - ); + const [stepDraft, setStepDraft] = + React.useState(null); const [stepMutationPending, setStepMutationPending] = React.useState< '' | 'add' | 'apply' | 'remove' >(''); @@ -929,9 +965,7 @@ export const StudioWorkflowBuildPanel: React.FC = ) => StudioStepInspectorDraft | null), ) => { const nextDraft = - typeof updater === 'function' - ? updater(stepDraftRef.current) - : updater; + typeof updater === 'function' ? updater(stepDraftRef.current) : updater; stepDraftRef.current = nextDraft; setStepDraft(nextDraft); }, @@ -949,8 +983,7 @@ export const StudioWorkflowBuildPanel: React.FC = }, [selectedGraphNodeId, workflowGraph.steps]); const selectedNodeId = React.useMemo( () => - selectedGraphNodeId || - (selectedStep ? `step:${selectedStep.id}` : ''), + selectedGraphNodeId || (selectedStep ? `step:${selectedStep.id}` : ''), [selectedGraphNodeId, selectedStep], ); const selectedStepId = React.useMemo( @@ -963,10 +996,7 @@ export const StudioWorkflowBuildPanel: React.FC = [selectedGraphNodeId, selectedStep], ); const selectedStepDraftSeed = React.useMemo( - () => - selectedStep - ? createStepInspectorDraft(selectedStep) - : null, + () => (selectedStep ? createStepInspectorDraft(selectedStep) : null), [ selectedStep?.id, selectedStep?.type, @@ -992,19 +1022,24 @@ export const StudioWorkflowBuildPanel: React.FC = availableStepTypes.map((stepType) => { const descriptor = runtimePrimitives.find((primitive) => { - if (primitive.name.trim().toLowerCase() === stepType.trim().toLowerCase()) { + if ( + primitive.name.trim().toLowerCase() === + stepType.trim().toLowerCase() + ) { return true; } return primitive.aliases.some( - (alias) => alias.trim().toLowerCase() === stepType.trim().toLowerCase(), + (alias) => + alias.trim().toLowerCase() === stepType.trim().toLowerCase(), ); }) ?? null; return { stepType, description: - descriptor?.description?.trim() || 'Create a new workflow step of this type.', + descriptor?.description?.trim() || + 'Create a new workflow step of this type.', }; }), [availableStepTypes, runtimePrimitives], @@ -1075,28 +1110,30 @@ export const StudioWorkflowBuildPanel: React.FC = : ''; if (currentParameterError) { setStepMutationError(currentParameterError); - void message.error(currentParameterError); + toast.error(workflowActionFailure); return; } if (!scopeId) { - const visibleMessage = 'Resolve the current workspace before running the workflow draft.'; + const visibleMessage = + 'Resolve the current workspace before running the workflow draft.'; setWorkflowRunError(visibleMessage); - void message.error(visibleMessage); + toast.error(workflowActionFailure); return; } if (dryRunBlockedReason?.trim()) { const visibleMessage = dryRunBlockedReason.trim(); setWorkflowRunError(visibleMessage); - void message.error(visibleMessage); + toast.error(workflowActionFailure); return; } if (!runPrompt.trim()) { - const visibleMessage = 'Sample input is required before running the workflow draft.'; + const visibleMessage = + 'Sample input is required before running the workflow draft.'; setWorkflowRunError(visibleMessage); - void message.error(visibleMessage); + toast.error(workflowActionFailure); return; } @@ -1119,7 +1156,10 @@ export const StudioWorkflowBuildPanel: React.FC = workflowYamls: await buildWorkflowYamls( currentStepDraft && selectedStepDraftSeed && - !areStepInspectorDraftsEqual(currentStepDraft, selectedStepDraftSeed) + !areStepInspectorDraftsEqual( + currentStepDraft, + selectedStepDraftSeed, + ) ? { stepId: selectedStepId, draft: currentStepDraft, @@ -1145,15 +1185,18 @@ export const StudioWorkflowBuildPanel: React.FC = } const rawMessage = describeError(error); - const disconnectedProvider = rawMessage.match(/Provider '([^']+)' not connected/i); - const visibleMessage = - disconnectedProvider - ? t("pages.studio.studiobuildpanels.dry.run.provider.provider", "Dry-run cannot run because the {value1} provider is not connected yet. Connect an available provider, then run the current workflow draft again.", { value1: disconnectedProvider[1] }) - : rawMessage; - setWorkflowRunError( - visibleMessage, + const disconnectedProvider = rawMessage.match( + /Provider '([^']+)' not connected/i, ); - void message.error(visibleMessage); + const visibleMessage = disconnectedProvider + ? t( + 'pages.studio.studiobuildpanels.dry.run.provider.provider', + 'Dry-run cannot run because the {value1} provider is not connected yet. Connect an available provider, then run the current workflow draft again.', + { value1: disconnectedProvider[1] }, + ) + : rawMessage; + setWorkflowRunError(visibleMessage); + toast.error(workflowActionFailure); setRunState({ ...IDLE_DRAFT_RUN_STATE, error: rawMessage, @@ -1173,28 +1216,33 @@ export const StudioWorkflowBuildPanel: React.FC = scopeId, selectedStepDraftSeed, selectedStepId, + toast, + workflowActionFailure, ]); - const handleInsertStep = React.useCallback(async (stepType: string) => { - if (stepMutationPendingRef.current) { - return; - } + const handleInsertStep = React.useCallback( + async (stepType: string) => { + if (stepMutationPendingRef.current) { + return; + } - stepMutationPendingRef.current = true; - setStepMutationPending('add'); - setStepMutationError(''); - try { - await onInsertStep(stepType); - setStepTypePickerOpen(false); - } catch (error) { - const visibleMessage = describeError(error); - setStepMutationError(visibleMessage); - void message.error(visibleMessage); - } finally { - stepMutationPendingRef.current = false; - setStepMutationPending(''); - } - }, [onInsertStep]); + stepMutationPendingRef.current = true; + setStepMutationPending('add'); + setStepMutationError(''); + try { + await onInsertStep(stepType); + setStepTypePickerOpen(false); + } catch (error) { + const visibleMessage = describeError(error); + setStepMutationError(visibleMessage); + toast.error(workflowActionFailure); + } finally { + stepMutationPendingRef.current = false; + setStepMutationPending(''); + } + }, + [onInsertStep, toast, workflowActionFailure], + ); const handleApplyStepChanges = React.useCallback(async () => { const currentStepDraft = stepDraftRef.current; @@ -1207,7 +1255,7 @@ export const StudioWorkflowBuildPanel: React.FC = ); if (currentParameterError) { setStepMutationError(currentParameterError); - void message.error(currentParameterError); + toast.error(workflowActionFailure); return; } @@ -1219,12 +1267,12 @@ export const StudioWorkflowBuildPanel: React.FC = } catch (error) { const visibleMessage = describeError(error); setStepMutationError(visibleMessage); - void message.error(visibleMessage); + toast.error(workflowActionFailure); } finally { stepMutationPendingRef.current = false; setStepMutationPending(''); } - }, [onApplyStepDraft]); + }, [onApplyStepDraft, toast, workflowActionFailure]); const handleSaveDraft = React.useCallback(() => { const currentStepDraft = stepDraftRef.current; @@ -1233,7 +1281,7 @@ export const StudioWorkflowBuildPanel: React.FC = : ''; if (currentParameterError) { setStepMutationError(currentParameterError); - void message.error(currentParameterError); + toast.error(workflowActionFailure); return; } @@ -1247,7 +1295,13 @@ export const StudioWorkflowBuildPanel: React.FC = } : null, ); - }, [onSaveDraft, selectedStepDraftSeed, selectedStepId]); + }, [ + onSaveDraft, + selectedStepDraftSeed, + selectedStepId, + toast, + workflowActionFailure, + ]); const handleRemoveStep = React.useCallback(async () => { if (stepMutationPendingRef.current) { @@ -1262,35 +1316,38 @@ export const StudioWorkflowBuildPanel: React.FC = } catch (error) { const visibleMessage = describeError(error); setStepMutationError(visibleMessage); - void message.error(visibleMessage); + toast.error(workflowActionFailure); } finally { stepMutationPendingRef.current = false; setStepMutationPending(''); } - }, [onRemoveSelectedStep]); - - const handleDeleteNodes = React.useCallback(async (nodeIds: string[]) => { - const normalizedNodeIds = nodeIds - .map((nodeId) => String(nodeId ?? '').trim()) - .filter(Boolean); - if (normalizedNodeIds.length === 0 || stepMutationPendingRef.current) { - return; - } + }, [onRemoveSelectedStep, toast, workflowActionFailure]); + + const handleDeleteNodes = React.useCallback( + async (nodeIds: string[]) => { + const normalizedNodeIds = nodeIds + .map((nodeId) => String(nodeId ?? '').trim()) + .filter(Boolean); + if (normalizedNodeIds.length === 0 || stepMutationPendingRef.current) { + return; + } - stepMutationPendingRef.current = true; - setStepMutationPending('remove'); - setStepMutationError(''); - try { - await onDeleteWorkflowNodes(normalizedNodeIds); - } catch (error) { - const visibleMessage = describeError(error); - setStepMutationError(visibleMessage); - void message.error(visibleMessage); - } finally { - stepMutationPendingRef.current = false; - setStepMutationPending(''); - } - }, [onDeleteWorkflowNodes]); + stepMutationPendingRef.current = true; + setStepMutationPending('remove'); + setStepMutationError(''); + try { + await onDeleteWorkflowNodes(normalizedNodeIds); + } catch (error) { + const visibleMessage = describeError(error); + setStepMutationError(visibleMessage); + toast.error(workflowActionFailure); + } finally { + stepMutationPendingRef.current = false; + setStepMutationPending(''); + } + }, + [onDeleteWorkflowNodes, toast, workflowActionFailure], + ); const workflowCanvasAutoFitKey = React.useMemo( () => @@ -1335,12 +1392,25 @@ export const StudioWorkflowBuildPanel: React.FC = data-testid="studio-workflow-build-panel" style={workflowWorkbenchLayoutStyle} > -
+
-
{t("pages.studio.studiobuildpanels.build.actions.2", "Build actions")}
+
+ {t( + 'pages.studio.studiobuildpanels.build.actions.2', + 'Build actions', + )} +
- {canSaveWorkflow ? t("pages.studio.studiobuildpanels.draft.ready.2", "draft ready") : t("pages.studio.studiobuildpanels.saved", "saved")} + {canSaveWorkflow + ? t( + 'pages.studio.studiobuildpanels.draft.ready.2', + 'draft ready', + ) + : t('pages.studio.studiobuildpanels.saved', 'saved')}
@@ -1350,13 +1420,18 @@ export const StudioWorkflowBuildPanel: React.FC = loading={savePending} onClick={handleSaveDraft} > - {t("pages.studio.studiobuildpanels.save.draft.2", "Save draft")} + {t('pages.studio.studiobuildpanels.save.draft.2', 'Save draft')} + + {t( + 'pages.studio.studiobuildpanels.continue.to.bind.4', + 'Continue to Bind', + )} +
{saveNotice ? ( @@ -1374,21 +1449,46 @@ export const StudioWorkflowBuildPanel: React.FC = ) : null}
-
+
-
{t("pages.studio.studiobuildpanels.dag.canvas.2", "DAG Canvas")}
- {t("pages.studio.studiobuildpanels.canvas.live.2", "canvas · live")} +
+ {t('pages.studio.studiobuildpanels.dag.canvas.2', 'DAG Canvas')} +
+ + {t( + 'pages.studio.studiobuildpanels.canvas.live.2', + 'canvas · live', + )} + - {workflowName || t("pages.studio.studiobuildpanels.untitled.workflow.2", "Untitled workflow")} + {workflowName || + t( + 'pages.studio.studiobuildpanels.untitled.workflow.2', + 'Untitled workflow', + )}
-
+
+ {t('pages.studio.studiobuildpanels.canvas.2', 'Canvas')} + -
+ {t('pages.studio.studiobuildpanels.yaml.3', 'YAML')} + + + {t('pages.studio.studiobuildpanels.add.step.2', 'Add step')} + + {t( + 'pages.studio.studiobuildpanels.auto.layout.2', + 'Auto-layout', + )} +
{stepTypePickerOpen ? ( -
-
{t("pages.studio.studiobuildpanels.choose.step.type.2", "Choose step type")}
+
+
+ {t( + 'pages.studio.studiobuildpanels.choose.step.type.2', + 'Choose step type', + )} +
- {t("pages.studio.studiobuildpanels.step", "Choose which step type to insert, then connect it after the currently selected node.")}
+ {t( + 'pages.studio.studiobuildpanels.step', + 'Choose which step type to insert, then connect it after the currently selected node.', + )} +
= style={workflowTypeOptionStyle} type="button" > - {entry.stepType} - + + {entry.stepType} + + {entry.description} @@ -1486,7 +1613,11 @@ export const StudioWorkflowBuildPanel: React.FC = data-deletion-tracking-id={ STUDIO_WORKFLOW_YAML_AUTHORING_DELETION_TRACKING.issue } - style={{ ...buildSurfaceCardStyle, flex: '1 1 auto', minHeight: 0 }} + style={{ + ...buildSurfaceCardStyle, + flex: '1 1 auto', + minHeight: 0, + }} >
= justifyContent: 'space-between', }} > -
{t("pages.studio.studiobuildpanels.workflow.yaml.2", "Workflow YAML")}
- {t("pages.studio.studiobuildpanels.raw.draft.2", "raw draft")} +
+ {t( + 'pages.studio.studiobuildpanels.workflow.yaml.2', + 'Workflow YAML', + )} +
+ + {t( + 'pages.studio.studiobuildpanels.raw.draft.2', + 'raw draft', + )} +
onSetDraftYaml(event.target.value)} @@ -1523,324 +1667,489 @@ export const StudioWorkflowBuildPanel: React.FC = }} >
-
{t("pages.studio.studiobuildpanels.step.detail.2", "Step Detail")}
- {selectedStep ? {selectedStep.id} : null} +
+ {t( + 'pages.studio.studiobuildpanels.step.detail.2', + 'Step Detail', + )} +
+ {selectedStep ? ( + {selectedStep.id} + ) : null}
{selectedStep ? {selectedStep.type} : null}
- {stepMutationError ? : null} + {stepMutationError ? ( + + ) : null}
{selectedStep && stepDraft ? ( <>
-
-
{t("pages.studio.studiobuildpanels.basics.2", "Basics")}
- - - updateStepDraft((current) => - current - ? { - ...current, - id: event.target.value, - } - : current, - ) - } - /> - - ({ - label: t("pages.studio.studiobuildpanels.copy.4", "{value1} ({value2})", { value1: role.name, value2: role.id }), - value: role.id, - }))} - placeholder={workflowRoleIds[0] || t("pages.studio.studiobuildpanels.select.role", "Select role")} - value={stepDraft.targetRole || undefined} - onChange={(value) => - updateStepDraft((current) => - current - ? { - ...current, - targetRole: value || '', - } - : current, - ) - } - /> - - + updateStepDraft((current) => + current + ? { + ...current, + id: event.target.value, + } + : current, + ) + } /> - ) : null} - {stepParameterConfig?.fields.length ? ( -
- {stepParameterConfig.fields.map((field) => { - const label = formatNodeConfigFieldCopy(field.label); - const description = formatNodeConfigFieldCopy( - field.description, - ); - const placeholder = formatNodeConfigFieldCopy( - field.placeholder, - ); - const parameterAriaLabel = `Parameter ${label}`; - const inputId = `workflow-step-parameter-${field.name}`; - return ( -
- - {field.kind === 'select' ? ( - - updateStepDraft((current) => - current - ? { - ...current, - parametersText: - updateNodeConfigFieldParametersText({ - field, - nodeType: current.type, - parametersText: current.parametersText, - rawValue: event.target.value, - }), - } - : current, - ) - } - /> - )} -
- {description} -
-
- ); - })} + + ({ + label: t( + 'pages.studio.studiobuildpanels.copy.4', + '{value1} ({value2})', + { value1: role.name, value2: role.id }, + ), + value: role.id, + }))} + placeholder={ + workflowRoleIds[0] || + t( + 'pages.studio.studiobuildpanels.select.role', + 'Select role', + ) + } + value={stepDraft.targetRole || undefined} + onChange={(value) => + updateStepDraft((current) => + current + ? { + ...current, + targetRole: value || '', + } + : current, + ) + } + /> + + ({ + label: formatNodeConfigFieldCopy( + option.label, + ), + value: option.value, + }))} + placeholder={placeholder} + value={field.value || undefined} + onChange={(value) => + updateStepDraft((current) => + current + ? { + ...current, + parametersText: + updateNodeConfigFieldParametersText( + { + field, + nodeType: current.type, + parametersText: + current.parametersText, + rawValue: String(value || ''), + }, + ), + } + : current, + ) + } + /> + ) : field.kind === 'json' ? ( + + updateStepDraft((current) => + current + ? { + ...current, + parametersText: + updateNodeConfigFieldParametersText( + { + field, + nodeType: current.type, + parametersText: + current.parametersText, + rawValue: event.target.value, + }, + ), + } + : current, + ) + } + /> + ) : ( + + updateStepDraft((current) => + current + ? { + ...current, + parametersText: + updateNodeConfigFieldParametersText( + { + field, + nodeType: current.type, + parametersText: + current.parametersText, + rawValue: event.target.value, + }, + ), + } + : current, + ) + } + /> + )} +
+ {description} +
+
+ ); + })} +
+ ) : ( +
+ {t( + 'pages.studio.studiobuildpanels.step.type.raw.json', + 'The current step type has no guided parameters declared. Edit the raw JSON below directly.', + )} +
+ )} +
+ + {t( + 'pages.studio.studiobuildpanels.raw.parameters.json.2', + 'Raw parameters JSON', + )} + +
+ + + updateStepDraft((current) => + current + ? { + ...current, + parametersText: event.target.value, + } + : current, + ) + } + /> +
+
+
+
+
+ + {t( + 'pages.studio.studiobuildpanels.advanced.routing.json.2', + 'Advanced routing JSON', + )} + +
+ + + updateStepDraft((current) => + current + ? { + ...current, + branchesText: event.target.value, + } + : current, + ) + } + /> +
+
+
-
-
- - -
- - ) : ( - - )} +
+ + +
+ + ) : ( + + )}
-
-
+
+
-
{t("pages.studio.studiobuildpanels.dry.run.4", "Dry-run")}
- {t("pages.studio.studiobuildpanels.workflow.draft.run.2", "Workflow draft run")} +
+ {t('pages.studio.studiobuildpanels.dry.run.4', 'Dry-run')} +
+ + {t( + 'pages.studio.studiobuildpanels.workflow.draft.run.2', + 'Workflow draft run', + )} +
- - {t("pages.studio.studiobuildpanels.draft.input", "Draft input")} + + {t('pages.studio.studiobuildpanels.draft.input', 'Draft input')} +
- {t("pages.studio.studiobuildpanels.route.label", "Route: ")}{dryRunRouteLabel || t("pages.studio.studiobuildpanels.config.default.2", "Config default")} + {t('pages.studio.studiobuildpanels.route.label', 'Route: ')} + {dryRunRouteLabel || + t( + 'pages.studio.studiobuildpanels.config.default.2', + 'Config default', + )}
- {t("pages.studio.studiobuildpanels.model.label", "Model: ")}{dryRunModelLabel || t("pages.studio.studiobuildpanels.use.configured.default.2", "Use configured default")} + {t('pages.studio.studiobuildpanels.model.label', 'Model: ')} + {dryRunModelLabel || + t( + 'pages.studio.studiobuildpanels.use.configured.default.2', + 'Use configured default', + )}
{dryRunBlockedReason ? ( @@ -1853,7 +2162,11 @@ export const StudioWorkflowBuildPanel: React.FC = type="link" onClick={onOpenRunSetup} > - {t("pages.studio.studiobuildpanels.connect.provider.2", "Connect provider")} + {t( + 'pages.studio.studiobuildpanels.connect.provider.2', + 'Connect provider', + )} + ) : undefined } message={dryRunBlockedReason} @@ -1862,9 +2175,15 @@ export const StudioWorkflowBuildPanel: React.FC = /> ) : null} onRunPromptChange(event.target.value)} /> @@ -1881,7 +2200,8 @@ export const StudioWorkflowBuildPanel: React.FC = } onClick={() => void handleRun()} > - {t("pages.studio.studiobuildpanels.run.4", "Run")} + {t('pages.studio.studiobuildpanels.run.4', 'Run')} + + {t( + 'pages.studio.studiobuildpanels.load.sample.input', + 'Load sample input', + )} + {workflowRunError ? ( ) : null}
-
{t("pages.studio.studiobuildpanels.output.4", "Output")}
-
{renderRunOutput(runState)}
+
+ {t('pages.studio.studiobuildpanels.output.4', 'Output')} +
+
+            {renderRunOutput(runState)}
+          
{renderRunSummary(runState) ? (
- {t("pages.studio.studiobuildpanels.debug.details.3", "Debug details")} -
{renderRunSummary(runState)}
+ + {t( + 'pages.studio.studiobuildpanels.debug.details.3', + 'Debug details', + )} + +
+              {renderRunSummary(runState)}
+            
) : null}
@@ -1932,10 +2267,16 @@ export type StudioScriptBuildPanelProps = { readonly onCreateScriptDraft?: () => void; readonly onRefreshScripts?: () => Promise | unknown; readonly onContinueToBind: () => void; - readonly onRegisterLeaveGuard?: (guard: (() => Promise) | null) => void; - readonly onScriptBuildStateChange?: (state: StudioScriptBuildState | null) => void; + readonly onRegisterLeaveGuard?: ( + guard: (() => Promise) | null, + ) => void; + readonly onScriptBuildStateChange?: ( + state: StudioScriptBuildState | null, + ) => void; readonly pendingScriptDraft?: StudioPendingScriptDraft | null; - readonly onPendingScriptDraftChange?: (draft: StudioPendingScriptDraft | null) => void; + readonly onPendingScriptDraftChange?: ( + draft: StudioPendingScriptDraft | null, + ) => void; readonly onScriptDraftSaved?: (scriptId: string) => void; }; @@ -2008,7 +2349,8 @@ function buildAppliedScriptDetail( script: summary, source: { sourceText, - definitionActorId: summary.definitionActorId || acceptedScript.definitionActorId, + definitionActorId: + summary.definitionActorId || acceptedScript.definitionActorId, revision: summary.activeRevision || acceptedScript.revisionId, sourceHash: summary.activeSourceHash || acceptedScript.sourceHash, }, @@ -2089,16 +2431,19 @@ export const StudioScriptBuildPanel: React.FC = ({ const [runOutput, setRunOutput] = React.useState( 'Run the current script draft to inspect the draft-run result here.', ); - const [lastRunResult, setLastRunResult] = React.useState(null); + const [lastRunResult, setLastRunResult] = + React.useState(null); const [leaveDialogOpen, setLeaveDialogOpen] = React.useState(false); - const leaveResolverRef = React.useRef<((value: boolean) => void) | null>(null); + const leaveResolverRef = React.useRef<((value: boolean) => void) | null>( + null, + ); const saveObservationTimerRef = React.useRef(null); const saveObservationTokenRef = React.useRef(0); const activeScriptIdRef = React.useRef(''); const availableScripts = React.useMemo( () => - (scriptsQuery.data ?? []).filter( - (detail): detail is ScopedScriptDetail => Boolean(detail.available && detail.script), + (scriptsQuery.data ?? []).filter((detail): detail is ScopedScriptDetail => + Boolean(detail.available && detail.script), ), [scriptsQuery.data], ); @@ -2110,7 +2455,10 @@ export const StudioScriptBuildPanel: React.FC = ({ [pendingScriptDraft, scopeId, selectedScriptId], ); const selectedCatalogScript = React.useMemo( - () => availableScripts.find((detail) => detail.script?.scriptId === selectedScriptId) || null, + () => + availableScripts.find( + (detail) => detail.script?.scriptId === selectedScriptId, + ) || null, [availableScripts, selectedScriptId], ); const selectedObservedAppliedScript = React.useMemo( @@ -2128,9 +2476,12 @@ export const StudioScriptBuildPanel: React.FC = ({ availableScripts[0] || null; activeScriptIdRef.current = activeScript?.script?.scriptId || ''; - const activeScriptIsDraft = Boolean(pendingScriptDetail && activeScript === pendingScriptDetail); + const activeScriptIsDraft = Boolean( + pendingScriptDetail && activeScript === pendingScriptDetail, + ); const activeScriptIsObserved = Boolean( - selectedObservedAppliedScript && activeScript === selectedObservedAppliedScript, + selectedObservedAppliedScript && + activeScript === selectedObservedAppliedScript, ); const persistedSource = React.useMemo( () => activeScript?.source?.sourceText || '', @@ -2148,7 +2499,11 @@ export const StudioScriptBuildPanel: React.FC = ({ [scriptPackage, selectedFilePath], ); const editorMarkers = React.useMemo( - () => mapScriptMarkers(validationResult?.diagnostics, selectedPackageEntry?.path || ''), + () => + mapScriptMarkers( + validationResult?.diagnostics, + selectedPackageEntry?.path || '', + ), [selectedPackageEntry?.path, validationResult?.diagnostics], ); const packageEntries = React.useMemo( @@ -2165,15 +2520,18 @@ export const StudioScriptBuildPanel: React.FC = ({ return; } - const nextPackage = activeScriptIsDraft && !activeScript.source?.sourceText - ? createScriptStarterPackage() - : deserializePersistedSource(activeScript.source?.sourceText || ''); + const nextPackage = + activeScriptIsDraft && !activeScript.source?.sourceText + ? createScriptStarterPackage() + : deserializePersistedSource(activeScript.source?.sourceText || ''); const nextEntry = getSelectedPackageEntry(nextPackage, nextPackage.entrySourcePath) || getSelectedPackageEntry(nextPackage, '') || null; setScriptPackage(nextPackage); - setSelectedFilePath(nextEntry?.path || nextPackage.entrySourcePath || 'Behavior.cs'); + setSelectedFilePath( + nextEntry?.path || nextPackage.entrySourcePath || 'Behavior.cs', + ); setValidationResult(null); setValidationError(''); setFocusedDiagnostic(null); @@ -2194,12 +2552,22 @@ export const StudioScriptBuildPanel: React.FC = ({ ]); React.useEffect(() => { - if (selectedScriptId || pendingScriptDraft?.scriptId || !availableScripts[0]?.script?.scriptId) { + const firstAvailableScriptId = availableScripts[0]?.script?.scriptId; + if ( + selectedScriptId || + pendingScriptDraft?.scriptId || + !firstAvailableScriptId + ) { return; } - onSelectScriptId(availableScripts[0].script!.scriptId); - }, [availableScripts, onSelectScriptId, pendingScriptDraft?.scriptId, selectedScriptId]); + onSelectScriptId(firstAvailableScriptId); + }, [ + availableScripts, + onSelectScriptId, + pendingScriptDraft?.scriptId, + selectedScriptId, + ]); const cancelSaveObservationPoll = React.useCallback(() => { saveObservationTokenRef.current += 1; @@ -2248,16 +2616,15 @@ export const StudioScriptBuildPanel: React.FC = ({ : validationError ? 'invalid' : 'unknown'; - const effectiveSaveStatus: StudioScriptBuildState['saveStatus'] = - isDirty - ? saveStatus === 'failed' - ? 'failed' - : 'idle' - : saveStatus === 'accepted' || saveStatus === 'failed' - ? saveStatus - : activeScript?.script?.activeRevision - ? 'applied' - : saveStatus; + const effectiveSaveStatus: StudioScriptBuildState['saveStatus'] = isDirty + ? saveStatus === 'failed' + ? 'failed' + : 'idle' + : saveStatus === 'accepted' || saveStatus === 'failed' + ? saveStatus + : activeScript?.script?.activeRevision + ? 'applied' + : saveStatus; const scriptReadyToBind = Boolean( activeScript?.script?.scriptId && !isDirty && @@ -2267,42 +2634,43 @@ export const StudioScriptBuildPanel: React.FC = ({ const lifecycleStatus = !activeScript?.script?.scriptId ? 'No script' : isDirty && effectiveSaveStatus !== 'failed' - ? 'Unsaved edits' - : saveObservationStatus === 'accepted' || effectiveSaveStatus === 'accepted' - ? 'Save accepted' - : saveObservationStatus === 'pending' - ? 'Waiting for catalog' - : effectiveSaveStatus === 'failed' - ? 'Save needs attention' - : activeScriptIsDraft - ? 'Draft' - : scriptReadyToBind - ? 'Catalog applied' - : 'Catalog script'; + ? 'Unsaved edits' + : saveObservationStatus === 'accepted' || + effectiveSaveStatus === 'accepted' + ? 'Save accepted' + : saveObservationStatus === 'pending' + ? 'Waiting for catalog' + : effectiveSaveStatus === 'failed' + ? 'Save needs attention' + : activeScriptIsDraft + ? 'Draft' + : scriptReadyToBind + ? 'Catalog applied' + : 'Catalog script'; const lifecycleStatusColor = lifecycleStatus === 'Catalog applied' ? 'green' : lifecycleStatus === 'Save needs attention' ? 'red' - : lifecycleStatus === 'Waiting for catalog' || lifecycleStatus === 'Save accepted' + : lifecycleStatus === 'Waiting for catalog' || + lifecycleStatus === 'Save accepted' ? 'gold' : activeScriptIsDraft ? 'blue' : 'default'; - const bindReadinessLabel = - !activeScript?.script?.scriptId - ? 'Create or select a script' - : validationStatus === 'invalid' - ? 'Fix validation errors' - : isDirty && validationStatus !== 'valid' - ? 'Validate current source' - : isDirty - ? 'Save script' - : effectiveSaveStatus === 'accepted' - ? 'Waiting for catalog' - : scriptReadyToBind - ? 'Ready to bind' - : 'Save script'; + const bindReadinessLabel = !activeScript?.script?.scriptId + ? 'Create or select a script' + : validationStatus === 'invalid' + ? 'Fix validation errors' + : isDirty && validationStatus !== 'valid' + ? 'Validate current source' + : isDirty + ? 'Save script' + : effectiveSaveStatus === 'accepted' + ? 'Waiting for catalog' + : scriptReadyToBind + ? 'Ready to bind' + : 'Save script'; const saveObservationInFlight = saveObservationStatus === 'accepted' || saveObservationStatus === 'pending'; const saveNoticeType = @@ -2329,7 +2697,8 @@ export const StudioScriptBuildPanel: React.FC = ({ onScriptBuildStateChange?.({ scriptId: activeScript.script.scriptId, - displayName: pendingScriptDraft?.displayName || activeScript.script.scriptId, + displayName: + pendingScriptDraft?.displayName || activeScript.script.scriptId, scriptRevision: activeScript.source?.revision || activeScript.script.activeRevision || @@ -2392,7 +2761,12 @@ export const StudioScriptBuildPanel: React.FC = ({ }); } }, - [activeScriptIsDraft, cancelSaveObservationPoll, onPendingScriptDraftChange, pendingScriptDraft], + [ + activeScriptIsDraft, + cancelSaveObservationPoll, + onPendingScriptDraftChange, + pendingScriptDraft, + ], ); const handleValidate = React.useCallback(async () => { @@ -2442,7 +2816,7 @@ export const StudioScriptBuildPanel: React.FC = ({ setSaveObservationStatus('applied'); setSaveStatus('applied'); setSaveNotice( - t("pages.studio.studiobuildpanels.save.applied", "Save applied."), + t('pages.studio.studiobuildpanels.save.applied', 'Save applied.'), ); }, [onRefreshScripts, onScriptDraftSaved, scopeId], @@ -2497,7 +2871,10 @@ export const StudioScriptBuildPanel: React.FC = ({ setSaveStatus('failed'); setSaveNotice( observation.message || - t("pages.studio.studiobuildpanels.save.rejected", "Save rejected."), + t( + 'pages.studio.studiobuildpanels.save.rejected', + 'Save rejected.', + ), ); return; } @@ -2508,8 +2885,8 @@ export const StudioScriptBuildPanel: React.FC = ({ saveObservationTimerRef.current = null; setSaveNotice( t( - "pages.studio.studiobuildpanels.save.accepted.waiting.for.catalog", - "Save accepted. Still waiting for catalog; use Refresh catalog to check again.", + 'pages.studio.studiobuildpanels.save.accepted.waiting.for.catalog', + 'Save accepted. Still waiting for catalog; use Refresh catalog to check again.', ), ); return; @@ -2517,8 +2894,8 @@ export const StudioScriptBuildPanel: React.FC = ({ setSaveNotice( t( - "pages.studio.studiobuildpanels.save.accepted.checking.again", - "Save accepted. Waiting for catalog; checking again in {value1}s.", + 'pages.studio.studiobuildpanels.save.accepted.checking.again', + 'Save accepted. Waiting for catalog; checking again in {value1}s.', { value1: Math.round(nextDelay / 1000) }, ), ); @@ -2548,7 +2925,9 @@ export const StudioScriptBuildPanel: React.FC = ({ const handleSave = React.useCallback(async () => { if (!scopeId || !activeScript?.script?.scriptId) { - setSaveNotice('Resolve the current workspace and select a script before saving.'); + setSaveNotice( + 'Resolve the current workspace and select a script before saving.', + ); return; } @@ -2631,12 +3010,20 @@ export const StudioScriptBuildPanel: React.FC = ({ return; } - const nextPackage = removePackageFile(scriptPackage, selectedPackageEntry.path); + const nextPackage = removePackageFile( + scriptPackage, + selectedPackageEntry.path, + ); const nextEntry = getSelectedPackageEntry(nextPackage, nextPackage.entrySourcePath) || getSelectedPackageEntry(nextPackage, ''); commitScriptPackage(nextPackage, nextEntry?.path); - }, [commitScriptPackage, packageEntries.length, scriptPackage, selectedPackageEntry]); + }, [ + commitScriptPackage, + packageEntries.length, + scriptPackage, + selectedPackageEntry, + ]); const handleSetEntrySource = React.useCallback(() => { if (!selectedPackageEntry || selectedPackageEntry.kind !== 'csharp') { @@ -2651,7 +3038,9 @@ export const StudioScriptBuildPanel: React.FC = ({ const handlePromoteEvolution = React.useCallback(async () => { if (!scopeId || !activeScript?.script?.scriptId) { - setPromotionNotice('Resolve the current workspace and script before proposing evolution.'); + setPromotionNotice( + 'Resolve the current workspace and script before proposing evolution.', + ); return; } @@ -2672,7 +3061,8 @@ export const StudioScriptBuildPanel: React.FC = ({ setPromotionNotice( decision.accepted ? 'Promotion accepted.' - : decision.failureReason || `Promotion ${decision.status || 'not accepted'}.`, + : decision.failureReason || + `Promotion ${decision.status || 'not accepted'}.`, ); } catch (error) { setPromotionNotice(describeError(error)); @@ -2690,7 +3080,9 @@ export const StudioScriptBuildPanel: React.FC = ({ const handleRun = React.useCallback(async () => { if (!scopeId || !activeScript?.script?.scriptId) { - setRunOutput('Resolve the current workspace and select a script before running.'); + setRunOutput( + 'Resolve the current workspace and select a script before running.', + ); return; } @@ -2728,16 +3120,26 @@ export const StudioScriptBuildPanel: React.FC = ({ if (scriptsQuery.isLoading) { return ( -
+
- {t("pages.studio.studiobuildpanels.loading.workspace.scripts.2", "Loading workspace scripts...")} + {t( + 'pages.studio.studiobuildpanels.loading.workspace.scripts.2', + 'Loading workspace scripts...', + )} +
); } if (scriptsQuery.isError) { return ( -
+
= ({ return (
-
{t("pages.studio.studiobuildpanels.script.source.2", "Script Source")}
+
+ {t( + 'pages.studio.studiobuildpanels.script.source.2', + 'Script Source', + )} +
{hasActiveScript - ? t("pages.studio.studiobuildpanels.script.mode.script.draft", "Script mode does one thing: iterate on the current script draft's typed source, lint results, and dry-run implementation.") - : t("pages.studio.studiobuildpanels.create.script.to.start.editing.saved", "Create a script to start editing. Saved workspace scripts appear here when this catalog has one.")} + ? t( + 'pages.studio.studiobuildpanels.script.mode.script.draft', + "Script mode does one thing: iterate on the current script draft's typed source, lint results, and dry-run implementation.", + ) + : t( + 'pages.studio.studiobuildpanels.create.script.to.start.editing.saved', + 'Create a script to start editing. Saved workspace scripts appear here when this catalog has one.', + )}
-
+
{hasActiveScript ? ( <> @@ -2774,16 +3196,25 @@ export const StudioScriptBuildPanel: React.FC = ({ ) : null} {hasActiveScript ? ( commitScriptPackage( - updateEntryBehaviorTypeName(scriptPackage, event.target.value), + updateEntryBehaviorTypeName( + scriptPackage, + event.target.value, + ), selectedPackageEntry.path, ) } @@ -2984,13 +3499,21 @@ export const StudioScriptBuildPanel: React.FC = ({ disabled={selectedPackageEntry.kind !== 'csharp'} onClick={handleSetEntrySource} > - {t("pages.studio.studiobuildpanels.set.entry.source.2", "Set entry source")} + {t( + 'pages.studio.studiobuildpanels.set.entry.source.2', + 'Set entry source', + )} +
{ @@ -3019,28 +3542,48 @@ export const StudioScriptBuildPanel: React.FC = ({ }} >
-
{t("pages.studio.studiobuildpanels.compiler.2", "Compiler")}
+
+ {t('pages.studio.studiobuildpanels.compiler.2', 'Compiler')} +
{validationResult ? validationResult.success - ? t("pages.studio.studiobuildpanels.validation.completed.without.blocking.errors.2", "Validation completed without blocking errors.") - : t("pages.studio.studiobuildpanels.validation.returned.blocking.diagnostics.2", "Validation returned blocking diagnostics.") - : t("pages.studio.studiobuildpanels.run.validate.to.refresh.compiler.diagnostics", "Run Validate to refresh compiler diagnostics.")} + ? t( + 'pages.studio.studiobuildpanels.validation.completed.without.blocking.errors.2', + 'Validation completed without blocking errors.', + ) + : t( + 'pages.studio.studiobuildpanels.validation.returned.blocking.diagnostics.2', + 'Validation returned blocking diagnostics.', + ) + : t( + 'pages.studio.studiobuildpanels.run.validate.to.refresh.compiler.diagnostics', + 'Run Validate to refresh compiler diagnostics.', + )}
{validationResult?.diagnostics?.length ? ( - {t("pages.studio.studiobuildpanels.problems.2", "Problems")}{validationResult.diagnostics.length} + {t( + 'pages.studio.studiobuildpanels.problems.2', + 'Problems', + )} + {validationResult.diagnostics.length} ) : ( - {t("pages.studio.studiobuildpanels.clean.2", "Clean")} + + {t('pages.studio.studiobuildpanels.clean.2', 'Clean')} + )}
{validationResult?.diagnostics?.length ? ( -
= ({ padding: 12, }} > -
{t("pages.studio.studiobuildpanels.diagnostics.2", "Diagnostics")}
+
+ {t( + 'pages.studio.studiobuildpanels.diagnostics.2', + 'Diagnostics', + )} +
{validationResult.diagnostics.map((diagnostic, index) => { const diagnosticKey = `${diagnostic.filePath || 'source'}:${diagnostic.startLine || 0}:${diagnostic.startColumn || 0}:${diagnostic.code || index}`; const severityColor = @@ -3083,48 +3631,88 @@ export const StudioScriptBuildPanel: React.FC = ({ textAlign: 'left', }} > - - + + {diagnostic.severity} {formatScriptDiagnosticLocation(diagnostic)} {diagnostic.code ? ( - + {diagnostic.code} ) : null} - + {diagnostic.message} ); })} -
+
) : null}
) : ( + {t('pages.studio.studiobuildpanels.add.script.2', 'Add script')} + )}
{hasActiveScript ? ( -
+
{scriptReadyToBind - ? t("pages.studio.studiobuildpanels.script.revision.is.catalog.applied.continue", "Script revision is catalog-applied. Continue to Bind to publish the callable member contract.") - : t("pages.studio.studiobuildpanels.script.build.keeps.code.editing.here", "Script Build keeps code editing here. {value1}.", { value1: bindReadinessLabel })} + ? t( + 'pages.studio.studiobuildpanels.script.revision.is.catalog.applied.continue', + 'Script revision is catalog-applied. Continue to Bind to publish the callable member contract.', + ) + : t( + 'pages.studio.studiobuildpanels.script.build.keeps.code.editing.here', + 'Script Build keeps code editing here. {value1}.', + { value1: bindReadinessLabel }, + )} + {t( + 'pages.studio.studiobuildpanels.continue.to.bind.5', + 'Continue to Bind', + )} +
) : null} @@ -3145,83 +3737,136 @@ export const StudioScriptBuildPanel: React.FC = ({ {hasActiveScript ? ( +
@@ -799,24 +935,32 @@ +
+ + -
+
+ {refreshing ? ( + + ) : null}
({ + workflowScheduleApi: { + create: jest.fn(), + delete: jest.fn(), + disable: jest.fn(), + enable: jest.fn(), + get: jest.fn(), + list: jest.fn(), + preview: jest.fn(), + runNow: jest.fn(), + update: jest.fn(), + }, +})); type SerializableWorkflowDocument = { readonly name?: string; @@ -506,6 +521,11 @@ const mockWorkflowActivityApi = jest.requireMock( ).workflowActivityApi as { getRun: jest.Mock; }; +const mockWorkflowScheduleApi = jest.requireMock( + '@/shared/api/workflowScheduleApi', +).workflowScheduleApi as { + list: jest.Mock; +}; const mockObserveUserLlmSave = jest.requireMock( '@/pages/settings/userLlmSaveObservation', ).observeUserLlmSave as jest.Mock; @@ -518,6 +538,11 @@ describe('Workflow Activity vNext catalogue', () => { mockScopesApi.queryWorkflowCatalogue.mockResolvedValue( createCatalogueResponse([]), ); + mockWorkflowScheduleApi.list.mockResolvedValue({ + items: [], + nextCursor: null, + totalCount: 0, + }); mockScopesApi.archiveWorkflow.mockResolvedValue({ scopeId: 'scope-alpha', workflowId: 'wf-alpha', @@ -593,6 +618,82 @@ describe('Workflow Activity vNext catalogue', () => { ).toBeInTheDocument(); }); + it('reserves a stable action column for published workflow schedules', async () => { + mockScopesApi.queryWorkflowCatalogue.mockResolvedValue( + createCatalogueResponse([ + createCatalogueRow({ + workflowId: 'wf-published', + name: 'Published workflow', + committed: { + serviceKey: 'svc-published', + workflowName: 'published_workflow', + actorId: 'actor-published', + activeRevisionId: 'revision-published', + deploymentId: 'deployment-published', + deploymentStatus: 'Running', + }, + }), + ]), + ); + + renderWithQueryClient(); + + const table = await screen.findByRole('table'); + const columns = table.querySelectorAll('col'); + expect(table).toHaveClass('wa-vnext__table--workflow-catalogue'); + expect(columns).toHaveLength(4); + expect(columns[3]).toHaveStyle({ width: '500px' }); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__table--workflow-catalogue { min-width: 1160px; }', + ); + }); + + it('opens the published Workflow Schedule action as a management modal', async () => { + mockScopesApi.queryWorkflowCatalogue.mockResolvedValue( + createCatalogueResponse([ + createCatalogueRow({ + workflowId: 'wf-published', + name: 'Published workflow', + committed: { + serviceKey: 'svc-published', + workflowName: 'published_workflow', + actorId: 'actor-published', + activeRevisionId: 'revision-published', + deploymentId: 'deployment-published', + deploymentStatus: 'Running', + }, + }), + ]), + ); + + renderWithQueryClient(); + + fireEvent.click( + await screen.findByRole('button', { + name: 'Manage schedules for Published workflow', + }), + ); + + await waitFor(() => + expect( + screen.getByText('Schedules for Published workflow', { + selector: '.ant-modal-title', + }), + ).toBeVisible(), + ); + expect(screen.getByText('No schedules yet')).toBeVisible(); + expect(screen.getByRole('button', { name: 'New schedule' })).toBeVisible(); + expect( + screen.queryByText('Recurring runs for Published workflow'), + ).not.toBeInTheDocument(); + expect(screen.queryByText('WORKFLOW SCHEDULE')).not.toBeInTheDocument(); + expect(screen.queryByText('What will happen')).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Review schedule' }), + ).not.toBeInTheDocument(); + expect(screen.getAllByRole('dialog')).toHaveLength(1); + }); + it('refreshes the workflow catalogue when returning from the editor', async () => { const queryClient = new QueryClient({ defaultOptions: { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts index 8933933a58..bf8b7247ca 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts @@ -110,8 +110,10 @@ export const workflowActivityVNextCss = ` .wa-vnext__header h1 { font-size: 28px; font-weight: 700; line-height: 28px; margin: 0; text-wrap: balance; } .wa-vnext__header p { color: var(--wa-muted); font-size: 12px; line-height: 17px; margin: 7px 0 0; max-width: 760px; text-wrap: pretty; } .wa-vnext__header-actions { align-items: center; display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; justify-content: flex-end; } +.wa-vnext__run-detail-refresh { min-width: 132px; } .wa-vnext__content { min-width: 0; padding: 18px 40px 48px; } .wa-vnext__content--run-detail { display: flex; min-height: 0; overflow: hidden; padding-bottom: 40px; } +.wa-vnext__activity-filter-context { margin-bottom: 12px; } .wa-vnext__toolbar { align-items: center; display: flex; flex-wrap: wrap; gap: 10px; justify-content: space-between; margin-bottom: 12px; min-height: 32px; } .wa-vnext__toolbar-search { flex: 0 1 320px; max-width: 100%; width: 320px; } .wa-vnext__toolbar-filters { justify-content: flex-end; } @@ -134,6 +136,7 @@ export const workflowActivityVNextCss = ` height: clamp(420px, calc(100dvh - 300px), 720px); } .wa-vnext__table { border-collapse: separate; border-spacing: 0; font-size: 12px; line-height: 17px; min-width: 900px; table-layout: fixed; width: 100%; } +.wa-vnext__table--workflow-catalogue { min-width: 1160px; } .wa-vnext__table th { background: var(--wa-subtle); border-bottom: 1px solid var(--wa-line); @@ -402,8 +405,10 @@ export const workflowActivityVNextCss = ` max-height: 100%; min-height: 0; overflow: hidden; + position: relative; width: 100%; } +.wa-vnext-run-detail__refresh-content { display: contents; } .wa-vnext-run-detail__rail { background: var(--wa-surface); border-right: 1px solid var(--wa-line); @@ -649,6 +654,103 @@ export const workflowActivityVNextCss = ` .wa-vnext-run-detail .ant-tag { margin-inline-end: 0; } +@keyframes wa-vnext-run-detail-skeleton-pulse { + 0%, 100% { opacity: .55; } + 50% { opacity: 1; } +} +.wa-vnext-run-detail--loading { + cursor: progress; +} +.wa-vnext-run-detail__stage--loading { + cursor: progress; +} +.wa-vnext-run-detail--loading .wa-vnext-run-detail__rail-header, +.wa-vnext-run-detail__stage--loading .wa-vnext-run-detail__stage-header, +.wa-vnext-run-detail__stage--loading .wa-vnext-run-detail__logs-header, +.wa-vnext-run-detail__stage--loading .wa-vnext-run-detail__inspector-header { + min-height: 54px; +} +.wa-vnext-run-detail__skeleton-line, +.wa-vnext-run-detail__skeleton-dot, +.wa-vnext-run-detail__skeleton-node { + animation: wa-vnext-run-detail-skeleton-pulse 1.5s ease-in-out infinite; +} +.wa-vnext-run-detail__skeleton-line { + background: color-mix(in srgb, var(--wa-line) 64%, var(--wa-subtle)); + border-radius: 4px; + display: block; + height: 10px; + max-width: 100%; +} +.wa-vnext-run-detail__skeleton-line--title { height: 14px; width: 112px; } +.wa-vnext-run-detail__skeleton-line--short { width: 72px; } +.wa-vnext-run-detail__skeleton-line--run { width: 76%; } +.wa-vnext-run-detail__skeleton-line--meta { height: 9px; width: 48%; } +.wa-vnext-run-detail__skeleton-line--heading { height: 16px; width: 148px; } +.wa-vnext-run-detail__skeleton-line--pill { border-radius: 10px; height: 20px; width: 64px; } +.wa-vnext-run-detail__skeleton-line--subtitle { width: 196px; } +.wa-vnext-run-detail__skeleton-line--node-title { height: 12px; width: 62%; } +.wa-vnext-run-detail__skeleton-line--node-meta { height: 9px; width: 42%; } +.wa-vnext-run-detail__skeleton-line--label { height: 12px; width: 44px; } +.wa-vnext-run-detail__skeleton-line--duration { height: 9px; width: 42px; } +.wa-vnext-run-detail__skeleton-line--step { width: 68%; } +.wa-vnext-run-detail__skeleton-line--step-meta { height: 8px; width: 44%; } +.wa-vnext-run-detail__skeleton-line--inspector-title { height: 12px; width: 132px; } +.wa-vnext-run-detail__skeleton-line--tabs { height: 12px; margin-bottom: 10px; width: 180px; } +.wa-vnext-run-detail__skeleton-line--content { width: 82%; } +.wa-vnext-run-detail__skeleton-line--content-short { width: 58%; } +.wa-vnext-run-detail__skeleton-heading { + align-items: center; + display: flex; + gap: 10px; +} +.wa-vnext-run-detail__run--loading { + cursor: progress; + min-height: 53px; +} +.wa-vnext-run-detail__graph--loading { + overflow: hidden; + position: relative; +} +.wa-vnext-run-detail__skeleton-node { + background: var(--wa-surface); + border: 1px solid var(--wa-line); + border-radius: 6px; + display: grid; + gap: 9px; + left: 50%; + padding: 14px; + position: absolute; + transform: translateX(-50%); + width: clamp(160px, 28%, 220px); + z-index: 1; +} +.wa-vnext-run-detail__skeleton-node--1 { top: 10%; } +.wa-vnext-run-detail__skeleton-node--2 { top: 42%; } +.wa-vnext-run-detail__skeleton-node--3 { top: 74%; } +.wa-vnext-run-detail__skeleton-connector { + background: var(--wa-line); + height: 18%; + left: 50%; + position: absolute; + top: 27%; + width: 1px; +} +.wa-vnext-run-detail__skeleton-connector--second { top: 59%; } +.wa-vnext-run-detail__step--loading { cursor: progress; } +.wa-vnext-run-detail__skeleton-dot { + background: color-mix(in srgb, var(--wa-line) 72%, var(--wa-subtle)); + border-radius: 50%; + height: 12px; + justify-self: center; + width: 12px; +} +.wa-vnext-run-detail__skeleton-step-copy { + display: grid; + gap: 6px; + min-width: 0; +} +.wa-vnext-run-detail__inspector-body--loading { gap: 10px; } .wa-vnext__node-inspector { background: var(--wa-surface); border: 1px solid var(--wa-line); border-radius: var(--wa-radius); bottom: 16px; box-shadow: 0 16px 36px rgba(16, 24, 40, .16); display: flex; flex-direction: column; max-width: calc(100% - 32px); min-height: 0; overflow: hidden; position: absolute; right: 16px; top: 16px; width: min(400px, calc(100% - 32px)); z-index: 20; } .wa-vnext__node-inspector-header { align-items: flex-start; border-bottom: 1px solid var(--wa-line); display: flex; gap: 12px; justify-content: space-between; padding: 16px 16px 14px; } .wa-vnext__node-inspector-title.ant-typography { font-size: 15px; line-height: 1.35; margin: 0; } @@ -672,6 +774,147 @@ export const workflowActivityVNextCss = ` .wa-vnext__node-inspector-advanced { display: grid; gap: 10px; } .wa-vnext__node-inspector-advanced .ant-input { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; } .wa-vnext__node-inspector-actions { align-items: center; border-top: 1px solid var(--wa-line); display: flex; gap: 8px; justify-content: flex-end; padding: 12px 16px max(12px, env(safe-area-inset-bottom)); } +.wa-vnext-schedule-modal, .wa-vnext-schedule-drawer { + --wa-ink: #17202a; + --wa-muted: #667085; + --wa-line: #d0d5dd; + --wa-surface: #ffffff; + --wa-subtle: #f8fafc; + --wa-blue: #175cd3; + --wa-blue-bg: #eff8ff; + --wa-red: #b42318; + --wa-red-bg: #fef3f2; + --wa-radius: 8px; + color: var(--wa-ink); + font-family: AlibabaSans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + font-size: 12px; +} +.wa-vnext-schedule-modal .ant-modal-content { border-radius: var(--wa-radius); } +.wa-vnext-schedule-modal .ant-btn, .wa-vnext-schedule-drawer .ant-btn { font-size: 12px; } +.wa-vnext__schedule-surface-title { color: var(--wa-ink); font-size: 16px; font-weight: 650; line-height: 22px; margin: 0; } +.wa-vnext__schedule-form-title { color: var(--wa-ink); font-size: 15px; line-height: 21px; margin: 0; } +.wa-vnext__schedule-surface { display: grid; gap: 16px; min-width: 0; position: relative; } +.wa-vnext__schedule-toolbar { align-items: flex-start; display: flex; gap: 16px; justify-content: space-between; } +.wa-vnext__schedule-toolbar--actions { justify-content: flex-end; } +.wa-vnext__schedule-refresh-overlay { grid-column: 1; grid-row: 2; } +.wa-vnext__schedule-failure { max-width: 100%; min-width: 0; width: 100%; } +.wa-vnext__schedule-failure .ant-alert-section, .wa-vnext__schedule-failure .ant-alert-content, .wa-vnext__schedule-failure .ant-alert-description { max-width: 100%; min-width: 0; } +.wa-vnext__schedule-failure-copy { display: grid; gap: 10px; max-width: 100%; min-width: 0; } +.wa-vnext__schedule-failure-copy > p { margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-failure-copy .wa-vnext__technical-details { margin-top: 0; min-width: 0; } +.wa-vnext__schedule-failure-copy .wa-vnext__technical-details-body { max-height: 132px; overflow: auto; white-space: pre-wrap; } +.wa-vnext__schedule-list { display: grid; gap: 10px; } +.wa-vnext__schedule-row { align-items: center; appearance: none; background: var(--wa-subtle); border: 1px solid var(--wa-line); border-radius: var(--wa-radius); color: var(--wa-ink); cursor: pointer; display: flex; font: inherit; gap: 16px; justify-content: space-between; min-width: 0; padding: 14px; text-align: left; transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease; width: 100%; } +.wa-vnext__schedule-row:hover { background: #f2f4f7; border-color: #98a2b3; box-shadow: 0 2px 8px rgba(16, 24, 40, .06); } +.wa-vnext__schedule-row:focus-visible { outline: 3px solid rgba(23, 92, 211, .25); outline-offset: 2px; } +.wa-vnext__schedule-row-main { display: grid; gap: 5px; min-width: 0; } +.wa-vnext__schedule-row-heading { align-items: center; display: flex; gap: 8px; min-width: 0; } +.wa-vnext__schedule-row-heading strong { overflow-wrap: anywhere; } +.wa-vnext__schedule-row-main code { color: var(--wa-ink); font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; } +.wa-vnext__schedule-row-main > span { color: var(--wa-muted); font-size: 11px; overflow-wrap: anywhere; } +.wa-vnext__schedule-row-arrow { color: var(--wa-muted); flex: 0 0 auto; font-size: 22px; line-height: 1; } +.wa-vnext__schedule-selected-title { align-items: center; display: flex; gap: 8px; min-width: 0; } +.wa-vnext__schedule-selected-title > .ant-btn { flex: 0 0 auto; margin-inline-start: -8px; } +.wa-vnext__schedule-selected-heading { align-items: baseline; display: flex; font-weight: inherit; margin: 0; min-width: 0; overflow: hidden; white-space: nowrap; } +.wa-vnext__schedule-selected-title strong { color: var(--wa-ink); font-size: 16px; line-height: 21px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.wa-vnext__schedule-selected-title span { color: var(--wa-muted); font-size: 11px; line-height: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.wa-vnext__schedule-selected-heading strong, .wa-vnext__schedule-selected-heading .wa-vnext__schedule-selected-heading-context { flex: 0 1 auto; min-width: 0; } +.wa-vnext__schedule-selected-heading > span[aria-hidden="true"] { flex: 0 0 auto; font-size: 13px; line-height: 21px; } +.wa-vnext__schedule-selected-heading .wa-vnext__schedule-selected-heading-context { font-size: 13px; line-height: 21px; } +.wa-vnext__schedule-detail { display: grid; gap: 18px; min-width: 0; position: relative; } +.wa-vnext__schedule-detail > .ant-tabs { margin-bottom: -2px; } +.wa-vnext__schedule-overview { display: grid; gap: 14px; min-width: 0; } +.wa-vnext__schedule-overview-summary { background: var(--wa-subtle); border: 1px solid var(--wa-line); border-radius: var(--wa-radius); display: grid; gap: 8px; padding: 16px; } +.wa-vnext__schedule-overview-summary .ant-tag { justify-self: start; margin-inline-end: 0; } +.wa-vnext__schedule-overview-summary h2 { color: var(--wa-ink); font-size: 18px; line-height: 24px; margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-overview-summary p { color: var(--wa-muted); font-size: 12px; line-height: 17px; margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-overview-summary p span { font-weight: 650; } +.wa-vnext__schedule-overview-actions { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; } +.wa-vnext__schedule-detail-facts { background: var(--wa-surface); border: 1px solid var(--wa-line); border-radius: var(--wa-radius); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; overflow: hidden; } +.wa-vnext__schedule-detail-facts div { display: grid; gap: 4px; min-width: 0; padding: 12px 14px; } +.wa-vnext__schedule-detail-facts div:nth-child(odd) { border-right: 1px solid var(--wa-line); } +.wa-vnext__schedule-detail-facts div:nth-child(n + 3) { border-top: 1px solid var(--wa-line); } +.wa-vnext__schedule-detail-facts dt { color: var(--wa-muted); font-size: 11px; font-weight: 650; } +.wa-vnext__schedule-detail-facts dd { color: var(--wa-ink); font-size: 12px; margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-detail-facts code { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; } +.wa-vnext__schedule-run-input { display: grid; gap: 6px; } +.wa-vnext__schedule-run-input h3 { color: var(--wa-ink); font-size: 12px; line-height: 17px; margin: 0; } +.wa-vnext__schedule-run-input p { background: var(--wa-subtle); border: 1px solid var(--wa-line); border-radius: 6px; color: var(--wa-ink); line-height: 18px; margin: 0; max-height: 120px; overflow: auto; padding: 10px 12px; white-space: pre-wrap; } +.wa-vnext__schedule-advanced-details { color: var(--wa-muted); padding: 2px; } +.wa-vnext__schedule-advanced-details summary { cursor: pointer; font-size: 11px; font-weight: 650; } +.wa-vnext__schedule-advanced-details dl { display: grid; gap: 8px; margin: 10px 0 0; } +.wa-vnext__schedule-advanced-details div { align-items: baseline; display: grid; gap: 10px; grid-template-columns: max-content minmax(0, 1fr); } +.wa-vnext__schedule-advanced-details dt { font-size: 11px; } +.wa-vnext__schedule-advanced-details dd { color: var(--wa-ink); margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-advanced-details code { background: var(--wa-subtle); border-radius: 4px; font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; padding: 2px 5px; } +.wa-vnext__schedule-history { display: grid; gap: 14px; min-width: 0; } +.wa-vnext__schedule-history-header { align-items: flex-start; display: flex; gap: 16px; justify-content: space-between; } +.wa-vnext__schedule-history-header h2 { color: var(--wa-ink); font-size: 15px; line-height: 21px; margin: 0; } +.wa-vnext__schedule-history-header p { color: var(--wa-muted); line-height: 17px; margin: 4px 0 0; max-width: 520px; } +.wa-vnext__schedule-history-header a { flex: 0 0 auto; font-size: 12px; line-height: 17px; } +.wa-vnext__schedule-history-table-wrap { border: 1px solid var(--wa-line); border-radius: var(--wa-radius); max-height: min(440px, calc(100dvh - 320px)); max-width: 100%; overflow: auto; scrollbar-gutter: stable; } +.wa-vnext__schedule-history-table { border-collapse: separate; border-spacing: 0; font-size: 12px; min-width: 720px; table-layout: fixed; width: 100%; } +.wa-vnext__schedule-history-table th { background: var(--wa-subtle); border-bottom: 1px solid var(--wa-line); color: var(--wa-muted); font-size: 10px; font-weight: 650; letter-spacing: 1px; padding: 9px 12px; position: sticky; text-align: left; text-transform: uppercase; top: 0; z-index: 1; } +.wa-vnext__schedule-history-table td { border-bottom: 1px solid var(--wa-line); color: var(--wa-ink); line-height: 17px; padding: 11px 12px; vertical-align: top; } +.wa-vnext__schedule-history-table tbody tr:last-child td { border-bottom: 0; } +.wa-vnext__schedule-history-table tbody tr { position: relative; } +.wa-vnext__schedule-history-row--linked { cursor: pointer; } +.wa-vnext__schedule-history-row--linked td { transition: background-color .15s ease; } +.wa-vnext__schedule-history-row--linked:hover td { background: var(--wa-blue-bg); } +.wa-vnext__schedule-history-row--linked:focus-within td { background: var(--wa-blue-bg); } +.wa-vnext__schedule-history-row-link { border-radius: var(--wa-radius); inset: 0; position: absolute; z-index: 1; } +.wa-vnext__schedule-history-row-link:focus-visible { outline: 3px solid rgba(23, 92, 211, .3); outline-offset: -3px; } +.wa-vnext__schedule-history-row--linked details { position: relative; z-index: 2; } +.wa-vnext__schedule-history-table th:nth-child(1), .wa-vnext__schedule-history-table td:nth-child(1) { width: 28%; } +.wa-vnext__schedule-history-table th:nth-child(2), .wa-vnext__schedule-history-table td:nth-child(2) { width: 14%; } +.wa-vnext__schedule-history-table th:nth-child(3), .wa-vnext__schedule-history-table td:nth-child(3) { width: 28%; } +.wa-vnext__schedule-history-table th:nth-child(4), .wa-vnext__schedule-history-table td:nth-child(4) { width: 30%; } +.wa-vnext__schedule-history-result { display: grid; gap: 6px; min-width: 0; } +.wa-vnext__schedule-history-result .ant-tag { justify-self: start; margin-inline-end: 0; } +.wa-vnext__schedule-history-failure { color: var(--wa-red); font-size: 11px; margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-history-result details { color: var(--wa-muted); } +.wa-vnext__schedule-history-result summary { cursor: pointer; font-size: 11px; font-weight: 650; } +.wa-vnext__schedule-history-result code { background: var(--wa-red-bg); border-radius: 4px; color: #7a271a; display: block; font: 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace; margin-top: 6px; max-height: 96px; overflow: auto; overflow-wrap: anywhere; padding: 7px 8px; white-space: pre-wrap; } +.wa-vnext__schedule-history-completed { white-space: nowrap; } +.wa-vnext__schedule-empty--history { padding: 14px; } +.wa-vnext__schedule-empty { background: var(--wa-subtle); border: 1px solid var(--wa-line); border-radius: var(--wa-radius); padding: 20px 22px; } +.wa-vnext__schedule-empty-title { font-size: 15px; line-height: 22px; margin: 0 0 6px; } +.wa-vnext__schedule-empty p { color: var(--wa-muted); margin: 0; max-width: 560px; } +.wa-vnext__schedule-form { display: grid; gap: 14px; } +.wa-vnext__schedule-enabled { align-items: center; display: flex; gap: 10px; justify-content: space-between; } +.wa-vnext__schedule-preview-list { margin: 0; padding-left: 18px; } +.wa-vnext__schedule-preview-list li { margin: 3px 0; } +.wa-vnext__schedule-create, .wa-vnext__schedule-review, .wa-vnext__schedule-accepted { display: grid; gap: 18px; min-width: 0; } +.wa-vnext__schedule-context { align-items: center; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 6px; display: flex; gap: 10px; justify-content: space-between; padding: 12px 14px; } +.wa-vnext__schedule-context strong { color: #172554; font-size: 13px; overflow-wrap: anywhere; text-align: right; } +.wa-vnext__schedule-section { border-bottom: 1px solid var(--wa-line); display: grid; gap: 12px; padding-bottom: 18px; } +.wa-vnext__schedule-section:last-of-type { border-bottom: 0; padding-bottom: 0; } +.wa-vnext__schedule-section h3 { color: var(--wa-ink); font-size: 13px; line-height: 18px; margin: 0; } +.wa-vnext__schedule-repeat-grid { display: grid; gap: 12px; grid-template-columns: minmax(0, 1fr) minmax(120px, .7fr) minmax(0, 1fr); } +.wa-vnext__schedule-cron-grid { display: grid; gap: 12px; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); } +.wa-vnext__schedule-repeat-grid .ant-select { width: 100%; } +.wa-vnext__schedule-repeat-detail { grid-column: 1 / -1; max-width: 260px; } +.wa-vnext__schedule-cron-toggle { justify-self: start; padding-inline: 0; } +.wa-vnext__schedule-previewing { color: #166534; font-size: 12px; font-weight: 650; } +.wa-vnext__schedule-footer { align-items: center; border-top: 1px solid var(--wa-line); display: flex; gap: 8px; justify-content: flex-end; padding-top: 14px; } +.wa-vnext__schedule-review-panel { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 6px; display: grid; gap: 16px; padding: 16px; } +.wa-vnext__schedule-review-panel h2, .wa-vnext__schedule-accepted-panel h2 { color: var(--wa-ink); font-size: 15px; line-height: 21px; margin: 0; } +.wa-vnext__schedule-review-details { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; } +.wa-vnext__schedule-review-details div { display: grid; gap: 3px; min-width: 0; } +.wa-vnext__schedule-review-details dt { color: var(--wa-muted); font-size: 11px; font-weight: 650; } +.wa-vnext__schedule-review-details dd { color: var(--wa-ink); font-size: 12px; margin: 0; overflow-wrap: anywhere; } +.wa-vnext__schedule-fire-preview { border-top: 1px solid #bbf7d0; display: grid; gap: 8px; padding-top: 14px; } +.wa-vnext__schedule-fire-preview strong { color: #166534; font-size: 12px; } +.wa-vnext__schedule-accepted-panel { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 6px; display: grid; gap: 8px; padding: 18px; } +.wa-vnext__schedule-accepted-panel p { color: #166534; font-size: 13px; font-weight: 650; margin: 0; } +.wa-vnext__schedule-accepted-panel span { color: #475467; font-size: 12px; line-height: 18px; } +.wa-vnext-schedule-drawer .ant-drawer-content { background: var(--wa-surface); } +.wa-vnext-schedule-drawer .ant-drawer-header { border-bottom-color: var(--wa-line); } +.wa-vnext-schedule-drawer .ant-drawer-title, .wa-vnext-schedule-drawer .ant-drawer-close { color: var(--wa-ink); } +.wa-vnext-schedule-drawer .ant-drawer-body { padding: 20px; } +.wa-vnext-schedule-modal .ant-modal-close { color: var(--wa-muted); } +.wa-vnext-schedule-modal .ant-modal-close:hover { background: var(--wa-subtle); color: var(--wa-ink); } +.wa-vnext-schedule-modal .ant-modal-close:focus-visible { outline: 2px solid color-mix(in srgb, var(--wa-blue) 45%, transparent); outline-offset: 1px; } .wa-vnext button:focus-visible, .wa-vnext a:focus-visible, .wa-vnext input:focus-visible, .wa-vnext textarea:focus-visible, .wa-vnext select:focus-visible, .wa-vnext__table-wrap:focus-visible { outline: 3px solid rgba(23, 92, 211, .25); outline-offset: 2px; } .wa-vnext-drawer .ant-drawer-content { background: var(--wa-sidebar, #101828); color: #fff; } .wa-vnext-drawer .ant-drawer-header { border-bottom-color: #344054; min-height: 52px; padding: 0 16px; } @@ -752,16 +995,35 @@ export const workflowActivityVNextCss = ` .wa-vnext__account-profile, .wa-vnext__account-section-heading { align-items: stretch; flex-direction: column; } .wa-vnext__account-profile > .ant-btn, .wa-vnext__account-section-heading > .ant-btn { width: 100%; } .wa-vnext__state--compact { padding: 18px; } + .wa-vnext__schedule-toolbar, .wa-vnext__schedule-row { align-items: stretch; flex-direction: column; } + .wa-vnext__schedule-toolbar--actions { align-items: flex-end; flex-direction: row; } + .wa-vnext__schedule-toolbar .ant-space, .wa-vnext__schedule-row .ant-space { width: 100%; } + .wa-vnext__schedule-toolbar--actions .ant-space { width: auto; } + .wa-vnext__schedule-toolbar .ant-btn, .wa-vnext__schedule-row .ant-btn { flex: 1 1 auto; } .wa-vnext__template-toolbar { align-items: stretch; flex-direction: column; } .wa-vnext__template-search { flex-basis: auto; max-width: none; width: 100%; } .wa-vnext__template-sort { align-items: stretch; display: grid; gap: 6px; width: 100%; } .wa-vnext__template-sort .ant-select { min-width: 0; width: 100%; } .wa-vnext__publish-review-item { gap: 6px; padding-block: 10px; } } +@media (max-width: 767px) { + .wa-vnext-schedule-modal .ant-modal-content { padding: 18px; } + .wa-vnext__schedule-failure { align-items: flex-start; display: grid; grid-template-columns: auto minmax(0, 1fr); } + .wa-vnext__schedule-failure .ant-alert-icon { grid-column: 1; grid-row: 1; } + .wa-vnext__schedule-failure .ant-alert-section, .wa-vnext__schedule-failure .ant-alert-content { grid-column: 2; grid-row: 1; } + .wa-vnext__schedule-failure .ant-alert-action, .wa-vnext__schedule-failure .ant-alert-actions { grid-column: 2; margin-inline-start: 0; margin-top: 10px; } + .wa-vnext__schedule-repeat-grid, .wa-vnext__schedule-cron-grid, .wa-vnext__schedule-review-details, .wa-vnext__schedule-detail-facts { grid-template-columns: 1fr; } + .wa-vnext__schedule-context { align-items: flex-start; flex-direction: column; } + .wa-vnext__schedule-context strong { text-align: left; } + .wa-vnext__schedule-history-header { flex-direction: column; } + .wa-vnext__schedule-history-header a { align-self: flex-start; } + .wa-vnext__schedule-footer { align-items: stretch; flex-direction: column-reverse; } + .wa-vnext__schedule-footer .ant-btn { width: 100%; } +} @media (max-width: 360px) { .wa-vnext__settings-actions.ant-space { grid-template-columns: 1fr; } .wa-vnext__state--compact .ant-alert { align-items: flex-start; } .wa-vnext__state--compact .ant-alert-action { margin-inline-start: 0; margin-top: 8px; } } -@media (prefers-reduced-motion: reduce) { .wa-vnext *, .wa-vnext *::before, .wa-vnext *::after { scroll-behavior: auto !important; transition: none !important; } } +@media (prefers-reduced-motion: reduce) { .wa-vnext *, .wa-vnext *::before, .wa-vnext *::after { animation: none !important; scroll-behavior: auto !important; transition: none !important; } } `; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx index e3caf1f8b6..bdc5018358 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx @@ -433,6 +433,51 @@ describe('New workflow save-target recovery', () => { ); }); + it('treats a draft-create timeout as unconfirmed without resubmitting or guessing an identity', async () => { + const importedYaml = 'name: imported_review\nroles: []\nsteps: []\n'; + mockStudioApi.getWorkspaceSettings.mockResolvedValue(readyWorkspace); + mockStudioApi.listWorkflowDrafts + .mockResolvedValueOnce([]) + .mockResolvedValueOnce([ + { + directoryId: 'directory-alpha', + fileName: 'imported-review.yaml', + name: 'imported_review', + workflowId: 'wf-created-after-timeout', + }, + ]); + mockStudioApi.parseYaml.mockResolvedValue({ + document: { name: 'imported_review', roles: [], steps: [] }, + findings: [], + }); + mockStudioApi.createWorkflowDraft.mockRejectedValue({ + message: 'POST workflow-drafts returned 504', + status: 504, + }); + + renderWithQueryClient(); + + fireEvent.click(await screen.findByRole('button', { name: 'Import YAML' })); + const yamlInput = screen.getByLabelText('Workflow YAML'); + fireEvent.change(yamlInput, { target: { value: importedYaml } }); + fireEvent.click(screen.getByRole('button', { name: 'Import and open' })); + + expect( + await screen.findByText( + "Workflow creation couldn't be confirmed. Check Workflows before trying again.", + ), + ).toBeVisible(); + expect( + screen.queryByText("Workflow couldn't be created"), + ).not.toBeInTheDocument(); + expect(yamlInput).toHaveValue(importedYaml); + expect(mockStudioApi.createWorkflowDraft).toHaveBeenCalledTimes(1); + await waitFor(() => + expect(mockStudioApi.listWorkflowDrafts).toHaveBeenCalledTimes(2), + ); + expect(history.push).not.toHaveBeenCalled(); + }); + it('navigates to the template browser from the creation chooser', async () => { mockStudioApi.getWorkspaceSettings.mockResolvedValue(readyWorkspace); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx index 0faecfa314..778894c0ab 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx @@ -35,6 +35,17 @@ function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } +function isWorkflowCreateResultUnknown(error: unknown): boolean { + return ( + isStudioApiStatus(error, 408) || + isStudioApiStatus(error, 504) || + error instanceof TypeError || + (typeof DOMException !== 'undefined' && + error instanceof DOMException && + error.name === 'AbortError') + ); +} + const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ scopeId, }) => { @@ -151,8 +162,9 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ ); return; } - await finishSave( - await studioApi.createWorkflowDraft({ + let result: StudioWorkflowSaveResult; + try { + result = await studioApi.createWorkflowDraft({ directoryId, fileName: resolveAvailableWorkflowFileName( workflowName, @@ -162,8 +174,19 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ scopeId, workflowName, yaml: nextYaml, - }), - ); + }); + } catch (error) { + if (!isWorkflowCreateResultUnknown(error)) throw error; + toast.warning( + t( + 'workflowActivityVNext.new.createUnconfirmed', + "Workflow creation couldn't be confirmed. Check Workflows before trying again.", + ), + ); + void existingWorkflows.refetch(); + return; + } + await finishSave(result); }; const createBlank = async () => { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx index ef6cac020a..d333a64aeb 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx @@ -1,5 +1,6 @@ import { ArrowLeftOutlined, + CalendarOutlined, CodeOutlined, NodeIndexOutlined, PlayCircleOutlined, @@ -50,6 +51,7 @@ import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import WorkflowNodeInspector, { type WorkflowNodeInspectorHandle, } from './WorkflowNodeInspector'; +import WorkflowScheduleSurface from './WorkflowScheduleSurface'; const PUBLISHED_RUN_CONSOLE_ID = 'workflow-published-run-console'; @@ -142,6 +144,7 @@ const WorkflowEditorPage: React.FC<{ const [publishedDocumentVersion, setPublishedDocumentVersion] = React.useState(null); const [runPanelOpen, setRunPanelOpen] = React.useState(false); + const [schedulePanelOpen, setSchedulePanelOpen] = React.useState(false); const [runConsoleVisible, setRunConsoleVisible] = React.useState(false); const [runConsoleExpanded, setRunConsoleExpanded] = React.useState(false); const [activeRunLogIndex, setActiveRunLogIndex] = React.useState< @@ -330,6 +333,7 @@ const WorkflowEditorPage: React.FC<{ setHasUnappliedNodeChanges(false); setNodeLibraryOpen(false); setRunPanelOpen(false); + setSchedulePanelOpen(false); setRunConsoleVisible(false); setRunConsoleExpanded(false); setActiveRunLogIndex(null); @@ -1007,6 +1011,26 @@ const WorkflowEditorPage: React.FC<{ > {t('workflowActivityVNext.common.run', 'Run')} + {!publicationCurrent && (publishReadinessIssues.length > 0 ? (
+ setSchedulePanelOpen(false)} + open={schedulePanelOpen} + scopeId={activeScopeId} + workflowId={activeWorkflowId} + workflowName={editor.workflowTitle} + /> ({ + workflowScheduleApi: { + create: jest.fn(), + delete: jest.fn(), + disable: jest.fn(), + enable: jest.fn(), + get: jest.fn(), + list: jest.fn(), + preview: jest.fn(), + runNow: jest.fn(), + update: jest.fn(), + }, +})); + +const mockedWorkflowScheduleApi = jest.mocked(workflowScheduleApi); +const rawProxyError = + 'Error occurred while trying to proxy: 127.0.0.1:5175/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha'; + +function createScheduleSummary(overrides: Record = {}) { + return { + scheduleId: 'schedule-alpha', + displayName: 'Daily workflow run', + prompt: '', + cronExpression: '0 9 * * 1-5', + timezone: 'Asia/Shanghai', + enabled: true, + createdAt: '2026-08-18T00:00:00Z', + updatedAt: '2026-08-20T01:02:00Z', + nextFireAt: '2026-08-21T01:00:00Z', + lastFireAt: '2026-08-20T01:00:00Z', + fireCount: 12, + failureCount: 1, + ...overrides, + }; +} + +const mockToast = { + error: jest.fn(), + info: jest.fn(), + success: jest.fn(), + warning: jest.fn(), +}; + +jest.mock('@/shared/ui/ConsoleToast', () => ({ + useConsoleToast: () => mockToast, +})); + +function renderSurface( + available: boolean, + mode: 'modal' | 'panel' = 'modal', + onClose = jest.fn(), + initialView?: 'form' | 'list', +) { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { gcTime: 0, retry: false }, + }, + }); + return { + ...render( + + + , + ), + onClose, + }; +} + +function findScheduleOption(label: string): HTMLElement { + const option = Array.from( + document.querySelectorAll('.ant-select-item-option'), + ).find((element) => element.textContent?.trim() === label); + if (!option) throw new Error(`Schedule option not found: ${label}`); + return option; +} + +describe('WorkflowScheduleSurface', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [], + nextCursor: null, + totalCount: 0, + }); + mockedWorkflowScheduleApi.create.mockResolvedValue({ + scheduleId: 'schedule-alpha', + accepted: true, + }); + mockedWorkflowScheduleApi.preview.mockResolvedValue({ + cronExpression: '0 9 * * 1-5', + timezone: 'Asia/Shanghai', + nextFireTimes: [ + '2026-08-21T01:00:00Z', + '2026-08-24T01:00:00Z', + '2026-08-25T01:00:00Z', + '2026-08-26T01:00:00Z', + '2026-08-27T01:00:00Z', + ], + }); + }); + + it('allocates four Schedule history columns with a whole-row link affordance', () => { + const schedulePortalTokens = workflowActivityVNextCss.match( + /\.wa-vnext-schedule-modal, \.wa-vnext-schedule-drawer \{([^}]*)\}/, + )?.[1]; + + expect(schedulePortalTokens).toContain('--wa-blue-bg: #eff8ff;'); + expect(schedulePortalTokens).toContain('--wa-red: #b42318;'); + expect(schedulePortalTokens).toContain('--wa-red-bg: #fef3f2;'); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-table th:nth-child(1), .wa-vnext__schedule-history-table td:nth-child(1) { width: 28%; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-table th:nth-child(2), .wa-vnext__schedule-history-table td:nth-child(2) { width: 14%; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-table th:nth-child(3), .wa-vnext__schedule-history-table td:nth-child(3) { width: 28%; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-table th:nth-child(4), .wa-vnext__schedule-history-table td:nth-child(4) { width: 30%; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-row--linked:hover td { background: var(--wa-blue-bg); }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-history-row-link { border-radius: var(--wa-radius); inset: 0; position: absolute; z-index: 1; }', + ); + }); + + it('keeps Schedule failure content bounded at desktop and narrow widths', () => { + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-failure { max-width: 100%; min-width: 0; width: 100%; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-failure-copy > p { margin: 0; overflow-wrap: anywhere; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-failure-copy .wa-vnext__technical-details-body { max-height: 132px; overflow: auto; white-space: pre-wrap; }', + ); + expect(workflowActivityVNextCss).toContain( + '.wa-vnext__schedule-failure .ant-alert-action, .wa-vnext__schedule-failure .ant-alert-actions { grid-column: 2; margin-inline-start: 0; margin-top: 10px; }', + ); + }); + + it('does not query schedules for an unpublished Workflow', () => { + renderSurface(false); + + expect( + screen.getByText( + 'Schedule is unavailable until this Workflow is published', + ), + ).toBeInTheDocument(); + expect(workflowScheduleApi.list).not.toHaveBeenCalled(); + }); + + it('keeps transport diagnostics secondary when the Schedule list fails', async () => { + mockedWorkflowScheduleApi.list.mockRejectedValue(new Error(rawProxyError)); + + renderSurface(true, 'modal', jest.fn(), 'list'); + + await waitFor(() => + expect(screen.getByText("Schedules couldn't be loaded")).toBeVisible(), + ); + expect( + screen.getByText( + 'The current service is temporarily unavailable, please try again later.', + ), + ).toBeVisible(); + expect(screen.getByText(rawProxyError)).not.toBeVisible(); + expect(screen.getByText('Technical details')).toBeVisible(); + expect( + screen.getByText("Schedules couldn't be loaded").closest('.ant-alert'), + ).toHaveClass('wa-vnext__schedule-failure'); + + fireEvent.click(screen.getByText('Technical details')); + expect(screen.getByText(rawProxyError)).toBeVisible(); + }); + + it('opens modal creation directly with one design-aligned configure surface', async () => { + renderSurface(true); + + await waitFor(() => expect(screen.getByText('New schedule')).toBeVisible()); + expect(screen.getByText('Weekly review · Published')).toBeVisible(); + expect(screen.getByText('How often')).toBeVisible(); + expect(screen.getByText('Run input (optional)')).toBeVisible(); + expect(screen.getByText('Enabled after creation')).toBeVisible(); + expect(screen.queryByText('WORKFLOW SCHEDULE')).not.toBeInTheDocument(); + expect(screen.queryByText('What it needs')).not.toBeInTheDocument(); + expect(screen.queryByText('What will happen')).not.toBeInTheDocument(); + expect( + screen.queryByText('Previewed by the server'), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Review schedule' }), + ).toBeVisible(); + expect(screen.queryByText('Schedules')).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Refresh schedules' }), + ).not.toBeInTheDocument(); + expect(screen.getAllByRole('dialog')).toHaveLength(1); + }); + + it('previews five fire times, closes the modal, and shows a success toast', async () => { + const onClose = jest.fn(); + renderSurface(true, 'modal', onClose); + + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Daily workflow run' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.preview).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ + count: 5, + cronExpression: '0 9 * * 1-5', + timezone: expect.any(String), + }), + ), + ); + await waitFor(() => + expect( + screen.getByText('Review schedule', { selector: '.ant-modal-title' }), + ).toBeVisible(), + ); + expect( + screen.queryByRole('heading', { name: 'Review schedule' }), + ).not.toBeInTheDocument(); + expect(screen.getByText('Daily workflow run')).toBeVisible(); + expect(screen.getByText('Weekdays at 09:00')).toBeVisible(); + expect(screen.getByText('Run input')).toBeVisible(); + expect(screen.getByText('No prompt')).toBeVisible(); + expect(screen.getByText('Enabled after creation')).toBeVisible(); + for (const fireAt of [ + '2026-08-21T01:00:00Z', + '2026-08-24T01:00:00Z', + '2026-08-25T01:00:00Z', + '2026-08-26T01:00:00Z', + '2026-08-27T01:00:00Z', + ]) { + expect( + document.querySelector(`time[datetime="${fireAt}"]`), + ).toBeVisible(); + } + + fireEvent.click(screen.getByRole('button', { name: 'Create schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.create).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ + displayName: 'Daily workflow run', + cronExpression: '0 9 * * 1-5', + enabled: true, + prompt: '', + timezone: expect.any(String), + }), + ), + ); + expect( + mockedWorkflowScheduleApi.create.mock.calls[0][2], + ).not.toHaveProperty('owner'); + expect( + mockedWorkflowScheduleApi.create.mock.calls[0][2], + ).not.toHaveProperty('workflowChatTarget'); + await waitFor(() => + expect( + mockedWorkflowScheduleApi.list.mock.calls.length, + ).toBeGreaterThanOrEqual(2), + ); + expect(mockToast.success).toHaveBeenCalledWith( + 'Schedule request accepted. It will appear in the list shortly.', + ); + expect(onClose).toHaveBeenCalledTimes(1); + expect( + screen.queryByText('Refreshing Workflow schedules'), + ).not.toBeInTheDocument(); + }); + + it('keeps transport diagnostics out of Schedule failure toasts', async () => { + mockedWorkflowScheduleApi.preview.mockRejectedValue( + new Error(rawProxyError), + ); + renderSurface(true); + + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Daily workflow run' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(mockToast.error).toHaveBeenCalledWith( + 'The current service is temporarily unavailable, please try again later.', + ), + ); + expect(mockToast.error).not.toHaveBeenCalledWith(rawProxyError); + }); + + it('returns the editor panel to the list while schedule refresh continues', async () => { + let listCallCount = 0; + let resolveRefresh: (value: { + items: never[]; + nextCursor: null; + totalCount: number; + }) => void = () => undefined; + const deferredRefresh = new Promise<{ + items: never[]; + nextCursor: null; + totalCount: number; + }>((resolve) => { + resolveRefresh = resolve; + }); + mockedWorkflowScheduleApi.list.mockImplementation(() => { + listCallCount += 1; + return listCallCount === 1 + ? Promise.resolve({ items: [], nextCursor: null, totalCount: 0 }) + : deferredRefresh; + }); + const view = renderSurface(true, 'panel'); + await waitFor(() => + expect( + screen.getByRole('heading', { name: 'No schedules yet' }), + ).toBeVisible(), + ); + fireEvent.click(screen.getByRole('button', { name: 'New schedule' })); + + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Deferred refresh schedule' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + await waitFor(() => + expect( + screen.getByText('Review schedule', { selector: '.ant-drawer-title' }), + ).toBeVisible(), + ); + fireEvent.click(screen.getByRole('button', { name: 'Create schedule' })); + await waitFor(() => + expect(mockToast.success).toHaveBeenCalledWith( + 'Schedule request accepted. It will appear in the list shortly.', + ), + ); + expect( + screen.getByText('Schedules for Weekly review', { + selector: '.ant-drawer-title', + }), + ).toBeVisible(); + await waitFor(() => expect(listCallCount).toBeGreaterThanOrEqual(2)); + resolveRefresh({ items: [], nextCursor: null, totalCount: 0 }); + view.unmount(); + }); + + it('keeps refreshing until the accepted schedule is observed', async () => { + let listCallCount = 0; + const observedSchedule = { + scheduleId: 'schedule-alpha', + displayName: 'Observed schedule', + prompt: '', + cronExpression: '0 9 * * 1-5', + timezone: 'Asia/Shanghai', + enabled: true, + createdAt: '2026-08-20T00:00:00Z', + updatedAt: '2026-08-20T00:00:00Z', + nextFireAt: null, + lastFireAt: null, + fireCount: 0, + failureCount: 0, + }; + mockedWorkflowScheduleApi.list.mockImplementation(() => { + listCallCount += 1; + return Promise.resolve({ + items: listCallCount >= 3 ? [observedSchedule] : [], + nextCursor: null, + totalCount: listCallCount >= 3 ? 1 : 0, + }); + }); + const view = renderSurface(true, 'panel'); + + await waitFor(() => + expect( + screen.getByRole('heading', { name: 'No schedules yet' }), + ).toBeVisible(), + ); + fireEvent.click(screen.getByRole('button', { name: 'New schedule' })); + + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Observed schedule' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + await waitFor(() => + expect( + screen.getByText('Review schedule', { selector: '.ant-drawer-title' }), + ).toBeVisible(), + ); + fireEvent.click(screen.getByRole('button', { name: 'Create schedule' })); + + await waitFor(() => expect(listCallCount).toBeGreaterThanOrEqual(2)); + expect(mockToast.success).toHaveBeenCalledWith( + 'Schedule request accepted. It will appear in the list shortly.', + ); + + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1100)); + }); + await waitFor(() => expect(listCallCount).toBeGreaterThanOrEqual(3)); + + const observedCallCount = listCallCount; + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1100)); + }); + expect(listCallCount).toBe(observedCallCount); + view.unmount(); + }); + + it('synchronizes a custom weekly cron back to the repeat builder before review', async () => { + renderSurface(true); + + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Cron toggle schedule' }, + }); + fireEvent.click( + screen.getByRole('button', { name: 'write it as cron instead' }), + ); + fireEvent.change(screen.getByRole('textbox', { name: 'Cron expression' }), { + target: { value: '15 14 * * 2' }, + }); + fireEvent.click( + screen.getByRole('button', { name: 'use the repeat builder' }), + ); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.preview).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ cronExpression: '15 14 * * 2' }), + ), + ); + expect(screen.getAllByRole('definition')[2]).toHaveTextContent( + 'Every Tuesday at 14:15', + ); + fireEvent.click(screen.getByRole('button', { name: 'Create schedule' })); + await waitFor(() => + expect(workflowScheduleApi.create).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ cronExpression: '15 14 * * 2' }), + ), + ); + }); + + it('renders only the controls for the active cadence mode', () => { + renderSurface(true); + + expect( + screen.getByRole('combobox', { name: 'Repeat' }), + ).toBeInTheDocument(); + expect(screen.getByLabelText('Time')).toBeInTheDocument(); + expect( + screen.queryByRole('textbox', { name: 'Cron expression' }), + ).not.toBeInTheDocument(); + + fireEvent.click( + screen.getByRole('button', { name: 'write it as cron instead' }), + ); + + expect( + screen.queryByRole('combobox', { name: 'Repeat' }), + ).not.toBeInTheDocument(); + expect(screen.queryByLabelText('Time')).not.toBeInTheDocument(); + expect( + screen.getByRole('textbox', { name: 'Cron expression' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('textbox', { name: 'Timezone' }), + ).toBeInTheDocument(); + + fireEvent.click( + screen.getByRole('button', { name: 'use the repeat builder' }), + ); + + expect( + screen.getByRole('combobox', { name: 'Repeat' }), + ).toBeInTheDocument(); + expect(screen.getByLabelText('Time')).toBeInTheDocument(); + expect( + screen.queryByRole('textbox', { name: 'Cron expression' }), + ).not.toBeInTheDocument(); + }); + + it('offers hourly, daily, weekday, weekly, and monthly repeat presets', async () => { + renderSurface(true); + + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Repeat' })); + + for (const option of [ + 'Every hour', + 'Every day', + 'Weekdays', + 'Every week', + 'Every month', + ]) { + await waitFor(() => + expect(findScheduleOption(option)).toBeInTheDocument(), + ); + } + }); + + it('builds a weekly cron from the selected day', async () => { + renderSurface(true); + + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Repeat' })); + fireEvent.click(screen.getByText('Every week')); + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Day of week' })); + fireEvent.click(screen.getByText('Wednesday')); + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Weekly workflow run' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.preview).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ cronExpression: '0 9 * * 3' }), + ), + ); + await waitFor(() => + expect( + screen.getByText('Review schedule', { selector: '.ant-modal-title' }), + ).toBeVisible(), + ); + expect(screen.getAllByRole('definition')[2]).toHaveTextContent( + 'Every Wednesday at 09:00', + ); + }); + + it('builds a monthly cron from the selected day and fixes hourly cadence', async () => { + renderSurface(true); + + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Repeat' })); + fireEvent.click(findScheduleOption('Every month')); + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Day of month' })); + fireEvent.click(findScheduleOption('5')); + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Monthly workflow run' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.preview).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ cronExpression: '0 9 5 * *' }), + ), + ); + await waitFor(() => + expect( + screen.getByText('Review schedule', { selector: '.ant-modal-title' }), + ).toBeVisible(), + ); + expect(screen.getAllByRole('definition')[2]).toHaveTextContent( + 'Every month on day 5 at 09:00', + ); + + fireEvent.click(screen.getByRole('button', { name: 'Back' })); + fireEvent.mouseDown(screen.getByRole('combobox', { name: 'Repeat' })); + fireEvent.click(findScheduleOption('Every hour')); + await waitFor(() => expect(screen.getByLabelText('Time')).toBeDisabled()); + fireEvent.change(screen.getByRole('textbox', { name: 'Schedule name' }), { + target: { value: 'Hourly workflow run' }, + }); + fireEvent.click(screen.getByRole('button', { name: 'Review schedule' })); + + await waitFor(() => + expect(workflowScheduleApi.preview).toHaveBeenLastCalledWith( + 'scope-alpha', + 'wf-alpha', + expect.objectContaining({ cronExpression: '0 * * * *' }), + ), + ); + }); + + it('keeps the empty schedule state compact with one create action', async () => { + renderSurface(true, 'panel'); + + const drawer = document.querySelector( + '.wa-vnext-schedule-drawer', + ); + expect(drawer).not.toBeNull(); + if (!drawer) { + throw new Error('Expected Schedule drawer'); + } + + await waitFor(() => + expect( + screen.getByRole('heading', { name: 'No schedules yet' }), + ).toBeVisible(), + ); + + expect( + within(drawer).getByText('Schedules for Weekly review', { + selector: '.ant-drawer-title', + }), + ).toBeVisible(); + expect( + within(drawer).queryByText('Schedules', { + selector: '.wa-vnext__schedule-toolbar strong', + }), + ).not.toBeInTheDocument(); + expect( + within(drawer).queryByText('Recurring runs for Weekly review'), + ).not.toBeInTheDocument(); + expect( + screen.getAllByRole('button', { name: 'New schedule' }), + ).toHaveLength(1); + expect(drawer).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'No schedules yet' }), + ).toHaveClass('wa-vnext__schedule-empty-title'); + }); + + it('keeps committed Schedule rows visible while the list refreshes', async () => { + const schedule = createScheduleSummary(); + let listCallCount = 0; + let resolveRefresh: (value: { + items: ReturnType[]; + nextCursor: null; + totalCount: number; + }) => void = () => undefined; + const deferredRefresh = new Promise<{ + items: ReturnType[]; + nextCursor: null; + totalCount: number; + }>((resolve) => { + resolveRefresh = resolve; + }); + mockedWorkflowScheduleApi.list.mockImplementation(() => { + listCallCount += 1; + return listCallCount === 1 + ? Promise.resolve({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }) + : deferredRefresh; + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + + await waitFor(() => + expect( + screen.getByRole('button', { name: 'View Daily workflow run' }), + ).toBeVisible(), + ); + fireEvent.click(screen.getByRole('button', { name: 'Refresh schedules' })); + + const scheduleRegion = await screen.findByRole('region', { + name: 'Schedules for Weekly review', + }); + await waitFor(() => + expect(workflowScheduleApi.list).toHaveBeenCalledTimes(2), + ); + expect( + screen.getByRole('button', { name: 'Refresh schedules' }), + ).toBeDisabled(); + expect(scheduleRegion).toHaveAttribute('aria-busy', 'true'); + expect( + within(scheduleRegion).getByRole('button', { + name: 'View Daily workflow run', + }), + ).toBeVisible(); + const refreshStatus = within(scheduleRegion).getByRole('status', { + name: 'Refreshing schedules…', + }); + expect(refreshStatus).toHaveClass('aevatar-loading-overlay'); + expect(refreshStatus.querySelectorAll('.aevatar-loading-dot')).toHaveLength( + 3, + ); + expect(screen.getByRole('button', { name: 'New schedule' })).toBeEnabled(); + + await act(async () => { + resolveRefresh({ + items: [createScheduleSummary({ displayName: 'Updated workflow run' })], + nextCursor: null, + totalCount: 1, + }); + await deferredRefresh; + }); + + expect( + await screen.findByRole('button', { name: 'View Updated workflow run' }), + ).toBeVisible(); + expect(scheduleRegion).toHaveAttribute('aria-busy', 'false'); + expect( + within(scheduleRegion).queryByRole('status', { + name: 'Refreshing schedules…', + }), + ).not.toBeInTheDocument(); + }); + + it('opens a selected Schedule on Overview and returns through the stable header', async () => { + const schedule = createScheduleSummary(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:02:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + runActorId: '', + error: '', + manual: false, + }, + { + scheduledFireAt: '2026-08-19T03:00:00Z', + completedAt: '2026-08-19T03:01:00Z', + idempotencyKey: 'schedule-alpha:manual:1', + runActorId: '', + error: 'Workflow invocation failed', + manual: true, + }, + ], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + + await waitFor(() => + expect(screen.getByText('Daily workflow run')).toBeVisible(), + ); + expect( + screen.getByText('Schedules for Weekly review', { + selector: '.ant-modal-title', + }), + ).toBeVisible(); + expect( + screen.queryByText('Schedules', { + selector: '.wa-vnext__schedule-toolbar strong', + }), + ).not.toBeInTheDocument(); + expect( + screen.queryByText('Recurring runs for Weekly review'), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'View Daily workflow run' }), + ).toBeVisible(); + expect(screen.getByRole('button', { name: 'New schedule' })).toBeVisible(); + + fireEvent.click( + screen.getByRole('button', { name: 'View Daily workflow run' }), + ); + await waitFor(() => + expect(workflowScheduleApi.get).toHaveBeenCalledWith( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + ), + ); + expect( + screen.getByText('Daily workflow run', { + selector: '.ant-modal-title *', + }), + ).toBeVisible(); + expect(screen.getByText('Weekly review')).toBeVisible(); + expect(screen.getByRole('tab', { name: 'Overview' })).toHaveAttribute( + 'aria-selected', + 'true', + ); + expect(screen.getByRole('tab', { name: 'History' })).toHaveAttribute( + 'aria-selected', + 'false', + ); + expect(screen.queryByText('Recent attempts')).not.toBeInTheDocument(); + + fireEvent.click(screen.getByRole('button', { name: 'Back to schedules' })); + expect( + screen.getByText('Schedules for Weekly review', { + selector: '.ant-modal-title', + }), + ).toBeVisible(); + expect( + screen.queryByRole('button', { name: 'Back to schedules' }), + ).not.toBeInTheDocument(); + }); + + it('keeps one selected Schedule header while switching detail tabs', async () => { + const schedule = createScheduleSummary(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + + const dialog = screen.getByRole('dialog'); + const overviewHeader = dialog.querySelector( + '.wa-vnext__schedule-selected-heading', + ); + await waitFor(() => expect(overviewHeader).toBeVisible()); + + fireEvent.click(screen.getByRole('tab', { name: 'History' })); + expect( + await screen.findByRole('heading', { name: 'Recent attempts' }), + ).toBeVisible(); + + const historyHeader = dialog.querySelector( + '.wa-vnext__schedule-selected-heading', + ); + expect(historyHeader).toBe(overviewHeader); + expect(historyHeader).toHaveTextContent( + 'Daily workflow run · Weekly review', + ); + expect(historyHeader).toHaveAccessibleName( + 'Schedule Daily workflow run in workflow Weekly review', + ); + expect( + within(dialog).queryByText('Schedule history', { + selector: '.ant-modal-title *', + }), + ).not.toBeInTheDocument(); + }); + + it('presents Schedule Overview with primary actions and guarded lifecycle actions', async () => { + const schedule = createScheduleSummary({ + prompt: 'Summarize new feedback.', + }); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:02:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + runActorId: '', + error: '', + manual: false, + }, + ], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + + expect( + await screen.findByRole('heading', { + name: 'Every weekday at 09:00', + }), + ).toBeVisible(); + expect(screen.getByText('Enabled')).toBeVisible(); + expect(screen.getByText('Timezone')).toBeVisible(); + expect(screen.getByText('Asia/Shanghai')).toBeVisible(); + expect(screen.getByText('Next scheduled')).toBeVisible(); + expect(screen.getByText('Last attempt')).toBeVisible(); + expect(screen.getByText('Total attempts')).toBeVisible(); + expect(screen.getByText('Failed attempts')).toBeVisible(); + expect(screen.getByText('Run input')).toBeVisible(); + expect(screen.getByText('Summarize new feedback.')).toBeVisible(); + expect(screen.getByText('Advanced details')).toBeVisible(); + expect(screen.getByText('0 9 * * 1-5')).not.toBeVisible(); + + const advancedDetails = screen + .getByText('Advanced details') + .closest('details'); + expect(advancedDetails).not.toBeNull(); + if (!advancedDetails) { + throw new Error('Expected Schedule advanced details disclosure'); + } + + fireEvent.click(within(advancedDetails).getByText('Advanced details')); + + expect(within(advancedDetails).getByText('Runs')).toBeVisible(); + expect( + within(advancedDetails).getByText('Every weekday at 09:00'), + ).toBeVisible(); + expect(within(advancedDetails).getByText('Technical format')).toBeVisible(); + expect(within(advancedDetails).getByText('0 9 * * 1-5')).toBeVisible(); + + expect(screen.getByRole('button', { name: 'Run now' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Edit schedule' })).toBeVisible(); + expect( + screen.queryByRole('button', { name: 'Pause' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Delete schedule' }), + ).not.toBeInTheDocument(); + + fireEvent.click( + screen.getByRole('button', { name: 'More schedule actions' }), + ); + expect( + await screen.findByRole('menuitem', { name: 'Pause' }), + ).toBeInTheDocument(); + fireEvent.click(screen.getByRole('menuitem', { name: 'Delete schedule' })); + + expect( + await screen.findByText('Delete Daily workflow run?', { + selector: '.ant-modal-confirm-title', + }), + ).toBeInTheDocument(); + expect( + screen.getByText('Daily workflow run will stop running on schedule.'), + ).toBeInTheDocument(); + }); + + it('keeps committed Schedule details visible while an action refreshes them', async () => { + const schedule = createScheduleSummary(); + let resolveDetailRefresh: (value: { + schedule: ReturnType; + recentFires: never[]; + }) => void = () => undefined; + const deferredDetailRefresh = new Promise<{ + schedule: ReturnType; + recentFires: never[]; + }>((resolve) => { + resolveDetailRefresh = resolve; + }); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get + .mockResolvedValue({ + schedule: createScheduleSummary({ fireCount: 13 }), + recentFires: [], + }) + .mockResolvedValueOnce({ schedule, recentFires: [] }) + .mockReturnValueOnce(deferredDetailRefresh); + mockedWorkflowScheduleApi.runNow.mockResolvedValue({ + scheduleId: schedule.scheduleId, + accepted: true, + scheduledFireAt: '2026-08-20T01:00:00Z', + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + expect( + await screen.findByRole('heading', { + name: 'Every weekday at 09:00', + }), + ).toBeVisible(); + + fireEvent.click(screen.getByRole('button', { name: 'Run now' })); + + const detailRegion = await screen.findByRole('region', { + name: 'Schedule overview', + }); + await waitFor(() => + expect(workflowScheduleApi.get).toHaveBeenCalledTimes(2), + ); + expect(detailRegion).toHaveAttribute('aria-busy', 'true'); + expect( + within(detailRegion).getByRole('heading', { + name: 'Every weekday at 09:00', + }), + ).toBeVisible(); + const refreshStatus = within(detailRegion).getByRole('status', { + name: 'Refreshing schedule details…', + }); + expect(refreshStatus).toHaveClass('aevatar-loading-overlay'); + expect(refreshStatus.querySelectorAll('.aevatar-loading-dot')).toHaveLength( + 3, + ); + expect(screen.getByRole('tab', { name: 'Overview' })).toBeVisible(); + + await act(async () => { + resolveDetailRefresh({ + schedule: createScheduleSummary({ fireCount: 13 }), + recentFires: [], + }); + await deferredDetailRefresh; + }); + + await waitFor(() => + expect(detailRegion).toHaveAttribute('aria-busy', 'false'), + ); + expect( + within(detailRegion).queryByRole('status', { + name: 'Refreshing schedule details…', + }), + ).not.toBeInTheDocument(); + }); + + it('keeps Edit as a temporary mode that returns to Overview', async () => { + const schedule = createScheduleSummary(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click( + await screen.findByRole('button', { name: 'Edit schedule' }), + ); + + expect( + screen.getByText('Daily workflow run', { + selector: '.ant-modal-title *', + }), + ).toBeVisible(); + expect( + screen.getByRole('heading', { name: 'Edit schedule' }), + ).toBeVisible(); + expect( + screen.queryByRole('tab', { name: 'History' }), + ).not.toBeInTheDocument(); + + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + expect(screen.getByRole('tab', { name: 'Overview' })).toHaveAttribute( + 'aria-selected', + 'true', + ); + }); + + it('keeps History focused on recent attempts and hands actual Runs to Activity', async () => { + const schedule = createScheduleSummary(); + const onClose = jest.fn(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:02:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + runActorId: '', + error: 'Capability admission rejected the scheduled request.', + manual: false, + }, + { + scheduledFireAt: '2026-08-19T03:00:00Z', + completedAt: '2026-08-19T03:01:00Z', + idempotencyKey: 'schedule-alpha:manual:1', + runActorId: 'run-manual-alpha', + error: '', + manual: true, + }, + ], + }); + + renderSurface(true, 'modal', onClose, 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + + expect( + await screen.findByRole('heading', { name: 'Recent attempts' }), + ).toBeInTheDocument(); + const dialog = screen.getByRole('dialog'); + await waitFor(() => { + const historyHeading = dialog.querySelector( + '.wa-vnext__schedule-selected-heading', + ); + expect(historyHeading).toBeVisible(); + expect(historyHeading).toHaveTextContent( + 'Daily workflow run · Weekly review', + ); + expect(historyHeading).toHaveAttribute( + 'aria-label', + 'Schedule Daily workflow run in workflow Weekly review', + ); + }); + expect( + within(dialog).queryByText('Schedule: Daily workflow run'), + ).not.toBeInTheDocument(); + expect( + within(dialog).queryByText('Workflow: Weekly review'), + ).not.toBeInTheDocument(); + expect( + await screen.findByRole('columnheader', { name: 'Scheduled time' }), + ).toBeVisible(); + expect(screen.getByRole('columnheader', { name: 'Source' })).toBeVisible(); + expect( + screen.getByRole('columnheader', { name: 'Schedule outcome' }), + ).toBeVisible(); + expect( + screen.getByRole('columnheader', { name: 'Completed time' }), + ).toBeVisible(); + expect( + screen.queryByRole('columnheader', { name: 'Action' }), + ).not.toBeInTheDocument(); + expect(screen.getByText('Scheduled')).toBeVisible(); + expect(screen.getByText('Manual')).toBeVisible(); + expect(screen.getByText('Failed to start')).toBeVisible(); + expect(screen.getByText('Run created')).toBeVisible(); + expect(screen.queryByText('Run started')).not.toBeInTheDocument(); + expect(screen.queryByText('Succeeded')).not.toBeInTheDocument(); + expect( + screen.getByText('The scheduled attempt could not start the Workflow.'), + ).toBeVisible(); + expect( + screen.queryByText( + 'Capability admission rejected the scheduled request.', + ), + ).not.toBeVisible(); + expect(screen.getByText('Technical details')).toBeInTheDocument(); + expect(screen.queryByText('schedule-alpha:fire:1')).not.toBeInTheDocument(); + const runLink = screen.getByRole('link', { + name: /Open Run created by the Schedule attempt at/, + }); + expect(runLink).toBeVisible(); + expect(runLink).toHaveClass('wa-vnext__schedule-history-row-link'); + const runRow = runLink.closest('tr'); + expect(runRow).toHaveClass('wa-vnext__schedule-history-row--linked'); + expect(runRow?.querySelectorAll('td')).toHaveLength(4); + expect(runRow).toContainElement(screen.getByText('Run created')); + const completedTime = dialog.querySelector( + 'time[datetime="2026-08-19T03:01:00Z"]', + ); + expect(completedTime).toBeInTheDocument(); + expect(completedTime?.closest('td')?.cellIndex).toBe(3); + const failedRow = screen + .getByText('The scheduled attempt could not start the Workflow.') + .closest('tr'); + expect(failedRow).not.toHaveClass('wa-vnext__schedule-history-row--linked'); + const failedCells = failedRow?.querySelectorAll('td'); + expect(failedCells).toHaveLength(4); + expect(failedRow?.querySelector('a')).toBeNull(); + + fireEvent.click(screen.getByText('Technical details')); + expect( + screen.getByText('Capability admission rejected the scheduled request.'), + ).toBeVisible(); + + const relatedRunsLink = screen.getByRole('link', { + name: 'View related runs in Activity', + }); + expect(relatedRunsLink).toHaveAttribute( + 'href', + '/scopes/scope-alpha/workflow-activity-vnext/activity?workflowId=wf-alpha&schedule=schedule-alpha', + ); + expect(relatedRunsLink).toHaveAttribute('target', '_blank'); + expect(relatedRunsLink).toHaveAttribute('rel', 'noopener noreferrer'); + + fireEvent.click(relatedRunsLink); + + expect(onClose).not.toHaveBeenCalled(); + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + + it('opens an authoritative History attempt directly in Activity', async () => { + const schedule = createScheduleSummary(); + const onClose = jest.fn(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:02:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + runActorId: 'run-alpha', + error: '', + manual: false, + }, + ], + }); + + renderSurface(true, 'modal', onClose, 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + + const runLink = await screen.findByRole('link', { + name: /Open Run created by the Schedule attempt at/, + }); + expect(runLink).toHaveAttribute( + 'href', + '/scopes/scope-alpha/workflow-activity-vnext/activity/run-alpha?workflowId=wf-alpha&schedule=schedule-alpha', + ); + expect(runLink).toHaveAttribute('target', '_blank'); + expect(runLink).toHaveAttribute('rel', 'noopener noreferrer'); + + fireEvent.click(runLink); + + expect(onClose).not.toHaveBeenCalled(); + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + + it('keeps a History attempt without Run identity non-interactive', async () => { + const schedule = createScheduleSummary(); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:02:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + runActorId: '', + error: '', + manual: false, + }, + ], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + + await screen.findByRole('heading', { name: 'Recent attempts' }); + + expect( + screen.queryByRole('link', { name: /View related runs from/ }), + ).not.toBeInTheDocument(); + const attemptRow = screen.getByText('Accepted').closest('tr'); + expect(attemptRow).not.toBeNull(); + const attemptCells = attemptRow?.querySelectorAll('td'); + expect(attemptCells).toHaveLength(4); + expect(attemptRow).not.toHaveClass( + 'wa-vnext__schedule-history-row--linked', + ); + expect(attemptRow?.querySelector('a')).toBeNull(); + + const relatedRunsLink = screen.getByRole('link', { + name: 'View related runs in Activity', + }); + expect(relatedRunsLink).toHaveAttribute( + 'href', + '/scopes/scope-alpha/workflow-activity-vnext/activity?workflowId=wf-alpha&schedule=schedule-alpha', + ); + expect(relatedRunsLink).toHaveAttribute('target', '_blank'); + expect(relatedRunsLink).toHaveAttribute('rel', 'noopener noreferrer'); + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + + it('keeps multiple attempts scannable with one table row per attempt', async () => { + const schedule = createScheduleSummary({ + fireCount: 3, + failureCount: 2, + }); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [ + { + scheduledFireAt: '2026-08-21T01:00:00Z', + completedAt: '2026-08-21T01:01:00Z', + idempotencyKey: 'schedule-alpha:fire:3', + runActorId: '', + error: 'Scheduled request was rejected.', + manual: false, + }, + { + scheduledFireAt: '2026-08-20T01:00:00Z', + completedAt: '2026-08-20T01:01:00Z', + idempotencyKey: 'schedule-alpha:fire:2', + runActorId: '', + error: '', + manual: false, + }, + { + scheduledFireAt: '2026-08-19T03:00:00Z', + completedAt: '2026-08-19T03:01:00Z', + idempotencyKey: 'schedule-alpha:manual:1', + runActorId: '', + error: 'Manual request was rejected.', + manual: true, + }, + ], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + + await screen.findByRole('heading', { name: 'Recent attempts' }); + await waitFor(() => + expect( + document.querySelectorAll('.wa-vnext__schedule-history-table tbody tr'), + ).toHaveLength(3), + ); + expect(screen.getAllByText('Technical details')).toHaveLength(2); + expect( + screen.getByText('The scheduled attempt could not start the Workflow.'), + ).toBeInTheDocument(); + expect( + screen.getByText('The manual attempt could not start the Workflow.'), + ).toBeInTheDocument(); + }); + + it('shows a bounded History empty state when no attempts have been recorded', async () => { + const schedule = createScheduleSummary({ + lastFireAt: null, + fireCount: 0, + failureCount: 0, + }); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get.mockResolvedValue({ + schedule, + recentFires: [], + }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + fireEvent.click( + await screen.findByRole('button', { name: 'View Daily workflow run' }), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + + expect(await screen.findByText('No attempts yet')).toBeInTheDocument(); + expect(screen.queryByText('No fires yet')).not.toBeInTheDocument(); + }); + + it('keeps a detail failure distinct from an empty recent-fire result', async () => { + const schedule = createScheduleSummary({ + lastFireAt: null, + fireCount: 0, + failureCount: 0, + }); + mockedWorkflowScheduleApi.list.mockResolvedValue({ + items: [schedule], + nextCursor: null, + totalCount: 1, + }); + mockedWorkflowScheduleApi.get + .mockRejectedValueOnce(new Error(rawProxyError)) + .mockResolvedValueOnce({ schedule, recentFires: [] }); + + renderSurface(true, 'modal', jest.fn(), 'list'); + + await waitFor(() => + expect(screen.getByText('Daily workflow run')).toBeVisible(), + ); + fireEvent.click( + screen.getByRole('button', { name: 'View Daily workflow run' }), + ); + + await waitFor(() => + expect(screen.getByText("Schedule couldn't be loaded")).toBeVisible(), + ); + expect( + screen.getByText( + 'The current service is temporarily unavailable, please try again later.', + ), + ).toBeVisible(); + expect(screen.getByText(rawProxyError)).not.toBeVisible(); + expect(screen.getByText('Technical details')).toBeVisible(); + expect( + screen.getByText("Schedule couldn't be loaded").closest('.ant-alert'), + ).toHaveClass('wa-vnext__schedule-failure'); + expect(screen.queryByText('No attempts yet')).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText('Technical details')); + expect(screen.getByText(rawProxyError)).toBeVisible(); + + fireEvent.click(screen.getByRole('button', { name: 'Retry' })); + + await waitFor(() => + expect(workflowScheduleApi.get).toHaveBeenCalledTimes(2), + ); + fireEvent.click(await screen.findByRole('tab', { name: 'History' })); + expect(await screen.findByText('No attempts yet')).toBeVisible(); + expect( + screen.queryByText("Schedule couldn't be loaded"), + ).not.toBeInTheDocument(); + }); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowScheduleSurface.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowScheduleSurface.tsx new file mode 100644 index 0000000000..c6310aa5ab --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowScheduleSurface.tsx @@ -0,0 +1,1833 @@ +import { + ArrowLeftOutlined, + CalendarOutlined, + DeleteOutlined, + DownOutlined, + EditOutlined, + PlayCircleOutlined, + PlusOutlined, + ReloadOutlined, + StopOutlined, +} from '@ant-design/icons'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { getLocale } from '@umijs/max'; +import { + Alert, + Button, + Drawer, + Dropdown, + Input, + Modal, + Select, + Space, + Switch, + Tabs, + Tag, +} from 'antd'; +import React from 'react'; +import { + type WorkflowScheduleConfigurationInput, + type WorkflowScheduleFire, + type WorkflowSchedulePreview, + type WorkflowScheduleSummary, + workflowScheduleApi, +} from '@/shared/api/workflowScheduleApi'; +import { t } from '@/shared/i18n/messages'; +import { AevatarLoadingOverlay } from '@/shared/ui/AevatarLoading'; +import { useConsoleToast } from '@/shared/ui/ConsoleToast'; +import { describeError } from '@/shared/ui/errorText'; +import { + buildWorkflowActivityRunHref, + buildWorkflowActivitySectionHref, +} from '../navigation'; +import TechnicalDetails from '../TechnicalDetails'; + +type WorkflowScheduleSurfaceProps = { + readonly initialView?: ScheduleSurfaceView; + readonly mode: 'modal' | 'panel'; + readonly open: boolean; + readonly onClose: () => void; + readonly scopeId: string; + readonly workflowId: string; + readonly workflowName: string; + readonly available: boolean; +}; + +type ScheduleForm = { + readonly displayName: string; + readonly cronExpression: string; + readonly timezone: string; + readonly enabled: boolean; + readonly prompt: string; +}; + +type CreationStep = 'configure' | 'previewing' | 'review'; +type ScheduleSurfaceView = 'list' | 'detail' | 'form'; +type ScheduleDetailTab = 'overview' | 'history'; +type RepeatPreset = 'hourly' | 'daily' | 'weekdays' | 'weekly' | 'monthly'; +type ScheduleAttemptOutcome = 'accepted' | 'failed' | 'run-created'; + +const weekdayValues = ['1', '2', '3', '4', '5', '6', '0'] as const; +type WeekdayValue = (typeof weekdayValues)[number]; + +const monthlyDayValues = Array.from({ length: 31 }, (_, index) => + String(index + 1), +); + +const scheduleQueryKey = (scopeId: string, workflowId: string) => [ + 'workflow-activity-vnext', + 'workflow-schedules', + scopeId, + workflowId, +]; + +function getScheduleAttemptOutcome( + fire: Pick, +): ScheduleAttemptOutcome { + if (fire.error.trim()) return 'failed'; + return fire.runActorId.trim() ? 'run-created' : 'accepted'; +} + +function getScheduleAttemptOutcomeLabel( + outcome: ScheduleAttemptOutcome, +): string { + if (outcome === 'failed') { + return t('workflowActivityVNext.schedule.failedToStart', 'Failed to start'); + } + if (outcome === 'run-created') { + return t('workflowActivityVNext.schedule.runCreated', 'Run created'); + } + return t('workflowActivityVNext.schedule.attemptAccepted', 'Accepted'); +} + +function defaultTimezone(): string { + return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; +} + +function cronFromRepeat( + preset: RepeatPreset, + time: string, + weeklyDay: WeekdayValue, + monthlyDay: string, +): string { + const [hour = '9', minute = '0'] = time.split(':'); + if (preset === 'hourly') return '0 * * * *'; + if (preset === 'weekdays') { + return `${Number(minute)} ${Number(hour)} * * 1-5`; + } + if (preset === 'weekly') { + return `${Number(minute)} ${Number(hour)} * * ${weeklyDay}`; + } + if (preset === 'monthly') { + return `${Number(minute)} ${Number(hour)} ${Number(monthlyDay)} * *`; + } + return `${Number(minute)} ${Number(hour)} * * *`; +} + +function repeatFromCron(cronExpression: string): { + readonly preset: RepeatPreset; + readonly time: string; + readonly weeklyDay: WeekdayValue; + readonly monthlyDay: string; +} | null { + const normalized = cronExpression.trim().replace(/\s+/g, ' '); + if (normalized === '0 * * * *') { + return { + preset: 'hourly', + time: '09:00', + weeklyDay: '1', + monthlyDay: '1', + }; + } + const match = normalized.match( + /^(\d{1,2}) (\d{1,2}) (\*|\d{1,2}) (\*|\d{1,2}) (\*|1-5|[0-7])$/, + ); + if (!match) return null; + const minute = Number(match[1]); + const hour = Number(match[2]); + if (minute > 59 || hour > 23) return null; + const dayOfMonth = match[3]; + const month = match[4]; + const dayOfWeek = match[5]; + if (month !== '*' || (dayOfMonth !== '*' && dayOfWeek !== '*')) { + return null; + } + if (dayOfWeek === '1-5') { + return { + preset: 'weekdays', + time: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`, + weeklyDay: '1', + monthlyDay: '1', + }; + } + if (dayOfMonth !== '*') { + const parsedDay = Number(dayOfMonth); + if (parsedDay < 1 || parsedDay > 31 || dayOfWeek !== '*') return null; + return { + preset: 'monthly', + time: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`, + weeklyDay: '1', + monthlyDay: String(parsedDay), + }; + } + if (dayOfWeek !== '*') { + const parsedDay = dayOfWeek === '7' ? '0' : dayOfWeek; + if (!weekdayValues.includes(parsedDay as WeekdayValue)) return null; + return { + preset: 'weekly', + time: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`, + weeklyDay: parsedDay as WeekdayValue, + monthlyDay: '1', + }; + } + return { + preset: 'daily', + time: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`, + weeklyDay: '1', + monthlyDay: '1', + }; +} + +function emptyForm(): ScheduleForm { + return { + displayName: '', + cronExpression: '0 9 * * 1-5', + timezone: defaultTimezone(), + enabled: true, + prompt: '', + }; +} + +function formFromSchedule(schedule: WorkflowScheduleSummary): ScheduleForm { + return { + displayName: schedule.displayName, + cronExpression: schedule.cronExpression, + timezone: schedule.timezone, + enabled: schedule.enabled, + prompt: schedule.prompt, + }; +} + +function formatScheduleDate(value: string | null, timezone?: string): string { + if (!value) + return t('workflowActivityVNext.common.unavailable', 'Unavailable'); + const date = new Date(value); + if (Number.isNaN(date.getTime())) return value; + + try { + return new Intl.DateTimeFormat(getLocale(), { + dateStyle: 'medium', + timeStyle: 'short', + ...(timezone ? { timeZone: timezone } : {}), + }).format(date); + } catch { + return new Intl.DateTimeFormat(getLocale(), { + dateStyle: 'medium', + timeStyle: 'short', + }).format(date); + } +} + +function scheduleRecurrenceSummary(cronExpression: string): string { + const repeat = repeatFromCron(cronExpression); + if (!repeat) { + return t( + 'workflowActivityVNext.schedule.customRecurrence', + 'Custom schedule', + ); + } + if (repeat.preset === 'hourly') { + return t('workflowActivityVNext.schedule.hourly', 'Every hour'); + } + if (repeat.preset === 'weekdays') { + return t( + 'workflowActivityVNext.schedule.everyWeekdaySummary', + 'Every weekday at {time}', + { time: repeat.time }, + ); + } + if (repeat.preset === 'weekly') { + return t( + 'workflowActivityVNext.schedule.weeklySummary', + 'Every {day} at {time}', + { + day: t( + `workflowActivityVNext.schedule.weekday.${repeat.weeklyDay}`, + repeat.weeklyDay, + ), + time: repeat.time, + }, + ); + } + if (repeat.preset === 'monthly') { + return t( + 'workflowActivityVNext.schedule.monthlySummary', + 'Every month on day {day} at {time}', + { day: repeat.monthlyDay, time: repeat.time }, + ); + } + return t( + 'workflowActivityVNext.schedule.everyDaySummary', + 'Every day at {time}', + { time: repeat.time }, + ); +} + +function rawErrorMessage(error: unknown): string { + if (error instanceof Error) return error.message; + if (error && typeof error === 'object' && !Array.isArray(error)) { + const message = (error as { message?: unknown }).message; + if (typeof message === 'string') return message; + } + return String(error ?? ''); +} + +function scheduleErrorDescription(error: unknown): React.ReactNode { + const description = describeError(error); + const technicalDetails = rawErrorMessage(error).trim(); + + return ( +
+

{description}

+ {technicalDetails && technicalDetails !== description ? ( + {technicalDetails} + ) : null} +
+ ); +} + +const WorkflowScheduleSurface: React.FC = ({ + available, + initialView, + mode, + onClose, + open, + scopeId, + workflowId, + workflowName, +}) => { + const toast = useConsoleToast(); + const queryClient = useQueryClient(); + const queryKey = React.useMemo( + () => scheduleQueryKey(scopeId, workflowId), + [scopeId, workflowId], + ); + const [surfaceView, setSurfaceView] = React.useState( + initialView ?? (mode === 'modal' ? 'form' : 'list'), + ); + const [creationStep, setCreationStep] = + React.useState('configure'); + const [editingSchedule, setEditingSchedule] = + React.useState(null); + const [selectedSchedule, setSelectedSchedule] = + React.useState(null); + const [detailTab, setDetailTab] = + React.useState('overview'); + const [form, setForm] = React.useState(() => emptyForm()); + const [repeatPreset, setRepeatPreset] = + React.useState('weekdays'); + const [repeatTime, setRepeatTime] = React.useState('09:00'); + const [weeklyDay, setWeeklyDay] = React.useState('1'); + const [monthlyDay, setMonthlyDay] = React.useState('1'); + const [cronMode, setCronMode] = React.useState(false); + const [preview, setPreview] = React.useState( + null, + ); + const [saving, setSaving] = React.useState(false); + const [actionScheduleId, setActionScheduleId] = React.useState( + null, + ); + const [pendingObservationScheduleId, setPendingObservationScheduleId] = + React.useState(null); + + const previewing = creationStep === 'previewing'; + const busy = previewing || saving; + const formValid = Boolean( + form.displayName.trim() && + form.cronExpression.trim() && + form.timezone.trim(), + ); + + const schedules = useQuery({ + enabled: open && available, + queryKey, + queryFn: () => + workflowScheduleApi.list(scopeId, workflowId, { + includeTotalCount: true, + take: 50, + }), + refetchInterval: pendingObservationScheduleId ? 1000 : false, + refetchOnMount: 'always', + retry: false, + }); + + const scheduleDetail = useQuery({ + enabled: open && available && Boolean(selectedSchedule), + queryKey: [...queryKey, 'detail', selectedSchedule?.scheduleId], + queryFn: () => { + if (!selectedSchedule) { + throw new Error('A Schedule must be selected.'); + } + return workflowScheduleApi.get( + scopeId, + workflowId, + selectedSchedule.scheduleId, + ); + }, + retry: false, + }); + const scheduleListRefreshing = + schedules.isFetching && !schedules.isPending && Boolean(schedules.data); + const scheduleDetailRefreshing = + scheduleDetail.isFetching && + !scheduleDetail.isPending && + Boolean(scheduleDetail.data); + + React.useEffect(() => { + if (!open) return; + setSurfaceView(initialView ?? (mode === 'modal' ? 'form' : 'list')); + setCreationStep('configure'); + setEditingSchedule(null); + setSelectedSchedule(null); + setDetailTab('overview'); + setForm(emptyForm()); + setRepeatPreset('weekdays'); + setRepeatTime('09:00'); + setWeeklyDay('1'); + setMonthlyDay('1'); + setCronMode(false); + setPreview(null); + setPendingObservationScheduleId(null); + }, [initialView, mode, open, workflowId]); + + React.useEffect(() => { + if (!pendingObservationScheduleId || !schedules.data) return; + if ( + schedules.data.items.some( + (schedule) => schedule.scheduleId === pendingObservationScheduleId, + ) + ) { + setPendingObservationScheduleId(null); + } + }, [pendingObservationScheduleId, schedules.data]); + + const refreshSchedules = React.useCallback(async () => { + await queryClient.invalidateQueries({ queryKey }); + await queryClient.refetchQueries({ queryKey, type: 'active' }); + }, [queryClient, queryKey]); + + const openCreate = () => { + setEditingSchedule(null); + setSelectedSchedule(null); + setForm(emptyForm()); + setRepeatPreset('weekdays'); + setRepeatTime('09:00'); + setWeeklyDay('1'); + setMonthlyDay('1'); + setCronMode(false); + setPreview(null); + setCreationStep('configure'); + setSurfaceView('form'); + }; + + const openDetail = (schedule: WorkflowScheduleSummary) => { + setSelectedSchedule(schedule); + setEditingSchedule(null); + setDetailTab('overview'); + setSurfaceView('detail'); + }; + + const openEdit = () => { + const schedule = scheduleDetail.data?.schedule; + if (!schedule) return; + const repeat = repeatFromCron(schedule.cronExpression); + setEditingSchedule(schedule); + setForm(formFromSchedule(schedule)); + setRepeatPreset(repeat?.preset ?? 'weekdays'); + setRepeatTime(repeat?.time ?? '09:00'); + setWeeklyDay(repeat?.weeklyDay ?? '1'); + setMonthlyDay(repeat?.monthlyDay ?? '1'); + setCronMode(!repeat); + setPreview(null); + setCreationStep('configure'); + setSurfaceView('form'); + }; + + const leaveForm = () => { + if (busy) return; + if (editingSchedule && selectedSchedule) { + setEditingSchedule(null); + setCreationStep('configure'); + setPreview(null); + setSurfaceView('detail'); + return; + } + if (mode === 'modal' && initialView !== 'list') { + onClose(); + return; + } + setSurfaceView('list'); + setEditingSchedule(null); + setCreationStep('configure'); + setPreview(null); + }; + + const updateHumanRepeat = ( + nextPreset: RepeatPreset, + nextTime: string, + nextWeeklyDay = weeklyDay, + nextMonthlyDay = monthlyDay, + ) => { + setRepeatPreset(nextPreset); + setRepeatTime(nextTime); + setWeeklyDay(nextWeeklyDay); + setMonthlyDay(nextMonthlyDay); + setPreview(null); + setForm((current) => ({ + ...current, + cronExpression: cronFromRepeat( + nextPreset, + nextTime, + nextWeeklyDay, + nextMonthlyDay, + ), + })); + }; + + const previewSchedule = async () => { + if (!formValid || previewing) return; + setCreationStep('previewing'); + try { + const result = await workflowScheduleApi.preview(scopeId, workflowId, { + cronExpression: form.cronExpression, + timezone: form.timezone, + count: 5, + }); + setPreview(result); + setCreationStep('review'); + } catch (error) { + setCreationStep('configure'); + toast.error(describeError(error)); + } + }; + + const submitForm = async () => { + if (saving || !formValid) return; + setSaving(true); + const input: WorkflowScheduleConfigurationInput = { + displayName: form.displayName, + cronExpression: form.cronExpression, + timezone: form.timezone, + enabled: editingSchedule ? editingSchedule.enabled : form.enabled, + prompt: form.prompt, + }; + try { + if (editingSchedule) { + await workflowScheduleApi.update( + scopeId, + workflowId, + editingSchedule.scheduleId, + input, + ); + toast.success( + t( + 'workflowActivityVNext.schedule.updateAccepted', + 'Schedule update accepted.', + ), + ); + setSurfaceView('detail'); + setEditingSchedule(null); + await refreshSchedules(); + } else { + const receipt = await workflowScheduleApi.create( + scopeId, + workflowId, + input, + ); + setPendingObservationScheduleId(receipt.scheduleId); + setCreationStep('configure'); + setPreview(null); + setEditingSchedule(null); + setSurfaceView(mode === 'modal' ? 'form' : 'list'); + toast.success( + t( + 'workflowActivityVNext.schedule.created', + 'Schedule request accepted. It will appear in the list shortly.', + ), + ); + if (mode === 'modal') onClose(); + void refreshSchedules().catch((error) => { + toast.error(describeError(error)); + }); + } + } catch (error) { + toast.error(describeError(error)); + } finally { + setSaving(false); + } + }; + + const changeState = async ( + schedule: WorkflowScheduleSummary, + action: 'disable' | 'enable' | 'runNow', + ) => { + if (actionScheduleId) return; + setActionScheduleId(schedule.scheduleId); + try { + if (action === 'enable') + await workflowScheduleApi.enable( + scopeId, + workflowId, + schedule.scheduleId, + ); + if (action === 'disable') + await workflowScheduleApi.disable( + scopeId, + workflowId, + schedule.scheduleId, + ); + if (action === 'runNow') + await workflowScheduleApi.runNow( + scopeId, + workflowId, + schedule.scheduleId, + ); + toast.success( + t( + 'workflowActivityVNext.schedule.actionAccepted', + 'Schedule action accepted.', + ), + ); + await refreshSchedules(); + } catch (error) { + toast.error(describeError(error)); + } finally { + setActionScheduleId(null); + } + }; + + const deleteSchedule = async (schedule: WorkflowScheduleSummary) => { + if (actionScheduleId) return; + setActionScheduleId(schedule.scheduleId); + try { + await workflowScheduleApi.delete( + scopeId, + workflowId, + schedule.scheduleId, + ); + toast.success( + t( + 'workflowActivityVNext.schedule.deleteAccepted', + 'Schedule deletion accepted.', + ), + ); + await refreshSchedules(); + setSelectedSchedule(null); + setSurfaceView('list'); + } catch (error) { + toast.error(describeError(error)); + } finally { + setActionScheduleId(null); + } + }; + + const confirmDeleteSchedule = (schedule: WorkflowScheduleSummary) => { + Modal.confirm({ + cancelText: t( + 'workflowActivityVNext.schedule.keepSchedule', + 'Keep schedule', + ), + content: t( + 'workflowActivityVNext.schedule.deleteConfirmDescription', + '{name} will stop running on schedule.', + { name: schedule.displayName }, + ), + okButtonProps: { danger: true }, + okText: t( + 'workflowActivityVNext.schedule.deleteAction', + 'Delete schedule', + ), + onOk: () => deleteSchedule(schedule), + focusable: { autoFocusButton: 'cancel' }, + title: t( + 'workflowActivityVNext.schedule.deleteConfirmTitle', + 'Delete {name}?', + { name: schedule.displayName }, + ), + }); + }; + + const weekdayLabel = (value: WeekdayValue) => + t(`workflowActivityVNext.schedule.weekday.${value}`, value); + + const recurrenceSummary = cronMode + ? t('workflowActivityVNext.schedule.cronSummary', 'Cron: {cron}', { + cron: form.cronExpression, + }) + : repeatPreset === 'hourly' + ? t('workflowActivityVNext.schedule.hourly', 'Every hour') + : repeatPreset === 'weekly' + ? t( + 'workflowActivityVNext.schedule.weeklySummary', + 'Every {day} at {time}', + { day: weekdayLabel(weeklyDay), time: repeatTime }, + ) + : repeatPreset === 'monthly' + ? t( + 'workflowActivityVNext.schedule.monthlySummary', + 'Every month on day {day} at {time}', + { day: monthlyDay, time: repeatTime }, + ) + : t( + 'workflowActivityVNext.schedule.repeatSummary', + '{repeat} at {time}', + { + repeat: + repeatPreset === 'weekdays' + ? t('workflowActivityVNext.schedule.weekdays', 'Weekdays') + : t('workflowActivityVNext.schedule.daily', 'Every day'), + time: repeatTime, + }, + ); + + const toggleCronMode = () => { + setPreview(null); + if (cronMode) { + const repeat = repeatFromCron(form.cronExpression); + if (repeat) { + setRepeatPreset(repeat.preset); + setRepeatTime(repeat.time); + setWeeklyDay(repeat.weeklyDay); + setMonthlyDay(repeat.monthlyDay); + } + setForm((current) => ({ + ...current, + cronExpression: cronFromRepeat( + repeat?.preset ?? repeatPreset, + repeat?.time ?? repeatTime, + repeat?.weeklyDay ?? weeklyDay, + repeat?.monthlyDay ?? monthlyDay, + ), + })); + } + setCronMode((current) => !current); + }; + + const returnToScheduleList = () => { + setSelectedSchedule(null); + setEditingSchedule(null); + setDetailTab('overview'); + setSurfaceView('list'); + }; + + const selectedScheduleTitle = selectedSchedule ? ( +
+
+ ) : null; + + const scheduleCollectionTitle = t( + 'workflowActivityVNext.schedule.titleForWorkflow', + 'Schedules for {name}', + { name: workflowName }, + ); + + const surfaceTitle = + selectedScheduleTitle ?? + (surfaceView === 'list' + ? scheduleCollectionTitle + : creationStep === 'review' + ? t('workflowActivityVNext.schedule.reviewTitle', 'Review schedule') + : t('workflowActivityVNext.schedule.new', 'New schedule')); + + const workflowContext = ( +
+ + {workflowName} ·{' '} + {t('workflowActivityVNext.schedule.published', 'Published')} + + {editingSchedule ? ( + + {editingSchedule.enabled + ? t('workflowActivityVNext.schedule.enabled', 'Enabled') + : t('workflowActivityVNext.schedule.disabled', 'Disabled')} + + ) : null} +
+ ); + + const timezoneField = ( + + ); + + const formFields = ( + <> + {editingSchedule ? ( +

+ {t('workflowActivityVNext.schedule.editTitle', 'Edit schedule')} +

+ ) : ( + workflowContext + )} +
+ +
+
+

{t('workflowActivityVNext.schedule.howOften', 'How often')}

+ {!cronMode ? ( +
+ + ) : null} + {repeatPreset === 'monthly' ? ( + + {timezoneField} +
+ ) : null} + + {cronMode ? ( +
+ + {timezoneField} +
+ ) : null} + {previewing ? ( +
+ {t( + 'workflowActivityVNext.schedule.previewing', + 'Previewing schedule…', + )} +
+ ) : null} +
+
+ + {!editingSchedule ? ( + + ) : null} +
+ + ); + + const configureView = ( +
+ {formFields} +
+ + +
+
+ ); + + const reviewView = preview ? ( +
+ {workflowContext} +
+
+
+
{t('workflowActivityVNext.schedule.workflow', 'Workflow')}
+
{workflowName}
+
+
+
+ {t( + 'workflowActivityVNext.schedule.scheduleName', + 'Schedule name', + )} +
+
{form.displayName}
+
+
+
{t('workflowActivityVNext.schedule.repeat', 'Repeat')}
+
{recurrenceSummary}
+
+
+
{t('workflowActivityVNext.schedule.timezone', 'Timezone')}
+
{form.timezone}
+
+
+
+ {t( + 'workflowActivityVNext.schedule.enabledAfterCreation', + 'Enabled after creation', + )} +
+
+ {form.enabled + ? t('workflowActivityVNext.common.yes', 'Yes') + : t('workflowActivityVNext.common.no', 'No')} +
+
+
+
+ {t('workflowActivityVNext.schedule.promptReview', 'Run input')} +
+
+ {form.prompt.trim() + ? form.prompt + : t('workflowActivityVNext.schedule.noPrompt', 'No prompt')} +
+
+
+
+ + {t( + 'workflowActivityVNext.schedule.nextFiveFires', + 'Next five fire times', + )} + +
    + {preview.nextFireTimes.map((fireAt) => ( +
  1. + +
  2. + ))} +
+
+
+
+ + +
+
+ ) : null; + + const listView = ( +
+
+ + + +
+ {schedules.isPending ? ( +
+

+ {t('workflowActivityVNext.schedule.loading', 'Loading schedules…')} +

+
+ ) : schedules.isError ? ( + void schedules.refetch()} + > + {t('workflowActivityVNext.common.retry', 'Retry')} + + } + /> + ) : schedules.data?.items.length ? ( +
+ {schedules.data.items.map((schedule) => ( + + ))} +
+ ) : ( +
+

+ {t('workflowActivityVNext.schedule.empty', 'No schedules yet')} +

+

+ {t( + 'workflowActivityVNext.schedule.emptyDescription', + 'Create a recurring schedule for this published Workflow.', + )} +

+
+ )} + {scheduleListRefreshing ? ( + + ) : null} +
+ ); + + const activityHref = scheduleDetail.data + ? `${buildWorkflowActivitySectionHref(scopeId, 'activity')}?${new URLSearchParams( + [ + ['workflowId', workflowId], + ['schedule', scheduleDetail.data.schedule.scheduleId], + ], + ).toString()}` + : null; + + const historyView = scheduleDetail.data ? ( +
+
+
+

+ {t( + 'workflowActivityVNext.schedule.recentAttempts', + 'Recent attempts', + )} +

+

+ {t( + 'workflowActivityVNext.schedule.historyDescription', + 'Schedule attempts can fail before a Workflow Run exists.', + )} +

+
+ {activityHref ? ( + + {t( + 'workflowActivityVNext.schedule.viewRelatedRuns', + 'View related runs in Activity', + )} + + ) : null} +
+ {scheduleDetail.data.recentFires.length ? ( +
+ + + + + + + + + + + {scheduleDetail.data.recentFires.map((fire) => { + const outcome = getScheduleAttemptOutcome(fire); + const failed = outcome === 'failed'; + const source = fire.manual + ? t('workflowActivityVNext.schedule.manual', 'Manual') + : t('workflowActivityVNext.schedule.scheduled', 'Scheduled'); + const formattedScheduledAt = formatScheduleDate( + fire.scheduledFireAt, + scheduleDetail.data.schedule.timezone, + ); + const runActorId = fire.runActorId.trim(); + const attemptHref = runActorId + ? buildWorkflowActivityRunHref(scopeId, runActorId, { + workflowId, + schedule: scheduleDetail.data.schedule.scheduleId, + }) + : null; + const attemptLabel = attemptHref + ? t( + 'workflowActivityVNext.schedule.openRunAria', + 'Open Run created by the Schedule attempt at {date}', + { date: formattedScheduledAt }, + ) + : null; + return ( + + + + + + + ); + })} + +
+ {t( + 'workflowActivityVNext.schedule.scheduledTime', + 'Scheduled time', + )} + + {t('workflowActivityVNext.schedule.source', 'Source')} + + {t( + 'workflowActivityVNext.schedule.outcome', + 'Schedule outcome', + )} + + {t( + 'workflowActivityVNext.schedule.completedTime', + 'Completed time', + )} +
+ {attemptHref && attemptLabel ? ( + + + {attemptLabel} + + + ) : null} + + {source} +
+ + {getScheduleAttemptOutcomeLabel(outcome)} + + {failed ? ( + <> +

+ {t( + fire.manual + ? 'workflowActivityVNext.schedule.manualAttemptFailed' + : 'workflowActivityVNext.schedule.scheduledAttemptFailed', + fire.manual + ? 'The manual attempt could not start the Workflow.' + : 'The scheduled attempt could not start the Workflow.', + )} +

+
event.stopPropagation()} + onKeyDown={(event) => event.stopPropagation()} + > + + {t( + 'workflowActivityVNext.schedule.technicalDetails', + 'Technical details', + )} + + {fire.error} +
+ + ) : null} +
+
+ +
+
+ ) : ( +
+

+ {t('workflowActivityVNext.schedule.noAttempts', 'No attempts yet')} +

+
+ )} +
+ ) : null; + + const detailView = ( +
+ setDetailTab(key as ScheduleDetailTab)} + /> + {scheduleDetail.isPending ? ( +
+

+ {t( + detailTab === 'history' + ? 'workflowActivityVNext.schedule.historyLoading' + : 'workflowActivityVNext.schedule.detailLoading', + detailTab === 'history' + ? 'Loading history…' + : 'Loading schedule details…', + )} +

+
+ ) : scheduleDetail.isError ? ( + void scheduleDetail.refetch()} + > + {t('workflowActivityVNext.common.retry', 'Retry')} + + } + description={scheduleErrorDescription(scheduleDetail.error)} + showIcon + title={t( + detailTab === 'history' + ? 'workflowActivityVNext.schedule.historyLoadFailed' + : 'workflowActivityVNext.schedule.detailLoadFailed', + detailTab === 'history' + ? "History couldn't be loaded" + : "Schedule couldn't be loaded", + )} + type="error" + /> + ) : scheduleDetail.data ? ( + detailTab === 'overview' ? ( +
+
+ + {scheduleDetail.data.schedule.enabled + ? t('workflowActivityVNext.schedule.enabled', 'Enabled') + : t('workflowActivityVNext.schedule.paused', 'Paused')} + +

+ {scheduleRecurrenceSummary( + scheduleDetail.data.schedule.cronExpression, + )} +

+

+ + {t('workflowActivityVNext.schedule.timezone', 'Timezone')} + {' '} + {scheduleDetail.data.schedule.timezone} +

+
+
+ + + + ) : ( + + ), + key: 'toggle', + label: scheduleDetail.data.schedule.enabled + ? t('workflowActivityVNext.schedule.pause', 'Pause') + : t('workflowActivityVNext.schedule.enable', 'Enable'), + }, + { type: 'divider' }, + { + danger: true, + icon: , + key: 'delete', + label: t( + 'workflowActivityVNext.schedule.deleteAction', + 'Delete schedule', + ), + }, + ], + onClick: ({ key }) => { + if (key === 'delete') { + confirmDeleteSchedule(scheduleDetail.data.schedule); + return; + } + void changeState( + scheduleDetail.data.schedule, + scheduleDetail.data.schedule.enabled + ? 'disable' + : 'enable', + ); + }, + }} + trigger={['click']} + > + + +
+
+
+
+ {t( + 'workflowActivityVNext.schedule.nextScheduled', + 'Next scheduled', + )} +
+
+ {scheduleDetail.data.schedule.enabled && + scheduleDetail.data.schedule.nextFireAt + ? formatScheduleDate( + scheduleDetail.data.schedule.nextFireAt, + scheduleDetail.data.schedule.timezone, + ) + : t( + 'workflowActivityVNext.schedule.noUpcomingAttempt', + 'No upcoming attempt', + )} +
+
+
+
+ {t( + 'workflowActivityVNext.schedule.lastAttempt', + 'Last attempt', + )} +
+
+ {formatScheduleDate( + scheduleDetail.data.schedule.lastFireAt, + scheduleDetail.data.schedule.timezone, + )} + {scheduleDetail.data.recentFires[0] ? ( + + {' · '} + {getScheduleAttemptOutcomeLabel( + getScheduleAttemptOutcome( + scheduleDetail.data.recentFires[0], + ), + )} + + ) : null} +
+
+
+
+ {t( + 'workflowActivityVNext.schedule.totalAttempts', + 'Total attempts', + )} +
+
{scheduleDetail.data.schedule.fireCount}
+
+
+
+ {t( + 'workflowActivityVNext.schedule.failedAttempts', + 'Failed attempts', + )} +
+
{scheduleDetail.data.schedule.failureCount}
+
+
+ {scheduleDetail.data.schedule.prompt.trim() ? ( +
+

+ {t( + 'workflowActivityVNext.schedule.promptReview', + 'Run input', + )} +

+

{scheduleDetail.data.schedule.prompt}

+
+ ) : null} +
+ + {t( + 'workflowActivityVNext.schedule.advancedDetails', + 'Advanced details', + )} + +
+
+
{t('workflowActivityVNext.schedule.runs', 'Runs')}
+
+ {scheduleRecurrenceSummary( + scheduleDetail.data.schedule.cronExpression, + )} +
+
+
+
+ {t( + 'workflowActivityVNext.schedule.technicalFormat', + 'Technical format', + )} +
+
+ {scheduleDetail.data.schedule.cronExpression} +
+
+
+
+
+ ) : ( + historyView + ) + ) : null} + {scheduleDetailRefreshing ? ( + + ) : null} +
+ ); + + const activeBody = !available ? ( + + ) : surfaceView === 'list' ? ( + listView + ) : surfaceView === 'detail' ? ( + detailView + ) : creationStep === 'review' ? ( + reviewView + ) : ( + configureView + ); + + if (mode === 'panel') { + return ( + + {activeBody} + + ); + } + + return ( + + {activeBody} + + ); +}; + +export default WorkflowScheduleSurface; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx index 05467456b4..ea5ce92f63 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx @@ -1,4 +1,5 @@ import { + CalendarOutlined, CopyOutlined, DeleteOutlined, EditOutlined, @@ -33,6 +34,7 @@ import { } from '../navigation'; import TableScrollRegion from '../TableScrollRegion'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; +import WorkflowScheduleSurface from './WorkflowScheduleSurface'; import { canArchiveWorkflow, isWorkflowArchived, @@ -173,6 +175,8 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => { const [deleteFailed, setDeleteFailed] = React.useState(false); const [deleteSucceeded, setDeleteSucceeded] = React.useState(false); const [deleting, setDeleting] = React.useState(false); + const [scheduleTarget, setScheduleTarget] = + React.useState(null); const catalogue = useInfiniteQuery({ queryKey: [ 'workflow-activity-vnext', @@ -672,7 +676,13 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => { - +
+ + + + + + + + + + + + + ); +} + +export default function ChannelsPage({ + scopeId, +}: { + readonly scopeId: string; +}) { + const registrations = useChannelRegistrations(scopeId); + const toast = useConsoleToast(); + const queryClient = useQueryClient(); + const [refreshing, setRefreshing] = React.useState(false); + const onboardingUrl = getChannelOnboardingUrl(); + async function refresh() { + setRefreshing(true); + try { + const [result] = await Promise.all([ + registrations.refetch(), + queryClient.refetchQueries({ + queryKey: ['channels', scopeId, 'status'], + type: 'active', + }), + ]); + if (result.isError) + toast.error( + t('channels.error.refresh', 'Could not refresh channels. Try again.'), + ); + } finally { + setRefreshing(false); + } + } + return ( + + +

+ {t( + 'channels.intro', + 'Once connected, talk to your Aevatar bot directly from the channel.', + )} +

+
+

+ {t('channels.available', 'Available channels')} +

+
+ {platforms.map((platform) => { + const available = platform === 'lark' || platform === 'telegram'; + return ( +
+
+ + + {available + ? t('channels.availableNow', 'Available') + : t('channels.soon', 'Soon')} + +
+

{platformName(platform)}

+

+ {t( + `channels.platform.${platform}.description`, + { + lark: 'Connect your bot to chats and groups.', + feishu: 'Feishu, using the same local flow.', + telegram: + 'Connect with a BotFather bot token. Webhook setup is handled for you.', + discord: 'Server and direct-message support.', + slack: 'Workspace bot support.', + }[platform], + )} +

+ {available && onboardingUrl ? ( + + {t('channels.connect', 'Connect')} + + ) : ( + + {available + ? t('channels.setupUnavailable', 'Setup unavailable') + : t('channels.soon', 'Soon')} + + )} +
+ ); + })} +
+
+
+
+
+

+ {t('channels.connected', 'Connected')} + {registrations.data ? ( + + {registrations.data.length} + + ) : null} +

+

+ {t( + 'channels.connectedDescription', + 'Channels connected to your account.', + )} +

+
+
+ {registrations.isPending ? ( + + ) : registrations.isError && !registrations.data ? ( + void registrations.refetch()} + /> + ) : registrations.data?.length ? ( +
+
@@ -852,6 +862,23 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => { 'View activity', )} + {isPublished && !isArchived ? ( + + ) : null} = ({ scopeId }) => { )}

+ setScheduleTarget(null)} + open={Boolean(scheduleTarget)} + scopeId={scopeId} + workflowId={scheduleTarget?.workflowId ?? ''} + workflowName={scheduleTarget?.name ?? ''} + /> ); }; diff --git a/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.test.ts b/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.test.ts new file mode 100644 index 0000000000..7e684bc442 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.test.ts @@ -0,0 +1,304 @@ +import { persistAuthSession } from '@/shared/auth/session'; +import { workflowScheduleApi } from './workflowScheduleApi'; + +describe('workflowScheduleApi', () => { + const originalFetch = global.fetch; + + beforeEach(() => { + window.localStorage.clear(); + persistAuthSession({ + tokens: { + accessToken: 'access-token', + tokenType: 'Bearer', + expiresIn: 3600, + expiresAt: Date.now() + 3_600_000, + }, + user: { sub: 'user-1' }, + }); + }); + + afterEach(() => { + global.fetch = originalFetch; + jest.restoreAllMocks(); + window.localStorage.clear(); + }); + + function createSummary(overrides: Record = {}) { + return { + scheduleId: 'schedule-alpha', + displayName: 'Daily workflow run', + targetKind: 'ServiceInvocation', + targetActorId: 'actor-alpha', + payloadTypeUrl: 'type.googleapis.com/aevatar.ChatRequestEvent', + serviceKey: 'scope-alpha:default:default:service-alpha', + serviceId: 'service-alpha', + serviceEndpointId: 'chat', + prompt: 'Run the workflow', + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + enabled: true, + createdAt: '2026-08-20T08:00:00Z', + updatedAt: '2026-08-20T08:00:00Z', + nextFireAt: '2026-08-21T01:17:00Z', + lastFireAt: null, + lastTargetActorId: '', + lastCommandId: '', + lastCorrelationId: '', + lastError: '', + fireCount: 0, + failureCount: 0, + headers: {}, + scheduleActorId: 'schedule-actor-alpha', + scheduleKind: 'Workflow', + deleted: false, + ...overrides, + }; + } + + function createReceipt() { + return { + scheduleId: 'schedule-alpha', + scheduleActorId: 'schedule-actor-alpha', + accepted: true, + commandId: 'command-alpha', + correlationId: 'correlation-alpha', + ackedAt: '2026-08-20T08:00:00Z', + ackStage: 'accepted', + }; + } + + function createRunNowReceipt() { + return { + ...createReceipt(), + scheduledFireAt: '2026-08-20T08:01:00Z', + idempotencyKey: 'schedule-alpha:manual:1', + }; + } + + function mockJson(body: unknown) { + const fetchMock = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + json: async () => body, + } as Response); + global.fetch = fetchMock as typeof global.fetch; + return fetchMock; + } + + it('lists exact workflow schedules without service or owner query fields', async () => { + const fetchMock = mockJson({ + items: [createSummary()], + nextCursor: null, + totalCount: 1, + }); + + await expect( + workflowScheduleApi.list('scope/alpha', 'wf+alpha', { + includeTotalCount: true, + take: 25, + }), + ).resolves.toEqual({ + items: [expect.objectContaining({ scheduleId: 'schedule-alpha' })], + nextCursor: null, + totalCount: 1, + }); + + expect(fetchMock.mock.calls[0]?.[0]).toBe( + '/api/scopes/scope%2Falpha/workflows/wf%2Balpha/schedules?includeTotalCount=true&take=25', + ); + }); + + it('uses workflow-scoped preview and create payloads', async () => { + const fetchMock = mockJson({ + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + nextFireTimes: ['2026-08-21T01:17:00Z'], + }); + + await workflowScheduleApi.preview('scope-alpha', 'wf-alpha', { + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + count: 1, + }); + expect(fetchMock.mock.calls[0]?.[0]).toBe( + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/preview', + ); + expect(fetchMock.mock.calls[0]?.[1]).toEqual( + expect.objectContaining({ method: 'POST' }), + ); + expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({ + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + count: 1, + }); + + fetchMock.mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createReceipt(), + } as Response); + await workflowScheduleApi.create('scope-alpha', 'wf-alpha', { + displayName: 'Daily workflow run', + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + enabled: true, + prompt: 'Run the workflow', + }); + expect(fetchMock.mock.calls[1]?.[0]).toBe( + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules', + ); + expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({ + displayName: 'Daily workflow run', + cronExpression: '17 9 * * *', + timezone: 'Asia/Shanghai', + enabled: true, + prompt: 'Run the workflow', + headers: {}, + }); + }); + + it('maps the scheduled dispatch target actor to the authoritative Run destination', async () => { + mockJson({ + schedule: createSummary(), + recentFires: [ + { + scheduledFireAt: '2026-08-20T08:00:00Z', + completedAt: '2026-08-20T08:01:00Z', + idempotencyKey: 'schedule-alpha:fire:1', + targetActorId: 'run-alpha', + error: '', + manual: false, + }, + ], + }); + + await expect( + workflowScheduleApi.get('scope-alpha', 'wf-alpha', 'schedule-alpha'), + ).resolves.toEqual({ + schedule: expect.objectContaining({ scheduleId: 'schedule-alpha' }), + recentFires: [expect.objectContaining({ runActorId: 'run-alpha' })], + }); + }); + + it('keeps detail and mutation routes under the exact workflow identity', async () => { + const fetchMock = mockJson({ + schedule: createSummary(), + recentFires: [], + }); + fetchMock.mockReset(); + fetchMock + .mockResolvedValueOnce({ + ok: true, + status: 200, + json: async () => ({ + schedule: createSummary(), + recentFires: [ + { + ScheduledFireAt: '2026-08-20T08:00:00Z', + CompletedAt: '2026-08-20T08:01:00Z', + IdempotencyKey: 'schedule-alpha:fire:1', + RunActorId: 'run-alpha', + Error: '', + Manual: false, + }, + { + ScheduledFireAt: '2026-08-20T09:00:00Z', + CompletedAt: '2026-08-20T09:01:00Z', + IdempotencyKey: 'schedule-alpha:fire:2', + RunActorId: null, + Error: 'Run identity was not recorded', + Manual: false, + }, + { + ScheduledFireAt: '2026-08-20T10:00:00Z', + CompletedAt: '2026-08-20T10:01:00Z', + IdempotencyKey: 'schedule-alpha:fire:3', + Error: '', + Manual: false, + }, + ], + }), + } as Response) + .mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createReceipt(), + } as Response) + .mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createReceipt(), + } as Response) + .mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createReceipt(), + } as Response) + .mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createRunNowReceipt(), + } as Response) + .mockResolvedValueOnce({ + ok: true, + status: 202, + json: async () => createReceipt(), + } as Response); + + const detail = await workflowScheduleApi.get( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + ); + await workflowScheduleApi.update( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + { + displayName: 'Updated workflow run', + cronExpression: '0 10 * * 1', + timezone: 'Asia/Shanghai', + enabled: true, + }, + ); + await workflowScheduleApi.enable( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + 'resume', + ); + await workflowScheduleApi.disable( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + ); + await workflowScheduleApi.runNow( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + ); + await workflowScheduleApi.delete( + 'scope-alpha', + 'wf-alpha', + 'schedule-alpha', + 'remove schedule', + ); + + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha', + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha', + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha:enable', + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha:disable', + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha:run-now', + '/api/scopes/scope-alpha/workflows/wf-alpha/schedules/schedule-alpha?reason=remove+schedule', + ]); + expect( + fetchMock.mock.calls.slice(1).every(([, init]) => init?.method), + ).toBe(true); + expect(detail.recentFires).toEqual([ + expect.objectContaining({ runActorId: 'run-alpha' }), + expect.objectContaining({ runActorId: '' }), + expect.objectContaining({ runActorId: '' }), + ]); + }); +}); diff --git a/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.ts b/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.ts new file mode 100644 index 0000000000..56fa8b8c5b --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/workflowScheduleApi.ts @@ -0,0 +1,492 @@ +import { jsonBody, requestJson, withQuery } from '@/shared/api/http/client'; +import { + expectArray, + expectRecord, + readBoolean, + readNullableString, + readNumber, + readOptionalArray, + readString, +} from './http/decoders'; + +export type WorkflowScheduleConfigurationInput = { + readonly scheduleId?: string; + readonly displayName: string; + readonly cronExpression: string; + readonly timezone: string; + readonly enabled: boolean; + readonly prompt?: string; + readonly headers?: Readonly>; +}; + +export type WorkflowSchedulePreviewInput = { + readonly cronExpression: string; + readonly timezone: string; + readonly count?: number; + readonly fromUtc?: string; +}; + +export type WorkflowScheduleListQuery = { + readonly cursor?: string; + readonly includeTotalCount?: boolean; + readonly take?: number; +}; + +export type WorkflowScheduleSummary = { + readonly scheduleId: string; + readonly displayName: string; + readonly prompt: string; + readonly cronExpression: string; + readonly timezone: string; + readonly enabled: boolean; + readonly createdAt: string; + readonly updatedAt: string; + readonly nextFireAt: string | null; + readonly lastFireAt: string | null; + readonly fireCount: number; + readonly failureCount: number; +}; + +export type WorkflowScheduleFire = { + readonly scheduledFireAt: string; + readonly completedAt: string; + readonly idempotencyKey: string; + readonly runActorId: string; + readonly error: string; + readonly manual: boolean; +}; + +export type WorkflowScheduleDetail = { + readonly schedule: WorkflowScheduleSummary; + readonly recentFires: readonly WorkflowScheduleFire[]; +}; + +export type WorkflowSchedulePreview = { + readonly cronExpression: string; + readonly timezone: string; + readonly nextFireTimes: readonly string[]; +}; + +export type WorkflowScheduleMutationReceipt = { + readonly scheduleId: string; + readonly accepted: boolean; +}; + +export type WorkflowScheduleRunNowReceipt = WorkflowScheduleMutationReceipt & { + readonly scheduledFireAt: string; +}; + +export type WorkflowScheduleListResult = { + readonly items: readonly WorkflowScheduleSummary[]; + readonly nextCursor: string | null; + readonly totalCount: number | null; +}; + +export type WorkflowScheduleApi = { + readonly list: ( + scopeId: string, + workflowId: string, + query?: WorkflowScheduleListQuery, + ) => Promise; + readonly get: ( + scopeId: string, + workflowId: string, + scheduleId: string, + ) => Promise; + readonly preview: ( + scopeId: string, + workflowId: string, + input: WorkflowSchedulePreviewInput, + ) => Promise; + readonly create: ( + scopeId: string, + workflowId: string, + input: WorkflowScheduleConfigurationInput, + ) => Promise; + readonly update: ( + scopeId: string, + workflowId: string, + scheduleId: string, + input: WorkflowScheduleConfigurationInput, + ) => Promise; + readonly enable: ( + scopeId: string, + workflowId: string, + scheduleId: string, + reason?: string, + ) => Promise; + readonly disable: ( + scopeId: string, + workflowId: string, + scheduleId: string, + reason?: string, + ) => Promise; + readonly runNow: ( + scopeId: string, + workflowId: string, + scheduleId: string, + ) => Promise; + readonly delete: ( + scopeId: string, + workflowId: string, + scheduleId: string, + reason?: string, + ) => Promise; +}; + +function readDateTimeString( + record: Record, + keys: [string, string], + label: string, +): string { + const value = record[keys[0]] ?? record[keys[1]]; + if (value instanceof Date) return value.toISOString(); + if (typeof value === 'string') return value; + throw new Error(`${label} must be a string.`); +} + +function readNullableDateTimeString( + record: Record, + keys: [string, string], +): string | null { + const value = record[keys[0]] ?? record[keys[1]]; + if (value === null || value === undefined) return null; + if (value instanceof Date) return value.toISOString(); + if (typeof value === 'string') return value; + throw new Error(`${keys[0]} must be a string or null.`); +} + +function decodeWorkflowScheduleSummary( + value: unknown, + label = 'WorkflowScheduleSummary', +): WorkflowScheduleSummary { + const record = expectRecord(value, label); + return { + scheduleId: readString( + record, + ['scheduleId', 'ScheduleId'], + `${label}.scheduleId`, + ), + displayName: readString( + record, + ['displayName', 'DisplayName'], + `${label}.displayName`, + ), + prompt: + readNullableString(record, ['prompt', 'Prompt'], `${label}.prompt`) ?? '', + cronExpression: readString( + record, + ['cronExpression', 'CronExpression'], + `${label}.cronExpression`, + ), + timezone: readString(record, ['timezone', 'Timezone'], `${label}.timezone`), + enabled: readBoolean(record, ['enabled', 'Enabled'], `${label}.enabled`), + createdAt: readDateTimeString( + record, + ['createdAt', 'CreatedAt'], + `${label}.createdAt`, + ), + updatedAt: readDateTimeString( + record, + ['updatedAt', 'UpdatedAt'], + `${label}.updatedAt`, + ), + nextFireAt: readNullableDateTimeString(record, [ + 'nextFireAt', + 'NextFireAt', + ]), + lastFireAt: readNullableDateTimeString(record, [ + 'lastFireAt', + 'LastFireAt', + ]), + fireCount: readNumber( + record, + ['fireCount', 'FireCount'], + `${label}.fireCount`, + ), + failureCount: readNumber( + record, + ['failureCount', 'FailureCount'], + `${label}.failureCount`, + ), + }; +} + +function decodeWorkflowScheduleDetail(value: unknown): WorkflowScheduleDetail { + const record = expectRecord(value, 'WorkflowScheduleDetail'); + return { + schedule: decodeWorkflowScheduleSummary(record.schedule ?? record.Schedule), + recentFires: readOptionalArray( + record, + ['recentFires', 'RecentFires'], + 'WorkflowScheduleDetail.recentFires', + (entry, label) => { + const entryLabel = label ?? 'WorkflowScheduleDetail.recentFires[]'; + const fire = expectRecord(entry, entryLabel); + return { + scheduledFireAt: readDateTimeString( + fire, + ['scheduledFireAt', 'ScheduledFireAt'], + `${entryLabel}.scheduledFireAt`, + ), + completedAt: readDateTimeString( + fire, + ['completedAt', 'CompletedAt'], + `${entryLabel}.completedAt`, + ), + idempotencyKey: readString( + fire, + ['idempotencyKey', 'IdempotencyKey'], + `${entryLabel}.idempotencyKey`, + ), + runActorId: + readNullableString( + fire, + ['runActorId', 'RunActorId'], + `${entryLabel}.runActorId`, + ) ?? + readNullableString( + fire, + ['targetActorId', 'TargetActorId'], + `${entryLabel}.targetActorId`, + ) ?? + '', + error: readString(fire, ['error', 'Error'], `${entryLabel}.error`), + manual: readBoolean( + fire, + ['manual', 'Manual'], + `${entryLabel}.manual`, + ), + }; + }, + ), + }; +} + +function decodeWorkflowScheduleList( + value: unknown, +): WorkflowScheduleListResult { + const record = expectRecord(value, 'WorkflowScheduleListResult'); + const nextCursor = record.nextCursor ?? record.NextCursor; + const totalCount = record.totalCount ?? record.TotalCount; + return { + items: expectArray( + record.items ?? record.Items, + 'WorkflowScheduleListResult.items', + decodeWorkflowScheduleSummary, + ), + nextCursor: + nextCursor === null || nextCursor === undefined + ? null + : readString( + { value: nextCursor }, + 'value', + 'WorkflowScheduleListResult.nextCursor', + ), + totalCount: + totalCount === null || totalCount === undefined + ? null + : readNumber( + { value: totalCount }, + 'value', + 'WorkflowScheduleListResult.totalCount', + ), + }; +} + +function decodeWorkflowSchedulePreview( + value: unknown, +): WorkflowSchedulePreview { + const record = expectRecord(value, 'WorkflowSchedulePreview'); + return { + cronExpression: readString( + record, + ['cronExpression', 'CronExpression'], + 'WorkflowSchedulePreview.cronExpression', + ), + timezone: readString( + record, + ['timezone', 'Timezone'], + 'WorkflowSchedulePreview.timezone', + ), + nextFireTimes: expectArray( + record.nextFireTimes ?? record.NextFireTimes, + 'WorkflowSchedulePreview.nextFireTimes', + (entry, label) => { + if (typeof entry === 'string') return entry; + if (entry instanceof Date) return entry.toISOString(); + throw new Error(`${label} must be a string.`); + }, + ), + }; +} + +function decodeWorkflowScheduleReceipt( + value: unknown, +): WorkflowScheduleMutationReceipt { + const record = expectRecord(value, 'WorkflowScheduleMutationReceipt'); + return { + scheduleId: readString( + record, + ['scheduleId', 'ScheduleId'], + 'WorkflowScheduleMutationReceipt.scheduleId', + ), + accepted: readBoolean( + record, + ['accepted', 'Accepted'], + 'WorkflowScheduleMutationReceipt.accepted', + ), + }; +} + +function decodeWorkflowScheduleRunNowReceipt( + value: unknown, +): WorkflowScheduleRunNowReceipt { + const record = expectRecord(value, 'WorkflowScheduleRunNowReceipt'); + return { + ...decodeWorkflowScheduleReceipt(value), + scheduledFireAt: readDateTimeString( + record, + ['scheduledFireAt', 'ScheduledFireAt'], + 'WorkflowScheduleRunNowReceipt.scheduledFireAt', + ), + }; +} + +function requireIdentifier(value: string, label: string): string { + const normalized = value.trim(); + if (!normalized) throw new Error(`${label} is required.`); + return encodeURIComponent(normalized); +} + +function route(scopeId: string, workflowId: string): string { + return `/api/scopes/${requireIdentifier(scopeId, 'scopeId')}/workflows/${requireIdentifier(workflowId, 'workflowId')}/schedules`; +} + +function encodeConfiguration(input: WorkflowScheduleConfigurationInput) { + const displayName = input.displayName.trim(); + const cronExpression = input.cronExpression.trim(); + const timezone = input.timezone.trim(); + if (!displayName) throw new Error('Schedule displayName is required.'); + if (!cronExpression) throw new Error('Schedule cronExpression is required.'); + if (!timezone) throw new Error('Schedule timezone is required.'); + + return { + ...(input.scheduleId?.trim() + ? { scheduleId: input.scheduleId.trim() } + : {}), + displayName, + cronExpression, + timezone, + enabled: input.enabled, + prompt: input.prompt?.trim() ?? '', + headers: input.headers ?? {}, + }; +} + +function encodePreview(input: WorkflowSchedulePreviewInput) { + const cronExpression = input.cronExpression.trim(); + const timezone = input.timezone.trim(); + if (!cronExpression) throw new Error('Schedule cronExpression is required.'); + if (!timezone) throw new Error('Schedule timezone is required.'); + return { + cronExpression, + timezone, + count: input.count ?? 5, + ...(input.fromUtc?.trim() ? { fromUtc: input.fromUtc.trim() } : {}), + }; +} + +function mutationBody(reason?: string) { + const normalizedReason = reason?.trim() ?? ''; + return normalizedReason ? { reason: normalizedReason } : {}; +} + +export const workflowScheduleApi: WorkflowScheduleApi = { + list(scopeId, workflowId, query) { + return requestJson( + withQuery(route(scopeId, workflowId), { + cursor: query?.cursor, + includeTotalCount: query?.includeTotalCount, + take: query?.take, + }), + decodeWorkflowScheduleList, + ); + }, + + get(scopeId, workflowId, scheduleId) { + return requestJson( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}`, + decodeWorkflowScheduleDetail, + ); + }, + + preview(scopeId, workflowId, input) { + return requestJson( + `${route(scopeId, workflowId)}/preview`, + decodeWorkflowSchedulePreview, + { + method: 'POST', + ...jsonBody(encodePreview(input)), + }, + ); + }, + + create(scopeId, workflowId, input) { + return requestJson( + route(scopeId, workflowId), + decodeWorkflowScheduleReceipt, + { + method: 'POST', + ...jsonBody(encodeConfiguration(input)), + }, + ); + }, + + update(scopeId, workflowId, scheduleId, input) { + return requestJson( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}`, + decodeWorkflowScheduleReceipt, + { + method: 'PUT', + ...jsonBody(encodeConfiguration({ ...input, scheduleId })), + }, + ); + }, + + enable(scopeId, workflowId, scheduleId, reason) { + return requestJson( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}:enable`, + decodeWorkflowScheduleReceipt, + { method: 'POST', ...jsonBody(mutationBody(reason)) }, + ); + }, + + disable(scopeId, workflowId, scheduleId, reason) { + return requestJson( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}:disable`, + decodeWorkflowScheduleReceipt, + { method: 'POST', ...jsonBody(mutationBody(reason)) }, + ); + }, + + runNow(scopeId, workflowId, scheduleId) { + return requestJson( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}:run-now`, + decodeWorkflowScheduleRunNowReceipt, + { method: 'POST', ...jsonBody({}) }, + ); + }, + + delete(scopeId, workflowId, scheduleId, reason) { + const normalizedReason = reason?.trim() ?? ''; + return requestJson( + withQuery( + `${route(scopeId, workflowId)}/${requireIdentifier(scheduleId, 'scheduleId')}`, + normalizedReason ? { reason: normalizedReason } : undefined, + ), + decodeWorkflowScheduleReceipt, + { method: 'DELETE', ...jsonBody(mutationBody(reason)) }, + ); + }, +}; diff --git a/apps/aevatar-console-web/src/shared/ui/AevatarLoading.test.tsx b/apps/aevatar-console-web/src/shared/ui/AevatarLoading.test.tsx index e89f681b2f..fc29b1d949 100644 --- a/apps/aevatar-console-web/src/shared/ui/AevatarLoading.test.tsx +++ b/apps/aevatar-console-web/src/shared/ui/AevatarLoading.test.tsx @@ -2,6 +2,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { AevatarLoadingDots, + AevatarLoadingOverlay, AevatarLoadingPulseDot, AevatarPageLoading, AevatarStreamingCursor, @@ -64,4 +65,20 @@ describe('AevatarLoading', () => { 'aevatar-loading-visually-hidden', ); }); + + it('uses the shared loading language for committed-content overlays', () => { + const { container } = render( + , + ); + + const status = screen.getByRole('status', { + name: 'Refreshing run details', + }); + expect(status).toHaveAttribute('aria-busy', 'true'); + expect(status).toHaveClass('aevatar-loading-overlay'); + expect(container.querySelectorAll('.aevatar-loading-dot')).toHaveLength(3); + expect(screen.getByText('Refreshing run details')).toHaveClass( + 'aevatar-loading-visually-hidden', + ); + }); }); diff --git a/apps/aevatar-console-web/src/shared/ui/AevatarLoading.tsx b/apps/aevatar-console-web/src/shared/ui/AevatarLoading.tsx index 8e272bffa5..fb3d22db91 100644 --- a/apps/aevatar-console-web/src/shared/ui/AevatarLoading.tsx +++ b/apps/aevatar-console-web/src/shared/ui/AevatarLoading.tsx @@ -193,3 +193,33 @@ export function AevatarPageLoading({ ); } + +export type AevatarLoadingOverlayProps = { + ariaLabel: string; + className?: string; + style?: React.CSSProperties; +}; + +export function AevatarLoadingOverlay({ + ariaLabel, + className, + style, +}: AevatarLoadingOverlayProps): React.ReactElement { + return ( +
+ + {ariaLabel} +
+ ); +} From 80bce2ee1e4c1458706320dd9d30b0e7d651fdec Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:05:40 +0800 Subject: [PATCH 068/167] Clarify workflow publication failures (#3548) --- .../workflowActivityVNextMessages.en-US.ts | 8 ++ .../workflowActivityVNextMessages.zh-CN.ts | 7 ++ .../workflow-activity-vnext/index.test.tsx | 58 +++++++++- .../workflows/WorkflowEditorPage.tsx | 106 +++++++++++++----- 4 files changed, 145 insertions(+), 34 deletions(-) diff --git a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts index da8950764f..7f35f3aeb8 100644 --- a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts @@ -143,6 +143,14 @@ const workflowActivityVNextMessages = { 'workflowActivityVNext.publish.revisionId': 'Revision ID', 'workflowActivityVNext.publish.readinessIssues': 'Publish readiness issues', 'workflowActivityVNext.publish.success': 'Workflow published', + 'workflowActivityVNext.publish.submissionFailed': + "Workflow couldn't be submitted", + 'workflowActivityVNext.publish.submissionFailedDescription': + 'Review the error and try publishing again.', + 'workflowActivityVNext.publish.validationRejected': + "Workflow isn't ready to publish", + 'workflowActivityVNext.publish.validationRejectedDescription': + 'Fix the workflow configuration below, then publish again.', 'workflowActivityVNext.publish.resolvePublication': 'Resolve the current publication status before publishing again.', 'workflowActivityVNext.publish.saveBeforePublishing': diff --git a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts index 04fef2f7ed..abe014810b 100644 --- a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts @@ -144,6 +144,13 @@ const workflowActivityVNextMessages: Record = 'workflowActivityVNext.publish.revisionId': '版本 ID', 'workflowActivityVNext.publish.readinessIssues': '发布就绪问题', 'workflowActivityVNext.publish.success': '工作流已发布', + 'workflowActivityVNext.publish.submissionFailed': '无法提交工作流发布', + 'workflowActivityVNext.publish.submissionFailedDescription': + '请查看错误后重试发布。', + 'workflowActivityVNext.publish.validationRejected': + '工作流尚未满足发布条件', + 'workflowActivityVNext.publish.validationRejectedDescription': + '请修正下方工作流配置,然后重新发布。', 'workflowActivityVNext.publish.resolvePublication': '再次发布前请先处理当前发布状态。', 'workflowActivityVNext.publish.saveBeforePublishing': diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx index b79a663295..f4756b378c 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx @@ -268,7 +268,15 @@ jest.mock('@umijs/max', () => ({ })); jest.mock('@/shared/studio/api', () => ({ - isStudioApiErrorCode: () => false, + isStudioApiErrorCode: (error: unknown, status: number, code: string) => + Boolean( + error && + typeof error === 'object' && + 'status' in error && + error.status === status && + 'code' in error && + error.code === code, + ), isStudioApiStatus: (error: unknown, status: number) => Boolean( error && @@ -2720,7 +2728,7 @@ describe('Workflow Activity vNext editor', () => { fireEvent.click(await screen.findByRole('button', { name: 'Publish' })); expect( - await screen.findByText("Publication couldn't be confirmed"), + await screen.findByText("Workflow couldn't be submitted"), ).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Retry' })).toBeEnabled(); expect(mockStudioApi.publishWorkflow).toHaveBeenCalledTimes(1); @@ -3001,6 +3009,48 @@ describe('Workflow Activity vNext editor', () => { expect(mockConsoleToast.error).toHaveBeenCalledTimes(1); }); + it('presents a publication validation rejection as workflow configuration to fix', async () => { + arrangeSavedDraftPublication(); + const validationMessage = + "Step 'draft_weekly_report' can invoke llm_call and must declare an explicit allowed_tools scope on the step or its target role."; + mockStudioApi.previewExplicitRequests.mockRejectedValue( + Object.assign(new Error(validationMessage), { + code: 'INVALID_USER_WORKFLOW_REQUEST', + status: 400, + }), + ); + + renderWithQueryClient(); + + fireEvent.click(await screen.findByRole('button', { name: 'Publish' })); + + expect( + await screen.findByText("Workflow isn't ready to publish"), + ).toBeInTheDocument(); + expect( + screen.getByText( + 'Fix the workflow configuration below, then publish again.', + ), + ).toBeInTheDocument(); + const technicalDetails = screen.getByText('Technical details'); + expect(screen.getByText(validationMessage)).not.toBeVisible(); + fireEvent.click(technicalDetails); + expect(screen.getByText(validationMessage)).toBeVisible(); + expect( + screen.queryByText("Publication couldn't be confirmed"), + ).not.toBeInTheDocument(); + expect(mockStudioApi.publishWorkflow).not.toHaveBeenCalled(); + expect(mockScopesApi.getWorkflowDetail).toHaveBeenCalledTimes(1); + await waitFor(() => + expect(mockConsoleToast.error).toHaveBeenCalledWith( + "Workflow isn't ready to publish", + expect.objectContaining({ + key: expect.stringContaining('workflow-publication:'), + }), + ), + ); + }); + it('creates a fresh revision before retrying a publication that was not accepted', async () => { arrangeSavedDraftPublication(); const receiptRevisionId = 'rev-receipt-alpha'; @@ -3057,11 +3107,11 @@ describe('Workflow Activity vNext editor', () => { fireEvent.click(await screen.findByRole('button', { name: 'Publish' })); expect( - await screen.findByText("Publication couldn't be confirmed"), + await screen.findByText("Workflow couldn't be submitted"), ).toBeInTheDocument(); await waitFor(() => expect(mockConsoleToast.error).toHaveBeenCalledWith( - "Publication couldn't be confirmed", + "Workflow couldn't be submitted", expect.objectContaining({ key: expect.stringContaining('workflow-publication:'), }), diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx index d333a64aeb..e656b11ff5 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx @@ -12,15 +12,15 @@ import { import { useQuery } from '@tanstack/react-query'; import { Alert, Button, Input, Modal, Segmented, Space } from 'antd'; import React from 'react'; -import AevatarTooltip from '@/shared/ui/AevatarTooltip'; import WorkflowStudioEditorSurface from '@/pages/team-member-workflow-studio/components/WorkflowStudioEditorSurface'; import { scopesApi } from '@/shared/api/scopesApi'; import { formatUtcDateTime } from '@/shared/datetime/dateTime'; import { t } from '@/shared/i18n/messages'; import { getLocationSnapshot, history } from '@/shared/navigation/history'; -import { studioApi } from '@/shared/studio/api'; +import { isStudioApiErrorCode, studioApi } from '@/shared/studio/api'; import { buildWorkflowExecutionNodeSnapshots } from '@/shared/studio/execution'; import { createWorkflowRevisionIdentityCandidate } from '@/shared/studio/explicitRequestConfirmation'; +import AevatarTooltip from '@/shared/ui/AevatarTooltip'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { adaptActivityRunToExecutionLogs, @@ -187,6 +187,14 @@ const WorkflowEditorPage: React.FC<{ const publicationPhase = publicationReceipt ? publication.phase : publicationStage; + const publicationValidationRejected = + publicationReceipt === null && + publicationStage === 'failed' && + isStudioApiErrorCode( + publicationError, + 400, + 'INVALID_USER_WORKFLOW_REQUEST', + ); React.useEffect(() => { const pendingFocus = pendingRunConsoleFocusRef.current; if (!pendingFocus) return; @@ -246,10 +254,15 @@ const WorkflowEditorPage: React.FC<{ ].join(':'); } else if (publicationStage === 'failed') { toastIntent = 'error'; - toastMessage = t( - 'workflowActivityVNext.publish.failed', - "Publication couldn't be confirmed", - ); + toastMessage = publicationValidationRejected + ? t( + 'workflowActivityVNext.publish.validationRejected', + "Workflow isn't ready to publish", + ) + : t( + 'workflowActivityVNext.publish.submissionFailed', + "Workflow couldn't be submitted", + ); toastKey = [ 'workflow-publication', publicationGenerationRef.current, @@ -265,7 +278,13 @@ const WorkflowEditorPage: React.FC<{ if (toastIntent === 'success') toast.success(toastMessage, { key: toastKey }); else toast.error(toastMessage, { key: toastKey }); - }, [publication.phase, publicationReceipt, publicationStage, toast]); + }, [ + publication.phase, + publicationReceipt, + publicationStage, + publicationValidationRejected, + toast, + ]); const publicationObserved = publication.phase === 'observed'; const restoredPublishedInvocationTarget = restorePublishedInvocationTarget( activeScopeId, @@ -1169,27 +1188,44 @@ const WorkflowEditorPage: React.FC<{ ) : undefined } description={ - <> - {publicationPhase === 'unauthorized' - ? t( - 'workflowActivityVNext.publish.unauthorizedDescription', - 'Sign in again to check this publication.', - ) - : publicationPhase === 'forbidden' - ? t( - 'workflowActivityVNext.publish.forbiddenDescription', - "You don't have access to check this publication.", - ) - : t( - 'workflowActivityVNext.publish.failedDescription', - 'Resolve the error or try publishing again.', - )} - {publicationError || publication.error ? ( + publicationValidationRejected && publicationError ? ( + <> + {t( + 'workflowActivityVNext.publish.validationRejectedDescription', + 'Fix the workflow configuration below, then publish again.', + )} - {errorMessage(publicationError ?? publication.error)} + {errorMessage(publicationError)} - ) : null} - + + ) : ( + <> + {publicationPhase === 'unauthorized' + ? t( + 'workflowActivityVNext.publish.unauthorizedDescription', + 'Sign in again to check this publication.', + ) + : publicationPhase === 'forbidden' + ? t( + 'workflowActivityVNext.publish.forbiddenDescription', + "You don't have access to check this publication.", + ) + : publicationReceipt + ? t( + 'workflowActivityVNext.publish.failedDescription', + 'Resolve the error or try publishing again.', + ) + : t( + 'workflowActivityVNext.publish.submissionFailedDescription', + 'Review the error and try publishing again.', + )} + {publicationError || publication.error ? ( + + {errorMessage(publicationError ?? publication.error)} + + ) : null} + + ) } title={ publicationPhase === 'unauthorized' @@ -1202,10 +1238,20 @@ const WorkflowEditorPage: React.FC<{ 'workflowActivityVNext.state.forbidden', "You don't have access to this workspace", ) - : t( - 'workflowActivityVNext.publish.failed', - "Publication couldn't be confirmed", - ) + : publicationValidationRejected + ? t( + 'workflowActivityVNext.publish.validationRejected', + "Workflow isn't ready to publish", + ) + : publicationReceipt + ? t( + 'workflowActivityVNext.publish.failed', + "Publication couldn't be confirmed", + ) + : t( + 'workflowActivityVNext.publish.submissionFailed', + "Workflow couldn't be submitted", + ) } id="workflow-publication-status" showIcon From 31de43220ad0006f9869dc484cdf134c600f656b Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:54:31 +0800 Subject: [PATCH 069/167] Document workflow vNext home navigation --- .../plans/2026-08-31-workflow-vnext-home.md | 80 +++++++++++++++++++ .../2026-08-31-workflow-vnext-home-design.md | 45 +++++++++++ 2 files changed, 125 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-31-workflow-vnext-home.md create mode 100644 docs/superpowers/specs/2026-08-31-workflow-vnext-home-design.md diff --git a/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md new file mode 100644 index 0000000000..625a14e14b --- /dev/null +++ b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md @@ -0,0 +1,80 @@ +# Workflow vNext Home Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make the fixed-scope Workflow Activity vNext workflow catalogue the console home without modifying the existing Teams page implementation or breaking explicit login return targets. + +**Architecture:** Keep `src/shared/navigation/consoleHome.ts` as the single semantic owner of default console navigation. Make the unscoped home aliases in `config/routes.ts` consume that constant, while leaving scoped Team routes and all Teams page files unchanged. + +**Tech Stack:** TypeScript, Umi route configuration, Jest, Biome + +--- + +### Task 1: Lock The Home Navigation Contract + +**Files:** +- Modify: `apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts` +- Modify: `apps/aevatar-console-web/src/routesConfig.test.ts` + +- [ ] **Step 1: Update the shared home test** + +Assert that `WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE`, `getConsoleHomeRoute()`, and `CONSOLE_HOME_ROUTE` all equal the fixed workflow catalogue path. + +- [ ] **Step 2: Update the route contract test** + +Import `CONSOLE_HOME_ROUTE` and assert that `/`, `/overview`, and `/scopes` redirect to it, `/scopes` is hidden and does not mount `./teams`, and `/scopes/:scopeId/teams` still mounts `./teams`. + +- [ ] **Step 3: Run the changed tests and verify RED** + +Run: + +```bash +pnpm exec jest src/shared/navigation/consoleHome.test.ts src/routesConfig.test.ts --runInBand +``` + +Expected: FAIL because the shared home still equals `/scopes` and `/scopes` still mounts `./teams`. + +### Task 2: Point Default Navigation At Workflow Activity vNext + +**Files:** +- Modify: `apps/aevatar-console-web/src/shared/navigation/consoleHome.ts` +- Modify: `apps/aevatar-console-web/config/routes.ts` + +- [ ] **Step 1: Define the fixed Workflow Activity home** + +Replace the Teams home constant with a named Workflow Activity vNext home constant and retain `CONSOLE_HOME_ROUTE` as its public default-navigation alias. + +- [ ] **Step 2: Redirect unscoped home aliases** + +Import `CONSOLE_HOME_ROUTE` into `config/routes.ts`. Redirect `/`, `/overview`, and `/scopes` to it; make `/scopes` hidden and remove its Teams name/component fields. + +- [ ] **Step 3: Run the changed tests and verify GREEN** + +Run: + +```bash +pnpm exec jest src/shared/navigation/consoleHome.test.ts src/routesConfig.test.ts --runInBand +``` + +Expected: PASS with both changed test suites green. + +### Task 3: Focused Validation And Delivery + +**Files:** +- Review all files changed from `feat/2026-08-04_workflow-activity-vnext` + +- [ ] **Step 1: Analyze affected frontend scope** + +Run the `frontend-incremental-pr` analyzer with base `feat/2026-08-04_workflow-activity-vnext` and use its output to select exact tests and changed-file static checks. + +- [ ] **Step 2: Run focused tests and static checks** + +Run changed test files explicitly, related tests for changed production files, and Biome only for analyzer-reported static-check files. Do not run a full frontend test, typecheck, or build. + +- [ ] **Step 3: Review semantic drift and the full diff** + +Search the changed navigation area for stale Teams-home assertions, confirm no `src/pages/teams/**` file changed, and inspect `git diff --check` plus the complete base diff. + +- [ ] **Step 4: Commit and deliver** + +Stage only this task's tracked files, commit with an imperative single-purpose message, push the branch, and create a PR targeting `feat/2026-08-04_workflow-activity-vnext`. Record exact focused commands and state that the full frontend suite/build is delegated to GitHub CI. diff --git a/docs/superpowers/specs/2026-08-31-workflow-vnext-home-design.md b/docs/superpowers/specs/2026-08-31-workflow-vnext-home-design.md new file mode 100644 index 0000000000..a829fe898c --- /dev/null +++ b/docs/superpowers/specs/2026-08-31-workflow-vnext-home-design.md @@ -0,0 +1,45 @@ +# Workflow vNext Home Design + +## Product Decision + +The console currently treats the unscoped Teams entry as home, but this deployment expects the fixed-scope Workflow Activity vNext workflow catalogue to be home. The mismatch is an ownership and runtime mismatch: default navigation points to the old Teams resolver even though the intended first product surface is Workflow Activity vNext. + +The fixed console home is: + +`/scopes/ccb108c4-dcb3-473a-a0f7-e9859bb2f2a0/workflow-activity-vnext/workflows` + +## Scope + +- Make the shared console fallback route resolve to the fixed Workflow Activity vNext catalogue. +- Redirect `/`, `/overview`, and `/scopes` to that shared home route. +- Preserve explicit safe post-login deep links. A user who was sent to login from another protected page still returns to that page. +- Keep the existing Teams page implementation and canonical scoped Team routes unchanged. +- Do not add a duplicate Workflow Activity page or copy its UI into the old home page. + +## Semantic Ownership + +`CONSOLE_HOME_ROUTE` remains the single product-level owner of default console navigation. Route configuration consumes that constant rather than restating the fixed scope path. Authentication, callback fallback, protected-route fallback, root navigation, and not-found recovery therefore continue to agree on the same home. + +The old Teams page remains owned by scoped Team routes such as `/scopes/:scopeId/teams`. The technical `/scopes` entry no longer renders it and is hidden from the generated menu, so it cannot be presented as "My Teams" while opening Workflow Activity vNext. + +## Navigation Behavior + +| Entry | Result | +| --- | --- | +| Successful login without an explicit safe return target | Fixed Workflow Activity vNext workflow catalogue | +| `/` | Fixed Workflow Activity vNext workflow catalogue | +| `/overview` | Fixed Workflow Activity vNext workflow catalogue | +| `/scopes` | Fixed Workflow Activity vNext workflow catalogue | +| Login initiated from a safe protected deep link | Original deep link | +| `/scopes/:scopeId/teams` | Existing scoped Teams page | + +## Failure And Security Behavior + +No authentication or return-target validation logic changes. External, protocol-relative, login, and callback return targets continue to fall back through `sanitizeReturnTo`; safe internal paths continue to be preserved. + +## Verification + +- Update the home-navigation unit test to assert the exact fixed path. +- Update the route contract test to assert that `/`, `/overview`, and `/scopes` redirect to the shared home constant and that `/scopes` no longer mounts the Teams component. +- Keep an assertion that `/scopes/:scopeId/teams` still mounts the existing Teams page. +- Run only changed/related Jest tests and changed-file Biome checks, per the personal frontend validation policy. Full frontend validation remains owned by GitHub CI. From 6d2c957679fcb4e1e74101c0ba63377b62cd1689 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:06:20 +0800 Subject: [PATCH 070/167] Make workflow vNext the console home --- apps/aevatar-console-web/config/routes.ts | 12 ++++++------ .../src/routesConfig.test.ts | 15 ++++++++++----- .../src/shared/navigation/consoleHome.test.ts | 17 ++++++++++------- .../src/shared/navigation/consoleHome.ts | 7 ++++--- .../plans/2026-08-31-workflow-vnext-home.md | 18 +++++++++--------- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/apps/aevatar-console-web/config/routes.ts b/apps/aevatar-console-web/config/routes.ts index a320019e4a..ecb17b3256 100644 --- a/apps/aevatar-console-web/config/routes.ts +++ b/apps/aevatar-console-web/config/routes.ts @@ -10,6 +10,8 @@ * @param icon 配置路由的图标,取值参考 https://ant.design/components/icon-cn, 注意去除风格后缀和大小写,如想要配置图标为 则取值应为 stepBackward 或 StepBackward,如想要配置图标为 则取值应为 user 或者 User * @doc https://umijs.org/docs/guides/routes */ +import { CONSOLE_HOME_ROUTE } from '../src/shared/navigation/consoleHome'; + export default [ { path: '/login', @@ -23,7 +25,7 @@ export default [ }, { path: '/overview', - redirect: '/scopes', + redirect: CONSOLE_HOME_ROUTE, hideInMenu: true, }, { @@ -35,10 +37,8 @@ export default [ }, { path: '/scopes', - name: 'My Teams', - component: './teams', - menuGroupKey: 'teams', - hideInMenu: false, + redirect: CONSOLE_HOME_ROUTE, + hideInMenu: true, }, { path: '/scopes/:scopeId/workflow-activity-vnext', @@ -313,7 +313,7 @@ export default [ }, { path: '/', - redirect: '/scopes', + redirect: CONSOLE_HOME_ROUTE, }, { component: '404', diff --git a/apps/aevatar-console-web/src/routesConfig.test.ts b/apps/aevatar-console-web/src/routesConfig.test.ts index c115494903..b3958ff3e1 100644 --- a/apps/aevatar-console-web/src/routesConfig.test.ts +++ b/apps/aevatar-console-web/src/routesConfig.test.ts @@ -1,3 +1,5 @@ +import { CONSOLE_HOME_ROUTE } from './shared/navigation/consoleHome'; + describe('console routes', () => { function loadRoutes(): typeof import('../config/routes').default { let loadedRoutes!: typeof import('../config/routes').default; @@ -43,20 +45,23 @@ describe('console routes', () => { jest.resetModules(); }); - it('keeps scoped Team navigation as the default resource route model', () => { + it('routes console home to Workflow Activity while preserving scoped Teams', () => { const routes = loadRoutes(); expect(findRoute(routes, '/chat').hideInMenu).toBe(false); expect(findRoute(routes, '/chat').name).toBe('Chat'); expect(findRoute(routes, '/chat').menuGroupKey).toBe('chat'); expect(findRoute(routes, '/chat').icon).toBeUndefined(); - expect(findRoute(routes, '/scopes').hideInMenu).toBe(false); + expect(findRoute(routes, '/scopes').hideInMenu).toBe(true); expect(findRoute(routes, '/studio').hideInMenu).toBe(true); expect(findRoute(routes, '/runtime/runs').hideInMenu).toBeUndefined(); expect(findRoute(routes, '/runtime/runs').menuGroupKey).toBe('platform'); expect(findRoute(routes, '/scopes/overview').hideInMenu).toBe(true); - expect(findRoute(routes, '/scopes').name).toBe('My Teams'); - expect(findRoute(routes, '/scopes').component).toBe('./teams'); + expect(findRoute(routes, '/scopes').name).toBeUndefined(); + expect(findRoute(routes, '/scopes').component).toBeUndefined(); + for (const path of ['/', '/overview', '/scopes']) { + expect(findRoute(routes, path).redirect).toBe(CONSOLE_HOME_ROUTE); + } expect(findRouteIndex(routes, '/chat')).toBeLessThan( findRouteIndex(routes, '/scopes'), ); @@ -236,6 +241,6 @@ describe('console routes', () => { expect(findRoute(routes, '/workflows').redirect).toBe('/runtime/workflows'); expect(findRoute(routes, '/runs').redirect).toBe('/runtime/runs'); - expect(findRoute(routes, '/').redirect).toBe('/scopes'); + expect(findRoute(routes, '/').redirect).toBe(CONSOLE_HOME_ROUTE); }); }); diff --git a/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts b/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts index 0d149fced0..c28de6801b 100644 --- a/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts +++ b/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts @@ -1,8 +1,8 @@ -describe("consoleHome", () => { - function loadModule(): typeof import("./consoleHome") { - let loadedModule!: typeof import("./consoleHome"); +describe('consoleHome', () => { + function loadModule(): typeof import('./consoleHome') { + let loadedModule!: typeof import('./consoleHome'); jest.isolateModules(() => { - loadedModule = require("./consoleHome") as typeof import("./consoleHome"); + loadedModule = require('./consoleHome') as typeof import('./consoleHome'); }); return loadedModule; } @@ -11,10 +11,13 @@ describe("consoleHome", () => { jest.resetModules(); }); - it("uses the teams home route by default", () => { + it('uses the fixed-scope Workflow Activity catalogue as console home', () => { const module = loadModule(); + const expectedRoute = + '/scopes/ccb108c4-dcb3-473a-a0f7-e9859bb2f2a0/workflow-activity-vnext/workflows'; - expect(module.getConsoleHomeRoute()).toBe("/scopes"); - expect(module.CONSOLE_HOME_ROUTE).toBe("/scopes"); + expect(module.WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE).toBe(expectedRoute); + expect(module.getConsoleHomeRoute()).toBe(expectedRoute); + expect(module.CONSOLE_HOME_ROUTE).toBe(expectedRoute); }); }); diff --git a/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts b/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts index 7b02dbf97b..06c5247065 100644 --- a/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts +++ b/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts @@ -1,7 +1,8 @@ -export const TEAMS_HOME_ROUTE = "/scopes"; +export const WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE = + '/scopes/ccb108c4-dcb3-473a-a0f7-e9859bb2f2a0/workflow-activity-vnext/workflows'; export function getConsoleHomeRoute(): string { - return TEAMS_HOME_ROUTE; + return WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE; } -export const CONSOLE_HOME_ROUTE = TEAMS_HOME_ROUTE; +export const CONSOLE_HOME_ROUTE = WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE; diff --git a/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md index 625a14e14b..a85f5689af 100644 --- a/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md +++ b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md @@ -16,15 +16,15 @@ - Modify: `apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts` - Modify: `apps/aevatar-console-web/src/routesConfig.test.ts` -- [ ] **Step 1: Update the shared home test** +- [x] **Step 1: Update the shared home test** Assert that `WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE`, `getConsoleHomeRoute()`, and `CONSOLE_HOME_ROUTE` all equal the fixed workflow catalogue path. -- [ ] **Step 2: Update the route contract test** +- [x] **Step 2: Update the route contract test** Import `CONSOLE_HOME_ROUTE` and assert that `/`, `/overview`, and `/scopes` redirect to it, `/scopes` is hidden and does not mount `./teams`, and `/scopes/:scopeId/teams` still mounts `./teams`. -- [ ] **Step 3: Run the changed tests and verify RED** +- [x] **Step 3: Run the changed tests and verify RED** Run: @@ -40,15 +40,15 @@ Expected: FAIL because the shared home still equals `/scopes` and `/scopes` stil - Modify: `apps/aevatar-console-web/src/shared/navigation/consoleHome.ts` - Modify: `apps/aevatar-console-web/config/routes.ts` -- [ ] **Step 1: Define the fixed Workflow Activity home** +- [x] **Step 1: Define the fixed Workflow Activity home** Replace the Teams home constant with a named Workflow Activity vNext home constant and retain `CONSOLE_HOME_ROUTE` as its public default-navigation alias. -- [ ] **Step 2: Redirect unscoped home aliases** +- [x] **Step 2: Redirect unscoped home aliases** Import `CONSOLE_HOME_ROUTE` into `config/routes.ts`. Redirect `/`, `/overview`, and `/scopes` to it; make `/scopes` hidden and remove its Teams name/component fields. -- [ ] **Step 3: Run the changed tests and verify GREEN** +- [x] **Step 3: Run the changed tests and verify GREEN** Run: @@ -63,15 +63,15 @@ Expected: PASS with both changed test suites green. **Files:** - Review all files changed from `feat/2026-08-04_workflow-activity-vnext` -- [ ] **Step 1: Analyze affected frontend scope** +- [x] **Step 1: Analyze affected frontend scope** Run the `frontend-incremental-pr` analyzer with base `feat/2026-08-04_workflow-activity-vnext` and use its output to select exact tests and changed-file static checks. -- [ ] **Step 2: Run focused tests and static checks** +- [x] **Step 2: Run focused tests and static checks** Run changed test files explicitly, related tests for changed production files, and Biome only for analyzer-reported static-check files. Do not run a full frontend test, typecheck, or build. -- [ ] **Step 3: Review semantic drift and the full diff** +- [x] **Step 3: Review semantic drift and the full diff** Search the changed navigation area for stale Teams-home assertions, confirm no `src/pages/teams/**` file changed, and inspect `git diff --check` plus the complete base diff. From 2080e2fdbd1bbeb00b585a1844b4c8fadfc2a869 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:14:38 +0800 Subject: [PATCH 071/167] Complete workflow vNext home delivery plan --- docs/superpowers/plans/2026-08-31-workflow-vnext-home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md index a85f5689af..44695200b4 100644 --- a/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md +++ b/docs/superpowers/plans/2026-08-31-workflow-vnext-home.md @@ -75,6 +75,6 @@ Run changed test files explicitly, related tests for changed production files, a Search the changed navigation area for stale Teams-home assertions, confirm no `src/pages/teams/**` file changed, and inspect `git diff --check` plus the complete base diff. -- [ ] **Step 4: Commit and deliver** +- [x] **Step 4: Commit and deliver** Stage only this task's tracked files, commit with an imperative single-purpose message, push the branch, and create a PR targeting `feat/2026-08-04_workflow-activity-vnext`. Record exact focused commands and state that the full frontend suite/build is delegated to GitHub CI. From cf4d2cc53ac7a6248bdf701ea25b56b8fee31696 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 12:58:16 +0800 Subject: [PATCH 072/167] Fix stale published run status --- .../MemberPublishedRunsReplay.tsx | 21 +++++++-- .../runtime-published-runs/index.test.tsx | 46 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/runtime-published-runs/MemberPublishedRunsReplay.tsx b/apps/aevatar-console-web/src/pages/runtime-published-runs/MemberPublishedRunsReplay.tsx index 2815a776e3..940d017610 100644 --- a/apps/aevatar-console-web/src/pages/runtime-published-runs/MemberPublishedRunsReplay.tsx +++ b/apps/aevatar-console-web/src/pages/runtime-published-runs/MemberPublishedRunsReplay.tsx @@ -1093,9 +1093,13 @@ const MemberPublishedRunsReplay: React.FC = ({ retry: false, }); + const selectedAuditSummary = + auditQuery.data?.summary?.runId === selectedRunId + ? auditQuery.data.summary + : null; const selectedRun = + selectedAuditSummary ?? selectedCatalogRun ?? - auditQuery.data?.summary ?? (normalizedInitialRunId ? createFallbackRunSummary({ actorId: selectedRunActorId, @@ -1105,8 +1109,19 @@ const MemberPublishedRunsReplay: React.FC = ({ }) : null); const audit = auditQuery.data?.audit ?? null; - const displayRuns = - runs.length || !selectedRun ? runs : [selectedRun]; + const displayRuns = React.useMemo(() => { + if (!runs.length) { + return selectedRun ? [selectedRun] : runs; + } + + if (!selectedAuditSummary) { + return runs; + } + + return runs.map((run) => + run.runId === selectedAuditSummary.runId ? selectedAuditSummary : run, + ); + }, [runs, selectedAuditSummary, selectedRun]); const showReplaySkeleton = Boolean( runsQuery.isLoading || (selectedRun && auditQuery.isLoading), ); diff --git a/apps/aevatar-console-web/src/pages/runtime-published-runs/index.test.tsx b/apps/aevatar-console-web/src/pages/runtime-published-runs/index.test.tsx index 1ec0d10739..ff4bcbaa58 100644 --- a/apps/aevatar-console-web/src/pages/runtime-published-runs/index.test.tsx +++ b/apps/aevatar-console-web/src/pages/runtime-published-runs/index.test.tsx @@ -350,6 +350,52 @@ describe("TeamMemberPublishedRunsPage", () => { ); }); + it("uses the completed audit status when the run catalog is still running", async () => { + window.history.replaceState( + {}, + "", + "/scopes/scope-1/teams/team-1/members/m-alpha/runs?runId=run-1", + ); + mockedScopeRuntimeApi.listMemberRuns.mockResolvedValueOnce({ + displayName: "Alpha Workflow", + memberId: "m-alpha", + publishedServiceId: "svc-alpha", + publishedServiceKey: "scope-1:default:default:svc-alpha", + runs: [ + { + actorId: "actor://scope-1/run-1", + bindingUpdatedAt: "2026-06-22T01:00:00Z", + boundAt: "2026-06-22T01:00:00Z", + completedSteps: 0, + completionStatus: "running", + definitionActorId: "definition://alpha", + deploymentId: "dep-alpha", + lastError: "", + lastEventId: "evt-1", + lastOutput: "", + lastSuccess: null, + lastUpdatedAt: "2026-06-22T01:00:00Z", + memberId: "m-alpha", + publishedServiceId: "svc-alpha", + revisionId: "rev-alpha", + roleReplyCount: 0, + runId: "run-1", + scopeId: "scope-1", + stateVersion: 1, + totalSteps: 2, + workflowName: "Alpha Workflow", + }, + ], + scopeId: "scope-1", + }); + + renderWithQueryClient(React.createElement(TeamMemberPublishedRunsPage)); + + expect(await screen.findByText("node:config")).toBeTruthy(); + expect(screen.queryByText("Running")).toBeNull(); + expect(screen.getAllByText("Completed").length).toBeGreaterThanOrEqual(2); + }); + it("returns from published runs to the Team members tab", async () => { renderWithQueryClient(React.createElement(TeamMemberPublishedRunsPage)); From bb5ec5005e3e5c67680fdaffd53c3490395caef7 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:35:53 +0800 Subject: [PATCH 073/167] Document guided workflow configuration --- ...-workflow-configuration-guidance-design.md | 406 ++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-02-workflow-configuration-guidance-design.md diff --git a/docs/superpowers/specs/2026-09-02-workflow-configuration-guidance-design.md b/docs/superpowers/specs/2026-09-02-workflow-configuration-guidance-design.md new file mode 100644 index 0000000000..ff47a31464 --- /dev/null +++ b/docs/superpowers/specs/2026-09-02-workflow-configuration-guidance-design.md @@ -0,0 +1,406 @@ +# Workflow Configuration Guidance Design + +## Summary + +The Workflow Activity vNext inspector currently presents runtime configuration as if it were a user task. A novice opening a `tool_call` step sees a required `Tool` text field containing `nyxid_proxy`, but the interface does not explain what the value represents, which values are valid, what action will run, or which inputs that action needs. + +The inspector will instead lead with the user's intent: choose an action, understand whether it is usable, and provide the action's inputs. Runtime details remain inspectable, but no longer occupy the primary configuration path. The first complete specialization is the external-operation form of `tool_call`; every node type also receives a clearer shared inspector hierarchy. + +## Problem Definition + +This is a semantic mismatch across five surfaces, not an isolated copy issue: + +- **Label:** `Tool` names a runtime mechanism rather than the user's action. +- **Placement:** raw implementation details appear before purpose, readiness, and required inputs. +- **Ownership:** the UI asks the user to reconstruct values owned by capability discovery. +- **Contract:** the current frontend draft model carries only `parameters`, even though external-operation identity belongs to the typed step-level `capability` selector. +- **Mental model:** the UI suggests that entering any string is enough to make a tool executable, while the runtime requires an exact discoverable operation and valid authorization/readiness. + +The canonical contract is: + +```yaml +type: tool_call +capability: + nyxid_operation: + user_service_id: us-home-alpha + endpoint_id: list-states +parameters: + tool: nyxid_proxy + arguments: '{"query":{},"headers":{},"response_mode":"text"}' +``` + +`nyxid_proxy` is an internal adapter. `user_service_id` and `endpoint_id` are an exact backend-owned selector. The user-facing concept is the discovered connected-service operation. + +## Design Principles + +1. **Start with the decision the user can make.** The first control answers “What should this step do?” rather than “Which runtime adapter should execute it?” +2. **Never ask users to guess identifiers or credentials.** Exact selectors are copied from backend discovery, and credentials never enter the workflow document. +3. **Reveal requirements progressively.** Required action inputs follow selection; optional behavior and technical details follow afterward. +4. **Report readiness honestly.** Drafts may remain incomplete, but the UI must distinguish ready, setup-required, unavailable, and indeterminate states. +5. **Keep expert access without making expertise mandatory.** Raw JSON and runtime fields remain available under explicit advanced/technical sections. +6. **Preserve the typed identity boundary.** Capability identity belongs to `step.capability`; invocation values belong to `step.parameters.arguments`. +7. **Let the backend own truth.** The frontend consumes typed discovery and readiness contracts and does not fabricate a catalog, infer selectors from labels, or guess operation schemas. + +## Considered Approaches + +### 1. Shared inspector shell with semantic node adapters + +Create one consistent inspector hierarchy, then allow node-specific editors to replace generic runtime fields with user-facing concepts. For external `tool_call`, use backend capability discovery/readiness, an action picker, and contract-driven inputs. + +This is the recommended approach. It fixes the actual semantic problem, preserves a coherent configuration experience across node types, and allows future specializations without duplicating an entire inspector. + +### 2. Copy and generic-schema polish only + +Rename fields, add placeholders, and add helper text to the current schema-driven form. + +This costs less, but it still asks the user to know a valid runtime tool name, cannot enumerate usable actions, cannot distinguish unavailable connections, and cannot generate the selected operation's required inputs. It would make the wrong mental model friendlier without making it correct. + +### 3. A bespoke wizard for every node type + +Replace the inspector with a unique multi-step wizard per primitive. + +This can maximize tailoring, but duplicates navigation, validation, discard handling, technical details, and accessibility behavior. It also makes simple nodes unnecessarily slow to configure. The maintenance and consistency cost is not justified. + +## Information Architecture + +### Header + +- Show a human-readable step name as the title. Until editable names exist, derive it from the action or the humanized node type. +- Show the node type as secondary context, for example `Tool call`. +- Do not show the raw step ID as the header subtitle; move it to Technical details. +- Keep the close control icon-only with an accessible name and tooltip. + +### Purpose and status + +- Begin the body with one sentence describing what this node type does. +- For external actions, show one compact readiness state: + - `Choose an action`: no selector yet. + - `Checking availability`: readiness request is in progress. + - `Ready`: the selected action is currently usable in the editing context. + - `Needs setup`: the action exists but readiness returns actionable blockers. + - `Unavailable`: the selector is stale, inaccessible, or no longer discoverable. + - `Could not check`: the readiness request failed; this is not misreported as a workflow validation failure. + +### Required settings + +- Required settings appear before all optional fields. +- Every control has a user-oriented label, concise helper text where the expected value is not self-evident, and an example or placeholder based on the selected contract. +- Validation is shown next to the field it concerns. The footer may summarize that the step needs attention, but it does not become the sole error location. + +### Optional settings + +- Optional operation inputs are shown after required inputs. +- When a node type has no optional settings, no empty section is rendered. +- Advanced response behavior appears only when the selected contract offers more than one valid choice. + +### Technical details + +The collapsed Technical details section contains: + +- step ID; +- runtime step type; +- target role; +- next step and branches; +- runtime tool adapter, such as `nyxid_proxy`; +- exact `user_service_id` and `endpoint_id` selector values. + +These values are selectable/copyable but not casually editable in the guided path. + +### Advanced JSON + +- Preserve the existing raw JSON editor as an expert escape hatch. +- Explain its scope as runtime parameters, not the entire step definition. +- Applying raw JSON and returning to guided mode must retain supported values. +- Common external-operation configuration must never require this editor. + +### Footer + +- `Cancel` closes the inspector and retains the existing discard confirmation for unapplied changes. +- The primary action is `Apply step`, because it applies an inspector draft to the workflow document rather than persisting the whole workflow. +- The button remains available for incomplete drafts only when the document can be represented safely. Its nearby status makes clear that incomplete external capability setup will block later publication or execution. + +## External Tool Call Experience + +### Entry states + +The editor distinguishes three tool-call shapes: + +1. **No tool selected:** show an `Action` picker first. Choosing a connected-service operation creates `parameters.tool = "nyxid_proxy"` and the exact typed selector. +2. **Existing `nyxid_proxy`:** resolve its typed selector against discovery and show the selected action. If no selector exists, show `Choose an action`; never present `nyxid_proxy` as a user choice. +3. **Other registered tool:** retain a guided `Tool` field for compatibility in this iteration, with clearer helper text and examples. Do not pretend the external operation catalog represents all internal agent tools. + +### Action picker + +- Label: `Action`. +- Prompt: `Choose what this step should do`. +- Search by operation display name and source/service display text. +- Group or annotate results by connected service/source when that information is available. +- Each option shows the backend-provided `display_name` and one truthful risk label: + - `Read only` when `read_only` is true; + - `Destructive` when `destructive` is true; + - `Writes data` otherwise. +- The selected value is a frontend-stable opaque key derived from the serialized typed selector for UI reconciliation only. On apply, the original selector object returned by discovery is copied without reconstructing IDs from the display name or key. +- An empty catalog says that no connected actions are available and provides backend diagnostics when they include a safe message. +- Discovery failure offers `Retry`; it does not clear an already selected selector. + +### Selection and readiness + +When an action is selected: + +1. The draft stores the exact `capability.nyxid_operation` selector. +2. The draft sets the runtime adapter to `parameters.tool = "nyxid_proxy"`. +3. The frontend requests readiness in interactive execution mode. +4. The response's `selected_selector` must match the requested selector. A mismatch is treated as an invalid response. +5. The frontend uses `selected_capability.nyx_id_user_service` as the operation contract for input rendering. +6. A stale response is ignored when the user has selected another action in the meantime. + +The UI does not request or store bearer tokens, credential IDs, proof fields, source stamps, grants, or admission digests. + +### Operation inputs + +Readiness exposes path, query, and header parameter contracts plus an optional JSON body contract. The editor maps them to a canonical invocation object: + +```json +{ + "path_params": {}, + "query": {}, + "headers": {}, + "body": {}, + "response_mode": "text" +} +``` + +- Only sections required by the operation or containing a value are serialized. +- Required path/query/header properties use individual fields. +- Optional properties render after required properties within their location group. +- String, integer, number, and boolean leaf schemas use an appropriate text, number, or boolean control. +- A finite `allowed_values` set uses a select control. +- Object/array inputs that cannot be represented without losing schema meaning use a labeled JSON editor with local JSON/type validation. +- Workflow expressions such as `${steps.lookup.output}` remain valid string input even for non-string operation schemas; they are not prematurely coerced in the browser. +- Header inputs are operation-declared business headers only. Authentication headers are never shown. +- `response_mode` is hidden when only one mode is allowed and selectable when both text and file artifact are allowed. + +Existing valid `parameters.arguments` are decoded into the generated form. Unknown keys are preserved during guided edits so that opening and applying the inspector does not silently destroy forward-compatible runtime values. Invalid JSON remains visible in Advanced JSON with an inline recovery message; the guided fields do not overwrite it until the user explicitly changes a guided value. + +### Readiness blockers + +- Render every backend `safe_message`; do not derive a diagnosis from enum names alone. +- Show the readiness status in plain language and retain the typed status for deterministic UI behavior. +- Render backend remediation `label` as an action only when `trusted_locator` is a supported, same-product destination. Unknown or external locator schemes remain descriptive text and are never opened blindly. +- `Connect credential`, `Register service`, `Request access`, and similar states are setup issues, not input validation errors. +- Destructive operations remain visibly marked while configuring. This inspector does not grant durable authorization and does not replace execution-time approval. + +## Shared Inspector Metadata + +Add a small presentation metadata registry keyed by step type: + +- humanized label; +- one-sentence purpose; +- optional guided-editor kind. + +This registry is presentation-only. It must not become a second runtime schema or redefine workflow semantics. Unknown node types fall back to the existing humanized type label and a neutral purpose sentence. + +The existing node configuration field schema remains the generic editor for node types without a semantic adapter. Field definitions gain optional helper text where needed, but the shared inspector owns section ordering, status, technical details, and advanced JSON. + +## API Contract + +The frontend must not call agent tools. Add narrow, authenticated host endpoints over the existing application ports: + +### List actions + +```http +GET /api/scopes/{scopeId}/workflow-capabilities +``` + +Response: + +```json +{ + "capabilities": [ + { + "displayName": "PostHog / List dashboards", + "readOnly": true, + "destructive": false, + "selector": { + "kind": "nyxid_operation", + "userServiceId": "us-posthog-alpha", + "endpointId": "list-dashboards" + }, + "source": { + "kind": "nyxid_user_services", + "sourceId": "...", + "sourceVersion": 7, + "observedAt": "...", + "freshUntil": "..." + } + } + ], + "candidateCount": 4, + "rejectedCount": 1, + "diagnostics": [ + { + "code": "unsupported_schema", + "safeMessage": "One operation was omitted because its input schema is unsupported.", + "count": 1 + } + ] +} +``` + +The first frontend specialization filters to `selector.kind = "nyxid_operation"`, while the host mapping remains exhaustive and rejects unsupported/unspecified selector shapes rather than returning ambiguous partial data. + +### Inspect readiness + +```http +POST /api/scopes/{scopeId}/workflow-capabilities:readiness +Content-Type: application/json + +{ + "selector": { + "kind": "nyxid_operation", + "userServiceId": "us-posthog-alpha", + "endpointId": "list-dashboards" + }, + "executionMode": "interactive" +} +``` + +The response exposes: + +- status; +- exact selected selector; +- safe blockers and remediation descriptors; +- source freshness; +- the selected connected-service operation contract, including method/path, typed parameters, request-body schema, response policy, and execution policy. + +HTTP DTOs use explicit camel-case enums rather than protobuf-generated numeric enum values. The frontend decoder validates every object, discriminant, collection, and identity string before the data reaches the inspector. + +Both endpoints: + +- enforce the existing scope access policy; +- build `ExternalWorkflowCapabilityAccessContext` from the authenticated caller and current forwarded credentials; +- delegate discovery/readiness to `IExternalWorkflowCapabilityListPort` and `IExternalWorkflowCapabilityReadinessPort`; +- return only safe contract fields; +- map expected access/validation failures to the host's existing problem response conventions; +- contain no capability-selection business logic in the Host layer. + +## Frontend Document Contract + +Introduce a typed external capability union on `StudioWorkflowStepDocument`: + +```ts +type StudioWorkflowCapability = { + readonly nyxid_operation: { + readonly user_service_id: string; + readonly endpoint_id: string; + }; +}; +``` + +Use document field names exactly as persisted by workflow YAML/JSON. API DTOs remain camelCase and are translated exactly once at the API/document boundary. + +`StudioStepInspectorDraft` gains a `capability` value. `createStepInspectorDraft` clones it from the step; `applyStepInspectorDraft` writes or removes it without routing it through `parametersText`. The inspector change callback accepts the complete inspector draft change needed to update both capability and parameters atomically. + +No variable named `workflowId`, `memberId`, or `publishedServiceId` is reused for selector identity. Values remain explicitly named `userServiceId`, `endpointId`, or `capabilitySelector`. + +## Data Flow + +```mermaid +flowchart LR + A["Open tool-call inspector"] --> B["Load scope capability descriptors"] + B --> C["Choose a user-facing action"] + C --> D["Copy exact typed selector into draft"] + D --> E["Inspect selector readiness"] + E --> F["Render blockers or typed operation inputs"] + F --> G["Serialize declared values as parameters.arguments"] + G --> H["Apply capability and parameters atomically to the step"] +``` + +Discovery is cached per scope through the frontend's existing query/data-fetching convention if one exists on the page. Readiness is keyed by scope, exact selector, and execution mode. Selection changes cancel or logically invalidate older readiness responses. + +## Loading, Empty, and Error States + +- The inspector shell and existing draft values render immediately; discovery never blocks opening the modal. +- While discovery loads, the action control shows a stable-height skeleton/loading row so the layout does not jump. +- A selected operation continues to display from its saved descriptor/selector representation while the latest catalog is loading. +- An empty successful list is distinct from a failed list. +- Discovery and readiness errors are recoverable inline and retain the draft. +- A malformed server response fails closed with a generic safe error and is reported through existing client diagnostics; unchecked data is never rendered. +- Applying another node's generic configuration does not depend on capability endpoints. + +## Accessibility and Responsive Behavior + +- Every field has an associated visible label; helper and error text are connected with `aria-describedby`. +- Status is conveyed by text and icon, never color alone. +- The action picker is fully keyboard navigable and its search results expose risk labels to assistive technology. +- The modal traps focus through the existing Ant Design behavior, returns focus on close, and keeps the close button accessible. +- On narrow viewports, labels and fields use a single column, technical rows wrap, and footer actions remain visible without covering form content. +- Stable control heights prevent loading, badges, and validation text from shifting unrelated controls. +- No text is truncated without a tooltip or an accessible full value. + +## Validation and Test Strategy + +### Backend endpoint tests + +- list endpoint forwards authenticated scope/caller context and maps an exact operation descriptor; +- list endpoint maps safe diagnostics without leaking credentials or deprecated capability payloads; +- readiness endpoint parses the typed selector and interactive execution mode; +- readiness response maps input schemas, response policy, execution policy, blockers, and remediations; +- invalid/unsupported selectors and execution modes fail before the application port is called; +- scope access denial follows the existing endpoint behavior; +- route registration includes both new paths. + +### Frontend model and API tests + +- strict decoders accept canonical list/readiness payloads and reject malformed discriminants, duplicate or empty selector identities, and incomplete schemas; +- draft creation/application round-trips `capability` independently of `parameters`; +- removing a selection removes the typed capability without removing unrelated parameters; +- argument parsing/serialization preserves unknown keys and workflow expressions. + +### Inspector component tests + +- an unconfigured external tool call leads with `Action`, not an editable `nyxid_proxy` field; +- a saved selector resolves to the matching display name; +- selecting an action writes the exact selector and adapter atomically; +- readiness loading, ready, setup-required, unavailable, empty, failure, retry, and stale-response states render correctly; +- required/optional operation inputs map into the canonical arguments object; +- destructive and read-only risk labels are visible; +- generic non-external tools and other node types retain a usable fallback editor; +- technical details contain runtime identities and the header does not expose the raw step ID; +- discard confirmation and Apply step behavior remain intact; +- controls have accessible names and validation associations. + +Local frontend verification remains restricted to dependency-related tests and changed-file static checks. Full frontend suite, typecheck when no affected target exists, and production build are delegated to GitHub CI. + +## Rollout Scope + +This change delivers: + +- the shared inspector information hierarchy and node-purpose presentation; +- typed capability list/readiness REST endpoints; +- strict frontend contracts and draft capability round-trip; +- the complete discovered external-operation editor for direct `tool_call` nodes; +- existing generic configuration and raw JSON as fallbacks. + +The structure permits later guided adapters for `llm_call`, conditionals, loops, and file operations without requiring them in this change. + +## Non-Goals + +- Redesigning the workflow canvas, node library, run console, or whole Workflow Activity vNext shell. +- Creating or editing connected services inside the inspector. +- Inventing an internal agent-tool catalog where no authoritative discovery contract exists. +- Editing authored arbitrary NyxID request selectors in the guided UI. +- Granting durable authorization or bypassing execution-time approval for destructive actions. +- Changing `ToolCallModule` runtime semantics or moving capability identity into parameters. +- Making every workflow primitive bespoke in the first release. + +## Success Criteria + +A first-time user can open an external Tool call step and, without knowing `nyxid_proxy`, `user_service_id`, `endpoint_id`, or raw argument-envelope syntax: + +1. understand what the step is for; +2. choose a recognizable connected-service action; +3. see whether the action is ready or what setup is missing; +4. fill its declared inputs with examples and inline validation; +5. apply the step without exposing credentials or corrupting typed identity; +6. inspect runtime details only when they deliberately open the technical section. From c3522d70e51bfc2d42ac2edde333e22e40039e43 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:42:55 +0800 Subject: [PATCH 074/167] Plan guided workflow configuration --- ...6-09-02-workflow-configuration-guidance.md | 464 ++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-02-workflow-configuration-guidance.md diff --git a/docs/superpowers/plans/2026-09-02-workflow-configuration-guidance.md b/docs/superpowers/plans/2026-09-02-workflow-configuration-guidance.md new file mode 100644 index 0000000000..775c7789f8 --- /dev/null +++ b/docs/superpowers/plans/2026-09-02-workflow-configuration-guidance.md @@ -0,0 +1,464 @@ +# Workflow Configuration Guidance Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the novice-hostile `nyxid_proxy` text field with a guided connected-action picker, readiness feedback, and typed operation inputs while keeping runtime identity in the correct workflow fields. + +**Architecture:** Add two authenticated scope HTTP endpoints that project existing external-capability application ports into strict browser DTOs. Extend the Studio document draft to round-trip typed step capability independently from parameters, then add a focused external Tool call editor inside the existing inspector shell. Generic node configuration remains the fallback, and technical/raw details stay progressively disclosed. + +**Tech Stack:** ASP.NET Core minimal APIs, Protobuf contracts, xUnit/FluentAssertions, React 19, TypeScript, Ant Design, TanStack Query, Jest, Testing Library, Biome. + +--- + +## File Map + +- `src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs`: register list/readiness routes, enforce scope/caller context, and delegate to application ports. +- `src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs`: define narrow HTTP DTOs and exhaustive Protobuf-to-wire mappings. +- `test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs`: verify endpoint authority forwarding, response sanitization, mapping, and invalid input. +- `test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs`: verify both routes are registered. +- `apps/aevatar-console-web/src/shared/studio/models.ts`: define API DTOs and typed workflow capability document shapes. +- `apps/aevatar-console-web/src/shared/studio/api.ts`: strictly decode list/readiness responses and expose scoped API methods. +- `apps/aevatar-console-web/src/shared/studio/api.test.ts`: verify request paths, payloads, canonical responses, and decoder rejection. +- `apps/aevatar-console-web/src/shared/studio/document.ts`: round-trip capability in an inspector draft independently from parameters. +- `apps/aevatar-console-web/src/shared/studio/document.test.ts`: expose identity corruption with different selector/runtime values. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts`: pure selector keys, argument parsing/serialization, and contract field mapping. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts`: verify preservation, expressions, type coercion, and selector identity. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx`: render discovery, readiness, risk, remediation, and generated operation inputs. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx`: verify the novice path and recoverable states. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx`: integrate the semantic editor, improve hierarchy/copy, and apply capability plus parameters atomically. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts`: accept the full configuration change from the inspector. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx`: pass the active scope to the inspector. +- `apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts`: style stable status, picker options, input groups, technical details, and narrow layouts with existing tokens. + +### Task 1: Capability HTTP contract and endpoint tests + +**Files:** +- Create: `src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs` +- Modify: `src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs` +- Test: `test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs` +- Test: `test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs` + +- [ ] **Step 1: Write failing list endpoint tests** + +Add a recording `IExternalWorkflowCapabilityListPort`, construct a discovery result with an exact selector whose IDs differ from its display text, invoke `HandleListWorkflowCapabilitiesAsync`, and assert: + +```csharp +listPort.Request!.Access.ScopeId.Should().Be("user-1"); +listPort.Request.Access.CallerId.Should().Be("caller-alpha"); +body.Should().Contain("\"displayName\":\"PostHog / List dashboards\""); +body.Should().Contain("\"userServiceId\":\"us-posthog-alpha\""); +body.Should().Contain("\"endpointId\":\"list-dashboards\""); +body.Should().NotContain("transient-capability-bearer"); +``` + +Add a forbidden-scope test and assert the recording port is not called. + +- [ ] **Step 2: Write failing readiness endpoint tests** + +Pass this HTTP request: + +```csharp +new WorkflowCapabilityReadinessHttpRequest( + new WorkflowCapabilitySelectorHttpRequest( + "nyxid_operation", + "us-posthog-alpha", + "list-dashboards"), + "interactive") +``` + +Return a readiness contract containing path/query/header parameters, a JSON request body, response modes, write risk, approval, blockers, and remediation. Assert the application request contains the exact selector and the JSON contains explicit string enums and safe messages. + +- [ ] **Step 3: Write failing invalid request tests** + +Cover empty IDs, unsupported selector kind, and execution mode `background`. Assert `400 INVALID_USER_WORKFLOW_REQUEST` and no readiness port invocation. + +- [ ] **Step 4: Write failing route registration assertions** + +Assert the endpoint data source contains: + +```text +/api/scopes/{scopeId}/workflow-capabilities +/api/scopes/{scopeId}/workflow-capabilities:readiness +``` + +- [ ] **Step 5: Run the focused tests and confirm failure** + +Run: + +```bash +dotnet test test/Aevatar.GAgentService.Integration.Tests/Aevatar.GAgentService.Integration.Tests.csproj --nologo --filter "FullyQualifiedName~ScopeWorkflowEndpointsTests|FullyQualifiedName~GAgentServiceHostingServiceCollectionExtensionsTests" +``` + +Expected: compilation/test failure because the endpoint handlers and HTTP contracts do not exist. + +- [ ] **Step 6: Implement the HTTP DTO mapper** + +Create discriminated selector request/response records and response records for descriptors, diagnostics, source stamps, readiness, blockers, remediation, selected operation, schema, response policy, and execution policy. Map every supported enum through exhaustive switch expressions such as: + +```csharp +private static string ToWireValue(NyxIdOperationValueKind value) => value switch +{ + NyxIdOperationValueKind.String => "string", + NyxIdOperationValueKind.Integer => "integer", + NyxIdOperationValueKind.Number => "number", + NyxIdOperationValueKind.Boolean => "boolean", + NyxIdOperationValueKind.Object => "object", + NyxIdOperationValueKind.Array => "array", + _ => "unspecified", +}; +``` + +Reject unspecified/unsupported selector kinds and empty selector identities rather than constructing partial Protobuf messages. Omit credentials, grants, digests not required by input authoring, and deprecated capability payloads. + +- [ ] **Step 7: Implement endpoint handlers** + +Register: + +```csharp +group.MapGet("/{scopeId}/workflow-capabilities", HandleListWorkflowCapabilitiesAsync); +group.MapPost("/{scopeId}/workflow-capabilities:readiness", HandleWorkflowCapabilityReadinessAsync); +``` + +Each handler must run `AevatarScopeAccessGuard`, call `WorkflowCapabilityAdmissionHttpContext.Create`, build `ExternalWorkflowCapabilityAccessContext`, delegate to the appropriate port, and map credential/invalid-operation failures through existing safe responses. + +- [ ] **Step 8: Run the focused backend tests and commit** + +Run the Step 5 command. Expected: PASS. + +Commit: + +```bash +git add src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs +git commit -m "Expose workflow capability guidance endpoints" +``` + +### Task 2: Strict frontend capability API + +**Files:** +- Modify: `apps/aevatar-console-web/src/shared/studio/models.ts` +- Modify: `apps/aevatar-console-web/src/shared/studio/api.ts` +- Test: `apps/aevatar-console-web/src/shared/studio/api.test.ts` + +- [ ] **Step 1: Write failing discovery API tests** + +Mock a canonical list response, call `studioApi.listWorkflowCapabilities('scope-alpha')`, and assert the exact GET path plus the decoded descriptor. Add malformed cases for an unknown selector kind, empty `userServiceId`, and an unsupported source kind; each must reject before returning data. + +- [ ] **Step 2: Write failing readiness API tests** + +Call: + +```ts +studioApi.inspectWorkflowCapabilityReadiness({ + scopeId: 'scope-alpha', + executionMode: 'interactive', + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'list-dashboards', + }, +}); +``` + +Assert the exact POST body and decode nested schemas, parameters, policies, blockers, and remediations. Reject selector mismatch and unsupported readiness/schema enum values. + +- [ ] **Step 3: Run the frontend API test and confirm failure** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/shared/studio/api.test.ts +``` + +Expected: FAIL because the types and methods do not exist. + +- [ ] **Step 4: Add narrow TypeScript contracts and strict decoders** + +Define string unions for selector/source/readiness/schema/location/risk/approval/response enums and readonly interfaces for both responses. Reuse existing `expectRecord`, `expectString`, `decodeArray`, and enum-decoder patterns. Validate non-empty identities and selector equality after decoding readiness. + +- [ ] **Step 5: Add API methods** + +Implement one GET and one POST through `requestDecodedJson`; normalize the scope ID, serialize only the typed selector and execution mode, and never accept credential material in either input type. + +- [ ] **Step 6: Run the focused API test and commit** + +Run the Step 3 command. Expected: PASS. + +Commit: + +```bash +git add apps/aevatar-console-web/src/shared/studio/models.ts apps/aevatar-console-web/src/shared/studio/api.ts apps/aevatar-console-web/src/shared/studio/api.test.ts +git commit -m "Add typed workflow capability client" +``` + +### Task 3: Preserve step capability in inspector drafts + +**Files:** +- Modify: `apps/aevatar-console-web/src/shared/studio/models.ts` +- Modify: `apps/aevatar-console-web/src/shared/studio/document.ts` +- Modify: `apps/aevatar-console-web/src/shared/studio/document.test.ts` +- Modify: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts` + +- [ ] **Step 1: Write the failing round-trip test** + +Use distinct values: + +```ts +const capability = { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, +}; +``` + +Assert `createStepInspectorDraft` clones this value, `applyStepInspectorDraft` writes a changed selector while retaining `parameters.tool = 'nyxid_proxy'`, and setting `capability: null` removes only the capability field. + +- [ ] **Step 2: Run the document test and confirm failure** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects node --runInBand src/shared/studio/document.test.ts +``` + +Expected: FAIL because inspector drafts do not expose capability. + +- [ ] **Step 3: Implement the typed draft boundary** + +Add `StudioWorkflowCapability`, add `capability?: StudioWorkflowCapability | null` to the step document, and add `capability: StudioWorkflowCapability | null` to `StudioStepInspectorDraft`. Clone and validate only the supported `nyxid_operation` document shape; preserve unrelated step fields through existing spreads. + +Change the editor callback from a parameters string to: + +```ts +type StudioStepConfigurationChange = Pick< + StudioStepInspectorDraft, + 'parametersText' | 'capability' +>; +``` + +Apply both fields in one `applyStepInspectorDraft` call before YAML serialization. + +- [ ] **Step 4: Update existing test fixtures explicitly** + +Every literal `StudioStepInspectorDraft` in changed test paths gets `capability: null`; avoid making the new draft property optional because omission would obscure whether capability should be retained or removed. + +- [ ] **Step 5: Run the document test and commit** + +Run the Step 2 command. Expected: PASS. + +Commit: + +```bash +git add apps/aevatar-console-web/src/shared/studio/models.ts apps/aevatar-console-web/src/shared/studio/document.ts apps/aevatar-console-web/src/shared/studio/document.test.ts apps/aevatar-console-web/src/pages/workflow-activity-vnext/hooks/useWorkflowEditor.ts +git commit -m "Preserve workflow capability in inspector drafts" +``` + +### Task 4: Pure external-operation configuration mapping + +**Files:** +- Create: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts` +- Create: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts` + +- [ ] **Step 1: Write failing selector and argument tests** + +Cover: + +- selector keys do not rely on display names; +- exact API selector to document capability conversion; +- parsing canonical argument JSON into path/query/header/body/response values; +- updating one declared field preserves unknown top-level keys and undeclared location keys; +- `${steps.lookup.output}` stays a string for an integer schema; +- literal integer/number/boolean values are coerced only when valid; +- empty optional values are removed while required empty values produce a field error; +- invalid JSON returns an explicit parse result without silently replacing the original text. + +- [ ] **Step 2: Run the pure test and confirm failure** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts +``` + +Expected: FAIL because the module does not exist. + +- [ ] **Step 3: Implement immutable mapping helpers** + +Expose focused functions: + +```ts +export function capabilitySelectorKey(selector: StudioWorkflowCapabilitySelector): string; +export function toDocumentCapability(selector: StudioWorkflowCapabilitySelector): StudioWorkflowCapability; +export function parseToolArguments(text: unknown): ToolArgumentParseResult; +export function writeToolArgument(input: WriteToolArgumentInput): ToolArgumentWriteResult; +export function listOperationFields(readiness: StudioWorkflowCapabilityReadiness): readonly OperationField[]; +``` + +Use `JSON.parse`/`JSON.stringify`, clone only affected objects, and preserve unknown keys. Expressions bypass literal coercion. Do not manipulate JSON with string replacement. + +- [ ] **Step 4: Run the pure test and commit** + +Run the Step 2 command. Expected: PASS. + +Commit: + +```bash +git add apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts +git commit -m "Model guided tool call inputs" +``` + +### Task 5: Guided Tool call component + +**Files:** +- Create: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx` +- Create: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx` + +- [ ] **Step 1: Write the novice-path component test** + +Render with `scopeId="scope-alpha"`, `parameters.tool = 'nyxid_proxy'`, and no capability. Mock discovery and readiness methods. Assert: + +```ts +expect(screen.getByLabelText('Action')).toBeInTheDocument(); +expect(screen.queryByDisplayValue('nyxid_proxy')).not.toBeInTheDocument(); +``` + +Select `PostHog / List dashboards`; assert the change callback receives the exact document selector and `parameters.tool = 'nyxid_proxy'`. Then resolve readiness and assert required contract fields render. + +- [ ] **Step 2: Add state and accessibility tests** + +Cover loading, empty, retryable discovery failure, setup blockers, selected action missing from the refreshed catalog, read-only/write/destructive badges, stale readiness responses, allowed-values select, boolean switch, JSON object editor, response mode, and disabled state. Use role/label queries rather than class selectors. + +- [ ] **Step 3: Run the component test and confirm failure** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx +``` + +Expected: FAIL because the component does not exist. + +- [ ] **Step 4: Implement discovery and readiness state** + +Use TanStack Query with keys containing scope and exact selector. Keep discovery independent from inspector opening. Treat selection absence, network failure, empty success, setup blockers, unavailable selector, and ready as distinct rendered states. Keep any saved selector visible while discovery is pending. + +- [ ] **Step 5: Implement generated inputs** + +Render simple leaves with `Input`, `InputNumber`, `Switch`, or `Select`; render complex object/array values with `Input.TextArea`. Connect helper/error IDs through `aria-describedby`. Use the pure mapping helpers so every change returns a complete parameters object without deleting unknown values. + +- [ ] **Step 6: Run the component and mapping tests and commit** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts +``` + +Expected: PASS. + +Commit: + +```bash +git add apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx +git commit -m "Guide external tool call configuration" +``` + +### Task 6: Inspector information hierarchy and integration + +**Files:** +- Modify: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx` +- Modify: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx` +- Modify: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts` +- Test: `apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx` + +- [ ] **Step 1: Write failing inspector hierarchy tests** + +Assert the header title is action/type-oriented, purpose text is present, raw step ID appears only after opening `Technical details`, the advanced section is named `Advanced JSON`, the footer says `Apply step`, and closing an edited inspector still prompts before discard. + +- [ ] **Step 2: Run the inspector test and confirm failure** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx +``` + +Expected: FAIL against the current generic hierarchy. + +- [ ] **Step 3: Integrate atomic capability changes** + +Add `scopeId` to inspector props. Hold capability beside parameter text in the local draft hook. For direct `tool_call`, render `WorkflowToolCallConfiguration`; for all other node shapes, render the existing schema form. Pass `{ parametersText, capability }` to the editor callback in one apply operation. + +- [ ] **Step 4: Implement presentation metadata and hierarchy** + +Keep a small local registry of purpose sentences keyed by normalized step type. Use action display name when resolved, otherwise `Configure {nodeTypeLabel}`. Move the step ID, type, role, flow routing, runtime adapter, and selector IDs under `Technical details`. Rename raw disclosure to `Advanced JSON` and clarify that it edits runtime parameters. + +- [ ] **Step 5: Style with existing tokens** + +Extend `styles.ts` with compact status rows, risk badges, service options, grouped fields, inline help/errors, and technical values. Keep the panel width responsive, avoid nested card styling, use existing `--wa-*` tokens, and ensure narrow screens remain single-column with visible footer actions. + +- [ ] **Step 6: Run all directly related frontend tests** + +Run: + +```bash +pnpm --dir apps/aevatar-console-web jest --selectProjects node --runInBand src/shared/studio/document.test.ts +pnpm --dir apps/aevatar-console-web jest --selectProjects jsdom --runInBand src/shared/studio/api.test.ts src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx +``` + +Expected: PASS. + +- [ ] **Step 7: Commit the inspector integration** + +```bash +git add apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowEditorPage.tsx apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts +git commit -m "Reshape workflow step configuration" +``` + +### Task 7: Focused validation and pull request delivery + +**Files:** +- Modify only files already listed if validation exposes task-related defects. + +- [ ] **Step 1: Run the frontend scope analyzer** + +Run from the repository root: + +```bash +python3 ~/.codex/skills/frontend-incremental-pr/scripts/frontend_change_scope.py --repo . --base origin/feat/2026-08-04_workflow-activity-vnext +``` + +Use its `relatedTests` and `staticCheckFiles` output. Do not run full frontend test, lint, `tsc`, or build. + +- [ ] **Step 2: Run changed-file frontend static checks** + +After reading `frontend-incremental-pr/references/framework-commands.md`, run Biome only against the analyzer's `staticCheckFiles`. Run a typecheck only if the repository provides a reliable affected target; otherwise record that GitHub CI owns typecheck. + +- [ ] **Step 3: Run focused backend and stability checks** + +Run: + +```bash +dotnet test test/Aevatar.GAgentService.Integration.Tests/Aevatar.GAgentService.Integration.Tests.csproj --nologo --filter "FullyQualifiedName~ScopeWorkflowEndpointsTests|FullyQualifiedName~GAgentServiceHostingServiceCollectionExtensionsTests" +bash tools/ci/test_stability_guards.sh +``` + +Expected: PASS. + +- [ ] **Step 4: Review the final diff** + +Run `git diff --check`, inspect `git diff origin/feat/2026-08-04_workflow-activity-vnext...HEAD` plus remaining working-tree changes, confirm no credentials/logging leaks, and verify only task files changed. + +- [ ] **Step 5: Commit any validation fixes** + +Stage only explicit task paths and use an imperative, single-purpose commit message. Do not use `git add .`. + +- [ ] **Step 6: Push and create the pull request** + +Push `feat/2026-09-02_workflow-configuration-guidance` and create a PR targeting `feat/2026-08-04_workflow-activity-vnext`. The PR body must include problem/solution, impact paths, exact focused commands/results, and: + +```markdown +- Full frontend suite/build: deferred to GitHub CI by personal local workflow policy +``` + +Stop after reporting the PR URL; do not babysit CI unless requested. From cea9c7b01915d2779f9f6499164d141b0c83dced Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:53:01 +0800 Subject: [PATCH 075/167] Expose workflow capability guidance endpoints --- .../Endpoints/ScopeWorkflowEndpoints.cs | 85 ++++ .../WorkflowCapabilityHttpContracts.cs | 411 ++++++++++++++++++ ...HostingServiceCollectionExtensionsTests.cs | 2 + .../ScopeWorkflowEndpointsTests.cs | 293 +++++++++++++ 4 files changed, 791 insertions(+) create mode 100644 src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs diff --git a/src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs b/src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs index 61a7091700..6f9085a84e 100644 --- a/src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs +++ b/src/platform/Aevatar.GAgentService.Hosting/Endpoints/ScopeWorkflowEndpoints.cs @@ -40,6 +40,12 @@ public static IEndpointRouteBuilder MapScopeWorkflowCapabilityEndpoints(this IEn group.MapPost("/{scopeId}/workflows:explicit-request-preview", HandleExplicitRequestPreviewAsync) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest); + group.MapGet("/{scopeId}/workflow-capabilities", HandleListWorkflowCapabilitiesAsync) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status400BadRequest); + group.MapPost("/{scopeId}/workflow-capabilities:readiness", HandleWorkflowCapabilityReadinessAsync) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status400BadRequest); group.MapGet("/{scopeId}/workflows", HandleListWorkflowsAsync) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest); @@ -75,6 +81,65 @@ internal static async Task HandleExplicitRequestPreviewAsync( CancellationToken ct) => await HandleExplicitRequestPreviewAsyncCore(http, scopeId, request, previewService, ct); + internal static async Task HandleListWorkflowCapabilitiesAsync( + HttpContext http, + string scopeId, + [FromServices] IExternalWorkflowCapabilityListPort capabilityListPort, + CancellationToken ct) + { + try + { + if (AevatarScopeAccessGuard.TryCreateScopeAccessDeniedResult(http, scopeId, out var denied)) + return denied; + + var result = await capabilityListPort.ListAsync( + new ListExternalWorkflowCapabilitiesRequest( + CreateExternalCapabilityAccessContext(http, scopeId)), + ct); + return Results.Ok(WorkflowCapabilityHttpContracts.ToHttpResponse(result)); + } + catch (WorkflowCallerCredentialSelectionException) + { + return CallerCredentialBadRequest(); + } + catch (InvalidOperationException ex) + { + return InvalidUserWorkflowRequest(ex); + } + } + + internal static async Task HandleWorkflowCapabilityReadinessAsync( + HttpContext http, + string scopeId, + WorkflowCapabilityReadinessHttpRequest request, + [FromServices] IExternalWorkflowCapabilityReadinessPort capabilityReadinessPort, + CancellationToken ct) + { + try + { + if (AevatarScopeAccessGuard.TryCreateScopeAccessDeniedResult(http, scopeId, out var denied)) + return denied; + + ArgumentNullException.ThrowIfNull(request); + var executionMode = ParseExplicitRequestPreviewExecutionMode(request.ExecutionMode); + var readiness = await capabilityReadinessPort.InspectAsync( + new InspectExternalWorkflowCapabilityReadinessRequest( + CreateExternalCapabilityAccessContext(http, scopeId, executionMode), + WorkflowCapabilityHttpContracts.ToSelector(request.Selector), + executionMode), + ct); + return Results.Ok(WorkflowCapabilityHttpContracts.ToHttpResponse(readiness)); + } + catch (WorkflowCallerCredentialSelectionException) + { + return CallerCredentialBadRequest(); + } + catch (Exception ex) when (ex is InvalidOperationException or ArgumentNullException) + { + return InvalidUserWorkflowRequest(ex); + } + } + internal static async Task HandleListWorkflowsAsync( HttpContext http, string scopeId, @@ -227,6 +292,26 @@ private static IResult CallerCredentialBadRequest() => message = WorkflowCallerCredentialSelectionException.SafeMessage, }); + private static IResult InvalidUserWorkflowRequest(Exception exception) => + Results.BadRequest(new + { + code = "INVALID_USER_WORKFLOW_REQUEST", + message = exception.Message, + }); + + private static ExternalWorkflowCapabilityAccessContext CreateExternalCapabilityAccessContext( + HttpContext http, + string scopeId, + ExternalCapabilityExecutionMode executionMode = ExternalCapabilityExecutionMode.Interactive) + { + var admissionContext = WorkflowCapabilityAdmissionHttpContext.Create(http, executionMode); + return new ExternalWorkflowCapabilityAccessContext( + scopeId, + admissionContext.CallerId, + admissionContext.NyxIdCallerCredential, + admissionContext.NyxIdOrganizationBearerToken); + } + private static async Task HandleExplicitRequestPreviewAsyncCore( HttpContext http, string scopeId, diff --git a/src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs b/src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs new file mode 100644 index 0000000000..ef8923fe3a --- /dev/null +++ b/src/platform/Aevatar.GAgentService.Hosting/Endpoints/WorkflowCapabilityHttpContracts.cs @@ -0,0 +1,411 @@ +using Aevatar.Workflow.Abstractions; + +namespace Aevatar.GAgentService.Hosting.Endpoints; + +public sealed record WorkflowCapabilitySelectorHttpRequest( + string Kind, + string UserServiceId, + string EndpointId); + +public sealed record WorkflowCapabilityReadinessHttpRequest( + WorkflowCapabilitySelectorHttpRequest Selector, + string ExecutionMode); + +public sealed record WorkflowCapabilityListHttpResponse( + IReadOnlyList Capabilities, + int CandidateCount, + int RejectedCount, + IReadOnlyList Diagnostics); + +public sealed record WorkflowCapabilityDescriptorHttpResponse( + string DisplayName, + bool ReadOnly, + bool Destructive, + WorkflowCapabilitySelectorHttpResponse Selector, + WorkflowCapabilitySourceHttpResponse? Source); + +public sealed record WorkflowCapabilitySelectorHttpResponse( + string Kind, + string? UserServiceId = null, + string? EndpointId = null, + string? ConnectorCapabilityRef = null, + string? OperationId = null, + string? ContractDigest = null, + string? Method = null, + string? PathTemplate = null, + IReadOnlyList? QueryParameters = null, + IReadOnlyList? HeaderParameters = null, + string? BodyMode = null, + string? ResponseMode = null, + bool? BodyRequired = null); + +public sealed record WorkflowCapabilitySourceHttpResponse( + string Kind, + string SourceId, + long SourceVersion, + DateTimeOffset? ObservedAt, + DateTimeOffset? FreshUntil); + +public sealed record WorkflowCapabilityDiagnosticHttpResponse( + string Code, + string SafeMessage, + int Count, + WorkflowCapabilitySourceHttpResponse? Source); + +public sealed record WorkflowCapabilityReadinessHttpResponse( + string ExecutionMode, + string Status, + WorkflowCapabilitySelectorHttpResponse? SelectedSelector, + WorkflowCapabilityOperationHttpResponse? SelectedOperation, + IReadOnlyList Blockers, + IReadOnlyList Remediations, + IReadOnlyList Sources); + +public sealed record WorkflowCapabilityBlockerHttpResponse( + string Status, + string Code, + string SafeMessage); + +public sealed record WorkflowCapabilityRemediationHttpResponse( + string ActionKind, + string Label, + string TrustedLocator); + +public sealed record WorkflowCapabilityOperationHttpResponse( + string UserServiceId, + string EndpointId, + string ServiceSlug, + string HttpMethod, + string PathTemplate, + IReadOnlyList Parameters, + WorkflowCapabilityRequestBodyHttpResponse? RequestBody, + WorkflowCapabilityResponsePolicyHttpResponse? ResponsePolicy, + WorkflowCapabilityExecutionPolicyHttpResponse? ExecutionPolicy); + +public sealed record WorkflowCapabilityParameterHttpResponse( + string Name, + string Location, + bool Required, + WorkflowCapabilitySchemaHttpResponse Schema); + +public sealed record WorkflowCapabilityRequestBodyHttpResponse( + bool Required, + string MediaType, + WorkflowCapabilitySchemaHttpResponse Schema); + +public sealed record WorkflowCapabilitySchemaPropertyHttpResponse( + string Name, + WorkflowCapabilitySchemaHttpResponse Schema); + +public sealed record WorkflowCapabilitySchemaHttpResponse( + string ValueKind, + IReadOnlyList Properties, + IReadOnlyList RequiredProperties, + WorkflowCapabilitySchemaHttpResponse? Items, + IReadOnlyList AllowedValues, + bool AdditionalPropertiesAllowed); + +public sealed record WorkflowCapabilityResponsePolicyHttpResponse( + bool TextAllowed, + bool FileArtifactAllowed, + IReadOnlyList MediaTypes); + +public sealed record WorkflowCapabilityExecutionPolicyHttpResponse( + string Risk, + string Approval, + string EnforcementOwner, + IReadOnlyList AllowedExecutionModes); + +internal static class WorkflowCapabilityHttpContracts +{ + public static WorkflowCapabilityListHttpResponse ToHttpResponse( + ExternalWorkflowCapabilityDiscoveryResult result) => + new( + result.Capabilities.Select(ToDescriptor).ToArray(), + result.CandidateCount, + result.RejectedCount, + result.Diagnostics.Select(ToDiagnostic).ToArray()); + + public static WorkflowCapabilityReadinessHttpResponse ToHttpResponse( + ExternalCapabilityReadiness readiness) => + new( + ToWireValue(readiness.ExecutionMode), + ToWireValue(readiness.Status), + readiness.SelectedSelector?.SelectorCase is null or ExternalWorkflowCapabilitySelector.SelectorOneofCase.None + ? null + : ToSelector(readiness.SelectedSelector), + readiness.SelectedCapability?.CapabilityCase == ExternalWorkflowCapabilityRef.CapabilityOneofCase.NyxIdUserService + ? ToOperation(readiness.SelectedCapability.NyxIdUserService) + : null, + readiness.Blockers.Select(static blocker => new WorkflowCapabilityBlockerHttpResponse( + ToWireValue(blocker.Status), + blocker.Code, + blocker.SafeMessage)).ToArray(), + readiness.Remediations.Select(static remediation => new WorkflowCapabilityRemediationHttpResponse( + ToWireValue(remediation.ActionKind), + remediation.Label, + remediation.TrustedLocator)).ToArray(), + readiness.Sources.Select(ToSource).ToArray()); + + public static ExternalWorkflowCapabilitySelector ToSelector( + WorkflowCapabilitySelectorHttpRequest request) + { + ArgumentNullException.ThrowIfNull(request); + if (!string.Equals(request.Kind?.Trim(), "nyxid_operation", StringComparison.Ordinal)) + throw new InvalidOperationException("Selector kind must be 'nyxid_operation'."); + + return new ExternalWorkflowCapabilitySelector + { + NyxIdOperation = new NyxIdOperationSelector + { + UserServiceId = NormalizeRequired(request.UserServiceId, "Selector.UserServiceId"), + EndpointId = NormalizeRequired(request.EndpointId, "Selector.EndpointId"), + }, + }; + } + + private static WorkflowCapabilityDescriptorHttpResponse ToDescriptor( + ExternalWorkflowCapabilityDescriptor descriptor) => + new( + descriptor.DisplayName, + descriptor.ReadOnly, + descriptor.Destructive, + ToSelector(descriptor.Selector), + descriptor.Source is null ? null : ToSource(descriptor.Source)); + + private static WorkflowCapabilitySelectorHttpResponse ToSelector( + ExternalWorkflowCapabilitySelector selector) => selector.SelectorCase switch + { + ExternalWorkflowCapabilitySelector.SelectorOneofCase.HostConnector => new( + "host_connector", + ConnectorCapabilityRef: selector.HostConnector.ConnectorCapabilityRef, + OperationId: selector.HostConnector.OperationId, + ContractDigest: selector.HostConnector.ContractDigest), + ExternalWorkflowCapabilitySelector.SelectorOneofCase.NyxIdOperation => new( + "nyxid_operation", + UserServiceId: selector.NyxIdOperation.UserServiceId, + EndpointId: selector.NyxIdOperation.EndpointId), + ExternalWorkflowCapabilitySelector.SelectorOneofCase.NyxIdRequest => new( + "nyxid_request", + UserServiceId: selector.NyxIdRequest.UserServiceId, + Method: ToWireValue(selector.NyxIdRequest.Method), + PathTemplate: selector.NyxIdRequest.PathTemplate, + QueryParameters: selector.NyxIdRequest.QueryParameters.ToArray(), + HeaderParameters: selector.NyxIdRequest.HeaderParameters.ToArray(), + BodyMode: ToWireValue(selector.NyxIdRequest.BodyMode), + ResponseMode: ToWireValue(selector.NyxIdRequest.ResponseMode), + BodyRequired: selector.NyxIdRequest.BodyRequired), + _ => throw new InvalidOperationException("External capability selector is required."), + }; + + private static WorkflowCapabilitySourceHttpResponse ToSource( + ExternalCapabilitySourceStamp source) => + new( + ToWireValue(source.SourceKind), + source.SourceId, + source.SourceVersion, + source.ObservedAt?.ToDateTimeOffset(), + source.FreshUntil?.ToDateTimeOffset()); + + private static WorkflowCapabilityDiagnosticHttpResponse ToDiagnostic( + ExternalCapabilityDiscoveryDiagnostic diagnostic) => + new( + ToWireValue(diagnostic.Code), + diagnostic.SafeMessage, + diagnostic.Count, + diagnostic.Source is null ? null : ToSource(diagnostic.Source)); + + private static WorkflowCapabilityOperationHttpResponse ToOperation( + NyxIdUserServiceCapabilityRef operation) => + new( + operation.UserServiceId, + operation.EndpointId, + operation.ServiceSlugSnapshot, + operation.HttpMethod, + operation.PathTemplate, + operation.Parameters.Select(static parameter => new WorkflowCapabilityParameterHttpResponse( + parameter.Name, + ToWireValue(parameter.Location), + parameter.Required, + ToSchema(parameter.Schema))).ToArray(), + operation.RequestBody is null + ? null + : new WorkflowCapabilityRequestBodyHttpResponse( + operation.RequestBody.Required, + operation.RequestBody.MediaType, + ToSchema(operation.RequestBody.Schema)), + operation.ResponsePolicy is null + ? null + : new WorkflowCapabilityResponsePolicyHttpResponse( + operation.ResponsePolicy.TextAllowed, + operation.ResponsePolicy.FileArtifactAllowed, + operation.ResponsePolicy.MediaTypes.ToArray()), + operation.ExecutionPolicy is null + ? null + : new WorkflowCapabilityExecutionPolicyHttpResponse( + ToWireValue(operation.ExecutionPolicy.Risk), + ToWireValue(operation.ExecutionPolicy.Approval), + ToWireValue(operation.ExecutionPolicy.EnforcementOwner), + operation.ExecutionPolicy.AllowedExecutionModes.Select(ToWireValue).ToArray())); + + private static WorkflowCapabilitySchemaHttpResponse ToSchema(NyxIdOperationSchema? schema) + { + if (schema is null) + throw new InvalidOperationException("External capability input schema is required."); + + return new WorkflowCapabilitySchemaHttpResponse( + ToWireValue(schema.ValueKind), + schema.Properties.Select(static property => new WorkflowCapabilitySchemaPropertyHttpResponse( + property.Name, + ToSchema(property.Schema))).ToArray(), + schema.RequiredProperties.ToArray(), + schema.Items is null ? null : ToSchema(schema.Items), + schema.AllowedValues.ToArray(), + schema.AdditionalPropertiesAllowed); + } + + private static string NormalizeRequired(string? value, string name) + { + var normalized = value?.Trim(); + if (string.IsNullOrWhiteSpace(normalized)) + throw new InvalidOperationException($"{name} is required."); + + return normalized; + } + + private static string ToWireValue(ExternalCapabilityExecutionMode value) => value switch + { + ExternalCapabilityExecutionMode.Interactive => "interactive", + ExternalCapabilityExecutionMode.Durable => "durable", + _ => "unspecified", + }; + + private static string ToWireValue(ExternalCapabilityReadinessStatus value) => value switch + { + ExternalCapabilityReadinessStatus.SelectionRequired => "selection_required", + ExternalCapabilityReadinessStatus.ConnectorNotFound => "connector_not_found", + ExternalCapabilityReadinessStatus.ServiceRegistrationRequired => "service_registration_required", + ExternalCapabilityReadinessStatus.CredentialConnectionRequired => "credential_connection_required", + ExternalCapabilityReadinessStatus.ServiceAccessDenied => "service_access_denied", + ExternalCapabilityReadinessStatus.NodeBindingRequired => "node_binding_required", + ExternalCapabilityReadinessStatus.NodeUnavailable => "node_unavailable", + ExternalCapabilityReadinessStatus.EndpointContractRequired => "endpoint_contract_required", + ExternalCapabilityReadinessStatus.OperationSelectionRequired => "operation_selection_required", + ExternalCapabilityReadinessStatus.SourceStale => "source_stale", + ExternalCapabilityReadinessStatus.DurableAuthorizationUnavailable => "durable_authorization_unavailable", + ExternalCapabilityReadinessStatus.ContractDrift => "contract_drift", + ExternalCapabilityReadinessStatus.Ready => "ready", + ExternalCapabilityReadinessStatus.AdmissionRebindRequired => "admission_rebind_required", + _ => "unspecified", + }; + + private static string ToWireValue(ExternalCapabilityRemediationActionKind value) => value switch + { + ExternalCapabilityRemediationActionKind.SelectCapability => "select_capability", + ExternalCapabilityRemediationActionKind.ConfigureConnector => "configure_connector", + ExternalCapabilityRemediationActionKind.RegisterService => "register_service", + ExternalCapabilityRemediationActionKind.ConnectCredential => "connect_credential", + ExternalCapabilityRemediationActionKind.RequestAccess => "request_access", + ExternalCapabilityRemediationActionKind.BindNode => "bind_node", + ExternalCapabilityRemediationActionKind.RestoreNode => "restore_node", + ExternalCapabilityRemediationActionKind.PublishEndpointContract => "publish_endpoint_contract", + ExternalCapabilityRemediationActionKind.SelectOperation => "select_operation", + ExternalCapabilityRemediationActionKind.RefreshSource => "refresh_source", + ExternalCapabilityRemediationActionKind.UseInteractiveExecution => "use_interactive_execution", + ExternalCapabilityRemediationActionKind.RebindWorkflow => "rebind_workflow", + _ => "unspecified", + }; + + private static string ToWireValue(ExternalCapabilitySourceKind value) => value switch + { + ExternalCapabilitySourceKind.ConnectorCatalog => "connector_catalog", + ExternalCapabilitySourceKind.NyxIdUserServices => "nyxid_user_services", + ExternalCapabilitySourceKind.NyxIdOpenApi => "nyxid_open_api", + ExternalCapabilitySourceKind.DurableAuthorizationCatalog => "durable_authorization_catalog", + ExternalCapabilitySourceKind.NyxIdMcpConfig => "nyxid_mcp_config", + _ => "unspecified", + }; + + private static string ToWireValue(ExternalCapabilityDiscoveryDiagnosticCode value) => value switch + { + ExternalCapabilityDiscoveryDiagnosticCode.SourceUnavailable => "source_unavailable", + ExternalCapabilityDiscoveryDiagnosticCode.NoExactUserService => "no_exact_user_service", + ExternalCapabilityDiscoveryDiagnosticCode.GenericProxyRejected => "generic_proxy_rejected", + ExternalCapabilityDiscoveryDiagnosticCode.InvalidServiceIdentity => "invalid_service_identity", + ExternalCapabilityDiscoveryDiagnosticCode.AmbiguousServiceIdentity => "ambiguous_service_identity", + ExternalCapabilityDiscoveryDiagnosticCode.InvalidEndpointIdentity => "invalid_endpoint_identity", + ExternalCapabilityDiscoveryDiagnosticCode.AmbiguousEndpointIdentity => "ambiguous_endpoint_identity", + ExternalCapabilityDiscoveryDiagnosticCode.UnsupportedParameter => "unsupported_parameter", + ExternalCapabilityDiscoveryDiagnosticCode.UnsupportedRequestBody => "unsupported_request_body", + ExternalCapabilityDiscoveryDiagnosticCode.UnsupportedSchema => "unsupported_schema", + ExternalCapabilityDiscoveryDiagnosticCode.UnsupportedResponse => "unsupported_response", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdRequestMethod value) => value switch + { + NyxIdRequestMethod.Get => "get", + NyxIdRequestMethod.Head => "head", + NyxIdRequestMethod.Options => "options", + NyxIdRequestMethod.Post => "post", + NyxIdRequestMethod.Put => "put", + NyxIdRequestMethod.Patch => "patch", + NyxIdRequestMethod.Delete => "delete", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdRequestBodyMode value) => value switch + { + NyxIdRequestBodyMode.None => "none", + NyxIdRequestBodyMode.Json => "json", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdRequestResponseMode value) => value switch + { + NyxIdRequestResponseMode.Text => "text", + NyxIdRequestResponseMode.FileArtifact => "file_artifact", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdOperationParameterLocation value) => value switch + { + NyxIdOperationParameterLocation.Path => "path", + NyxIdOperationParameterLocation.Query => "query", + NyxIdOperationParameterLocation.Header => "header", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdOperationValueKind value) => value switch + { + NyxIdOperationValueKind.String => "string", + NyxIdOperationValueKind.Integer => "integer", + NyxIdOperationValueKind.Number => "number", + NyxIdOperationValueKind.Boolean => "boolean", + NyxIdOperationValueKind.Object => "object", + NyxIdOperationValueKind.Array => "array", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdOperationRisk value) => value switch + { + NyxIdOperationRisk.ReadOnly => "read_only", + NyxIdOperationRisk.Write => "write", + NyxIdOperationRisk.Destructive => "destructive", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdOperationApproval value) => value switch + { + NyxIdOperationApproval.None => "none", + NyxIdOperationApproval.Required => "required", + _ => "unspecified", + }; + + private static string ToWireValue(NyxIdOperationEnforcementOwner value) => value switch + { + NyxIdOperationEnforcementOwner.Aevatar => "aevatar", + NyxIdOperationEnforcementOwner.NyxId => "nyxid", + _ => "unspecified", + }; +} diff --git a/test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs b/test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs index 54b04dd92e..2901a65cad 100644 --- a/test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs +++ b/test/Aevatar.GAgentService.Integration.Tests/GAgentServiceHostingServiceCollectionExtensionsTests.cs @@ -391,6 +391,8 @@ public async Task AddGAgentServiceCapabilityBundle_ShouldRegisterCapabilityAndMa endpoints.Should().Contain("/api/scopes/{scopeId}/binding"); endpoints.Should().Contain("/api/scopes/{scopeId}/workflows:save-and-bind"); endpoints.Should().Contain("/api/scopes/{scopeId}/workflows:explicit-request-preview"); + endpoints.Should().Contain("/api/scopes/{scopeId}/workflow-capabilities"); + endpoints.Should().Contain("/api/scopes/{scopeId}/workflow-capabilities:readiness"); endpoints.Should().Contain("/api/scopes/{scopeId}/binding/revisions/{revisionId}:activate"); endpoints.Should().Contain("/api/scopes/{scopeId}/revisions"); endpoints.Should().Contain("/api/scopes/{scopeId}/revisions/{revisionId}"); diff --git a/test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs b/test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs index 8c24973fcc..d31a5c526f 100644 --- a/test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs +++ b/test/Aevatar.GAgentService.Integration.Tests/ScopeWorkflowEndpointsTests.cs @@ -29,6 +29,171 @@ namespace Aevatar.GAgentService.Integration.Tests; public sealed class ScopeWorkflowEndpointsTests { + [Fact] + public async Task HandleListWorkflowCapabilitiesAsync_ShouldReturnExactSanitizedDescriptors() + { + const string bearer = "transient-capability-bearer"; + var http = CreateHttpContext(); + http.Request.Headers.Authorization = $"Bearer {bearer}"; + var listPort = new RecordingExternalWorkflowCapabilityListPort + { + Result = new ExternalWorkflowCapabilityDiscoveryResult + { + CandidateCount = 2, + RejectedCount = 1, + Capabilities = + { + new ExternalWorkflowCapabilityDescriptor + { + DisplayName = "PostHog / List dashboards", + ReadOnly = true, + Selector = new ExternalWorkflowCapabilitySelector + { + NyxIdOperation = new NyxIdOperationSelector + { + UserServiceId = "us-posthog-alpha", + EndpointId = "list-dashboards", + }, + }, + Source = new ExternalCapabilitySourceStamp + { + SourceKind = ExternalCapabilitySourceKind.NyxIdUserServices, + SourceId = "source-posthog-alpha", + SourceVersion = 7, + ObservedAt = Timestamp.FromDateTimeOffset( + DateTimeOffset.Parse("2026-09-02T08:00:00Z")), + FreshUntil = Timestamp.FromDateTimeOffset( + DateTimeOffset.Parse("2026-09-02T08:05:00Z")), + }, + }, + }, + Diagnostics = + { + new ExternalCapabilityDiscoveryDiagnostic + { + Code = ExternalCapabilityDiscoveryDiagnosticCode.UnsupportedSchema, + SafeMessage = "One operation was omitted because its schema is unsupported.", + Count = 1, + }, + }, + }, + }; + + var result = await ScopeWorkflowEndpoints.HandleListWorkflowCapabilitiesAsync( + http, + "user-1", + listPort, + CancellationToken.None); + + await result.ExecuteAsync(http); + var body = await ReadBodyAsync(http.Response); + + http.Response.StatusCode.Should().Be(StatusCodes.Status200OK); + listPort.Request.Should().NotBeNull(); + listPort.Request!.Access.ScopeId.Should().Be("user-1"); + listPort.Request.Access.CallerId.Should().Be("caller-alpha"); + listPort.Request.Access.NyxIdCallerCredential?.SourceReadableUserBearerToken + .Should().Be(bearer); + body.Should().Contain("\"displayName\":\"PostHog / List dashboards\""); + body.Should().Contain("\"userServiceId\":\"us-posthog-alpha\""); + body.Should().Contain("\"endpointId\":\"list-dashboards\""); + body.Should().Contain("\"kind\":\"nyxid_operation\""); + body.Should().Contain("\"safeMessage\":\"One operation was omitted because its schema is unsupported.\""); + body.Should().NotContain(bearer); + body.ToLowerInvariant().Should().NotContain("credential"); + body.ToLowerInvariant().Should().NotContain("deprecated"); + } + + [Fact] + public async Task HandleListWorkflowCapabilitiesAsync_ShouldRejectMismatchedScopeBeforeListing() + { + var http = CreateAuthenticatedHttpContext("user-2"); + var listPort = new RecordingExternalWorkflowCapabilityListPort(); + + var result = await ScopeWorkflowEndpoints.HandleListWorkflowCapabilitiesAsync( + http, + "user-1", + listPort, + CancellationToken.None); + + await result.ExecuteAsync(http); + + http.Response.StatusCode.Should().Be(StatusCodes.Status403Forbidden); + listPort.Request.Should().BeNull(); + } + + [Fact] + public async Task HandleWorkflowCapabilityReadinessAsync_ShouldReturnTypedOperationContract() + { + var http = CreateHttpContext(); + var readinessPort = new RecordingExternalWorkflowCapabilityReadinessPort + { + Result = BuildReadyExternalCapability(), + }; + + var result = await ScopeWorkflowEndpoints.HandleWorkflowCapabilityReadinessAsync( + http, + "user-1", + new WorkflowCapabilityReadinessHttpRequest( + new WorkflowCapabilitySelectorHttpRequest( + "nyxid_operation", + "us-posthog-alpha", + "update-dashboard"), + "interactive"), + readinessPort, + CancellationToken.None); + + await result.ExecuteAsync(http); + var body = await ReadBodyAsync(http.Response); + + http.Response.StatusCode.Should().Be(StatusCodes.Status200OK); + readinessPort.Request.Should().NotBeNull(); + readinessPort.Request!.ExecutionMode.Should().Be(ExternalCapabilityExecutionMode.Interactive); + readinessPort.Request.Selector.NyxIdOperation.UserServiceId.Should().Be("us-posthog-alpha"); + readinessPort.Request.Selector.NyxIdOperation.EndpointId.Should().Be("update-dashboard"); + body.Should().Contain("\"status\":\"ready\""); + body.Should().Contain("\"httpMethod\":\"PATCH\""); + body.Should().Contain("\"location\":\"path\""); + body.Should().Contain("\"valueKind\":\"string\""); + body.Should().Contain("\"allowedValues\":[\"draft\",\"live\"]"); + body.Should().Contain("\"risk\":\"write\""); + body.Should().Contain("\"approval\":\"required\""); + body.Should().Contain("\"safeMessage\":\"Reconnect the PostHog service.\""); + body.Should().Contain("\"label\":\"Reconnect PostHog\""); + } + + [Theory] + [InlineData("", "us-posthog-alpha", "update-dashboard", "interactive")] + [InlineData("nyxid_request", "us-posthog-alpha", "update-dashboard", "interactive")] + [InlineData("nyxid_operation", "", "update-dashboard", "interactive")] + [InlineData("nyxid_operation", "us-posthog-alpha", "", "interactive")] + [InlineData("nyxid_operation", "us-posthog-alpha", "update-dashboard", "background")] + public async Task HandleWorkflowCapabilityReadinessAsync_ShouldRejectInvalidSelectorBeforeInspection( + string kind, + string userServiceId, + string endpointId, + string executionMode) + { + var http = CreateHttpContext(); + var readinessPort = new RecordingExternalWorkflowCapabilityReadinessPort(); + + var result = await ScopeWorkflowEndpoints.HandleWorkflowCapabilityReadinessAsync( + http, + "user-1", + new WorkflowCapabilityReadinessHttpRequest( + new WorkflowCapabilitySelectorHttpRequest(kind, userServiceId, endpointId), + executionMode), + readinessPort, + CancellationToken.None); + + await result.ExecuteAsync(http); + var body = await ReadBodyAsync(http.Response); + + http.Response.StatusCode.Should().Be(StatusCodes.Status400BadRequest); + body.Should().Contain("INVALID_USER_WORKFLOW_REQUEST"); + readinessPort.Request.Should().BeNull(); + } + [Fact] public async Task HandleExplicitRequestPreviewAsync_ShouldReturnSanitizedCanonicalPreview() { @@ -1337,6 +1502,102 @@ private static async Task ReadBodyAsync(HttpResponse response) return await reader.ReadToEndAsync(); } + private static ExternalCapabilityReadiness BuildReadyExternalCapability() + { + var stringSchema = new NyxIdOperationSchema + { + ValueKind = NyxIdOperationValueKind.String, + AllowedValues = { "draft", "live" }, + }; + return new ExternalCapabilityReadiness + { + ExecutionMode = ExternalCapabilityExecutionMode.Interactive, + Status = ExternalCapabilityReadinessStatus.Ready, + SelectedSelector = new ExternalWorkflowCapabilitySelector + { + NyxIdOperation = new NyxIdOperationSelector + { + UserServiceId = "us-posthog-alpha", + EndpointId = "update-dashboard", + }, + }, + SelectedCapability = new ExternalWorkflowCapabilityRef + { + NyxIdUserService = new NyxIdUserServiceCapabilityRef + { + UserServiceId = "us-posthog-alpha", + EndpointId = "update-dashboard", + ServiceSlugSnapshot = "posthog", + HttpMethod = "PATCH", + PathTemplate = "/api/projects/{project_id}/dashboards/{dashboard_id}", + Parameters = + { + new NyxIdOperationParameterContract + { + Name = "dashboard_id", + Location = NyxIdOperationParameterLocation.Path, + Required = true, + Schema = stringSchema, + }, + }, + RequestBody = new NyxIdOperationRequestBodyContract + { + Required = true, + MediaType = "application/json", + Schema = new NyxIdOperationSchema + { + ValueKind = NyxIdOperationValueKind.Object, + AdditionalPropertiesAllowed = false, + RequiredProperties = { "name" }, + Properties = + { + new NyxIdOperationSchemaProperty + { + Name = "name", + Schema = new NyxIdOperationSchema + { + ValueKind = NyxIdOperationValueKind.String, + }, + }, + }, + }, + }, + ResponsePolicy = new NyxIdOperationResponsePolicy + { + TextAllowed = true, + FileArtifactAllowed = false, + MediaTypes = { "application/json" }, + }, + ExecutionPolicy = new NyxIdOperationExecutionPolicy + { + Risk = NyxIdOperationRisk.Write, + Approval = NyxIdOperationApproval.Required, + EnforcementOwner = NyxIdOperationEnforcementOwner.Aevatar, + AllowedExecutionModes = { ExternalCapabilityExecutionMode.Interactive }, + }, + }, + }, + Blockers = + { + new ExternalCapabilityBlocker + { + Status = ExternalCapabilityReadinessStatus.CredentialConnectionRequired, + Code = "credential_connection_required", + SafeMessage = "Reconnect the PostHog service.", + }, + }, + Remediations = + { + new ExternalCapabilityRemediation + { + ActionKind = ExternalCapabilityRemediationActionKind.ConnectCredential, + Label = "Reconnect PostHog", + TrustedLocator = "/settings/connections/posthog", + }, + }, + }; + } + private static WorkflowRunEventEnvelope BuildRawObservedWorkflowExecutionStartedFrame() { var payload = new WorkflowRunExecutionStartedEvent @@ -1615,6 +1876,38 @@ public Task PreviewAsync( } } + private sealed class RecordingExternalWorkflowCapabilityListPort : + IExternalWorkflowCapabilityListPort + { + public ListExternalWorkflowCapabilitiesRequest? Request { get; private set; } + + public ExternalWorkflowCapabilityDiscoveryResult Result { get; init; } = new(); + + public Task ListAsync( + ListExternalWorkflowCapabilitiesRequest request, + CancellationToken cancellationToken = default) + { + Request = request; + return Task.FromResult(Result); + } + } + + private sealed class RecordingExternalWorkflowCapabilityReadinessPort : + IExternalWorkflowCapabilityReadinessPort + { + public InspectExternalWorkflowCapabilityReadinessRequest? Request { get; private set; } + + public ExternalCapabilityReadiness Result { get; init; } = new(); + + public Task InspectAsync( + InspectExternalWorkflowCapabilityReadinessRequest request, + CancellationToken cancellationToken = default) + { + Request = request; + return Task.FromResult(Result); + } + } + private sealed class RecordingDispatchService : ICommandDispatchService { From 2d0a973f87d1b83f900790db56991d5d4ca41b48 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:03:40 +0800 Subject: [PATCH 076/167] Add typed workflow capability client --- .../src/shared/studio/api.test.ts | 249 +++++++ .../src/shared/studio/api.ts | 637 ++++++++++++++++++ .../src/shared/studio/models.ts | 194 ++++++ 3 files changed, 1080 insertions(+) diff --git a/apps/aevatar-console-web/src/shared/studio/api.test.ts b/apps/aevatar-console-web/src/shared/studio/api.test.ts index 81661a57dd..db47fc85b7 100644 --- a/apps/aevatar-console-web/src/shared/studio/api.test.ts +++ b/apps/aevatar-console-web/src/shared/studio/api.test.ts @@ -1389,6 +1389,255 @@ describe('studioApi host-session requests', () => { ).rejects.toThrow(); }); + it('lists exact workflow capability selectors for the active scope', async () => { + const fetchMock = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + capabilities: [ + { + displayName: 'PostHog / List dashboards', + readOnly: true, + destructive: false, + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'list-dashboards', + }, + source: { + kind: 'nyxid_user_services', + sourceId: 'source-posthog-alpha', + sourceVersion: 7, + observedAt: '2026-09-02T08:00:00+00:00', + freshUntil: '2026-09-02T08:05:00+00:00', + }, + }, + ], + candidateCount: 2, + rejectedCount: 1, + diagnostics: [ + { + code: 'unsupported_schema', + safeMessage: + 'One operation was omitted because its schema is unsupported.', + count: 1, + source: null, + }, + ], + }), + } as Response); + global.fetch = fetchMock as typeof global.fetch; + + await expect( + studioApi.listWorkflowCapabilities(' scope-alpha '), + ).resolves.toEqual({ + capabilities: [ + expect.objectContaining({ + displayName: 'PostHog / List dashboards', + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'list-dashboards', + }, + }), + ], + candidateCount: 2, + rejectedCount: 1, + diagnostics: [ + expect.objectContaining({ + code: 'unsupported_schema', + count: 1, + }), + ], + }); + expect(fetchMock).toHaveBeenCalledWith( + '/api/scopes/scope-alpha/workflow-capabilities', + expect.objectContaining({ credentials: 'same-origin' }), + ); + }); + + it('inspects a workflow capability readiness contract using only its exact selector', async () => { + const fetchMock = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + executionMode: 'interactive', + status: 'ready', + selectedSelector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + selectedOperation: { + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + serviceSlug: 'posthog', + httpMethod: 'PATCH', + pathTemplate: '/api/dashboards/{dashboard_id}', + parameters: [ + { + name: 'dashboard_id', + location: 'path', + required: true, + schema: { + valueKind: 'string', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + ], + requestBody: { + required: true, + mediaType: 'application/json', + schema: { + valueKind: 'object', + properties: [ + { + name: 'name', + schema: { + valueKind: 'string', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + ], + requiredProperties: ['name'], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + responsePolicy: { + textAllowed: true, + fileArtifactAllowed: false, + mediaTypes: ['application/json'], + }, + executionPolicy: { + risk: 'write', + approval: 'required', + enforcementOwner: 'aevatar', + allowedExecutionModes: ['interactive'], + }, + }, + blockers: [ + { + status: 'credential_connection_required', + code: 'credential_connection_required', + safeMessage: 'Reconnect the PostHog service.', + }, + ], + remediations: [ + { + actionKind: 'connect_credential', + label: 'Reconnect PostHog', + trustedLocator: '/settings/connections/posthog', + }, + ], + sources: [], + }), + } as Response); + global.fetch = fetchMock as typeof global.fetch; + + const result = await studioApi.inspectWorkflowCapabilityReadiness({ + scopeId: 'scope-alpha', + executionMode: 'interactive', + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + }); + + expect(result.selectedOperation).toEqual( + expect.objectContaining({ + httpMethod: 'PATCH', + executionPolicy: expect.objectContaining({ + risk: 'write', + approval: 'required', + }), + }), + ); + const [input, init] = fetchMock.mock.calls[0] as [ + string, + RequestInit | undefined, + ]; + expect(input).toBe( + '/api/scopes/scope-alpha/workflow-capabilities:readiness', + ); + expect(init?.method).toBe('POST'); + expect(JSON.parse(String(init?.body))).toEqual({ + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + executionMode: 'interactive', + }); + }); + + it('rejects unsupported workflow capability selector kinds', async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + capabilities: [ + { + displayName: 'Future action', + readOnly: true, + destructive: false, + selector: { kind: 'future_selector' }, + source: null, + }, + ], + candidateCount: 1, + rejectedCount: 0, + diagnostics: [], + }), + } as Response) as typeof global.fetch; + + await expect( + studioApi.listWorkflowCapabilities('scope-alpha'), + ).rejects.toThrow('selector.kind is not supported.'); + }); + + it('rejects readiness when the returned selector does not match the request', async () => { + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + json: async () => ({ + executionMode: 'interactive', + status: 'ready', + selectedSelector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'different-operation', + }, + selectedOperation: null, + blockers: [], + remediations: [], + sources: [], + }), + } as Response) as typeof global.fetch; + + await expect( + studioApi.inspectWorkflowCapabilityReadiness({ + scopeId: 'scope-alpha', + executionMode: 'interactive', + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + }), + ).rejects.toThrow('returned a different selectedSelector'); + }); + it('rejects member workflow binding without a stable workflow id', async () => { expect(() => studioApi.bindMemberWorkflow({ diff --git a/apps/aevatar-console-web/src/shared/studio/api.ts b/apps/aevatar-console-web/src/shared/studio/api.ts index d7831d9aca..01861f442d 100644 --- a/apps/aevatar-console-web/src/shared/studio/api.ts +++ b/apps/aevatar-console-web/src/shared/studio/api.ts @@ -98,6 +98,22 @@ import type { StudioUserLlmSettings, StudioWorkflowBoardSnapshot, StudioWorkflowBoardSnapshotRequest, + StudioWorkflowCapabilityDescriptor, + StudioWorkflowCapabilityDiagnostic, + StudioWorkflowCapabilityDiagnosticCode, + StudioWorkflowCapabilityList, + StudioWorkflowCapabilityOperation, + StudioWorkflowCapabilityParameter, + StudioWorkflowCapabilityParameterLocation, + StudioWorkflowCapabilityReadiness, + StudioWorkflowCapabilityReadinessInput, + StudioWorkflowCapabilityReadinessStatus, + StudioWorkflowCapabilityRemediationAction, + StudioWorkflowCapabilitySchema, + StudioWorkflowCapabilitySelector, + StudioWorkflowCapabilitySource, + StudioWorkflowCapabilitySourceKind, + StudioWorkflowCapabilityValueKind, StudioWorkflowDocument, StudioWorkflowDraft, StudioWorkflowDraftCreateAcceptedReceipt, @@ -338,6 +354,596 @@ function decodeStudioExplicitRequestPreview( }; } +function decodeStudioWorkflowCapabilitySelector( + value: unknown, + label = 'StudioWorkflowCapabilityDescriptor.selector', +): StudioWorkflowCapabilitySelector { + const record = expectRecord(value, label); + const kind = readString(record, 'kind', label + '.kind'); + if (kind === 'nyxid_operation') { + return { + kind, + userServiceId: readNonBlankExplicitRequestString( + record, + 'userServiceId', + label + '.userServiceId', + ), + endpointId: readNonBlankExplicitRequestString( + record, + 'endpointId', + label + '.endpointId', + ), + }; + } + if (kind === 'host_connector') { + return { + kind, + connectorCapabilityRef: readNonBlankExplicitRequestString( + record, + 'connectorCapabilityRef', + label + '.connectorCapabilityRef', + ), + operationId: readNonBlankExplicitRequestString( + record, + 'operationId', + label + '.operationId', + ), + contractDigest: readNonBlankExplicitRequestString( + record, + 'contractDigest', + label + '.contractDigest', + ), + }; + } + if (kind === 'nyxid_request') { + return { + kind, + userServiceId: readNonBlankExplicitRequestString( + record, + 'userServiceId', + label + '.userServiceId', + ), + method: readExplicitRequestEnum( + record, + 'method', + label + '.method', + ['get', 'head', 'options', 'post', 'put', 'patch', 'delete'], + ), + pathTemplate: readNonBlankExplicitRequestString( + record, + 'pathTemplate', + label + '.pathTemplate', + ), + queryParameters: expectArray( + record.queryParameters, + label + '.queryParameters', + expectString, + ), + headerParameters: expectArray( + record.headerParameters, + label + '.headerParameters', + expectString, + ), + bodyMode: readExplicitRequestEnum( + record, + 'bodyMode', + label + '.bodyMode', + ['none', 'json'], + ), + responseMode: readExplicitRequestEnum( + record, + 'responseMode', + label + '.responseMode', + ['text', 'file_artifact'], + ), + bodyRequired: readBoolean( + record, + 'bodyRequired', + label + '.bodyRequired', + ), + }; + } + + throw new Error(label + '.kind is not supported.'); +} + +function workflowCapabilitySelectorKey( + selector: StudioWorkflowCapabilitySelector, +): string { + if (selector.kind === 'nyxid_operation') { + return [ + selector.kind, + selector.userServiceId, + selector.endpointId, + ].join('\u0000'); + } + if (selector.kind === 'host_connector') { + return [ + selector.kind, + selector.connectorCapabilityRef, + selector.operationId, + selector.contractDigest, + ].join('\u0000'); + } + return [ + selector.kind, + selector.userServiceId, + selector.method, + selector.pathTemplate, + ].join('\u0000'); +} + +function decodeStudioWorkflowCapabilitySource( + value: unknown, + label: string, +): StudioWorkflowCapabilitySource { + const record = expectRecord(value, label); + return { + kind: readExplicitRequestEnum( + record, + 'kind', + label + '.kind', + [ + 'connector_catalog', + 'nyxid_user_services', + 'nyxid_open_api', + 'durable_authorization_catalog', + 'nyxid_mcp_config', + ], + ), + sourceId: readNonBlankExplicitRequestString( + record, + 'sourceId', + label + '.sourceId', + ), + sourceVersion: readNumber( + record, + 'sourceVersion', + label + '.sourceVersion', + ), + observedAt: readNullableString(record, 'observedAt', label + '.observedAt'), + freshUntil: readNullableString( + record, + 'freshUntil', + label + '.freshUntil', + ), + }; +} + +function decodeNullableWorkflowCapabilitySource( + value: unknown, + label: string, +): StudioWorkflowCapabilitySource | null { + return value == null + ? null + : decodeStudioWorkflowCapabilitySource(value, label); +} + +function decodeStudioWorkflowCapabilityDescriptor( + value: unknown, + label = 'StudioWorkflowCapabilityDescriptor', +): StudioWorkflowCapabilityDescriptor { + const record = expectRecord(value, label); + return { + displayName: readNonBlankExplicitRequestString( + record, + 'displayName', + label + '.displayName', + ), + readOnly: readBoolean(record, 'readOnly', label + '.readOnly'), + destructive: readBoolean( + record, + 'destructive', + label + '.destructive', + ), + selector: decodeStudioWorkflowCapabilitySelector( + record.selector, + label + '.selector', + ), + source: decodeNullableWorkflowCapabilitySource( + record.source, + label + '.source', + ), + }; +} + +function decodeStudioWorkflowCapabilityDiagnostic( + value: unknown, + label = 'StudioWorkflowCapabilityDiagnostic', +): StudioWorkflowCapabilityDiagnostic { + const record = expectRecord(value, label); + return { + code: readExplicitRequestEnum( + record, + 'code', + label + '.code', + [ + 'source_unavailable', + 'no_exact_user_service', + 'generic_proxy_rejected', + 'invalid_service_identity', + 'ambiguous_service_identity', + 'invalid_endpoint_identity', + 'ambiguous_endpoint_identity', + 'unsupported_parameter', + 'unsupported_request_body', + 'unsupported_schema', + 'unsupported_response', + ], + ), + safeMessage: readString(record, 'safeMessage', label + '.safeMessage'), + count: readNumber(record, 'count', label + '.count'), + source: decodeNullableWorkflowCapabilitySource( + record.source, + label + '.source', + ), + }; +} + +function decodeStudioWorkflowCapabilityList( + value: unknown, +): StudioWorkflowCapabilityList { + const record = expectRecord(value, 'StudioWorkflowCapabilityList'); + const capabilities = expectArray( + record.capabilities, + 'StudioWorkflowCapabilityList.capabilities', + decodeStudioWorkflowCapabilityDescriptor, + ); + const selectorKeys = new Set(); + for (const descriptor of capabilities) { + const selectorKey = workflowCapabilitySelectorKey(descriptor.selector); + if (selectorKeys.has(selectorKey)) { + throw new Error( + 'StudioWorkflowCapabilityList.capabilities contains a duplicate selector.', + ); + } + selectorKeys.add(selectorKey); + } + + return { + capabilities, + candidateCount: readNumber( + record, + 'candidateCount', + 'StudioWorkflowCapabilityList.candidateCount', + ), + rejectedCount: readNumber( + record, + 'rejectedCount', + 'StudioWorkflowCapabilityList.rejectedCount', + ), + diagnostics: expectArray( + record.diagnostics, + 'StudioWorkflowCapabilityList.diagnostics', + decodeStudioWorkflowCapabilityDiagnostic, + ), + }; +} + +function decodeStudioWorkflowCapabilitySchema( + value: unknown, + label: string, +): StudioWorkflowCapabilitySchema { + const record = expectRecord(value, label); + return { + valueKind: readExplicitRequestEnum( + record, + 'valueKind', + label + '.valueKind', + ['string', 'integer', 'number', 'boolean', 'object', 'array'], + ), + properties: expectArray( + record.properties, + label + '.properties', + (entry, entryLabel = label + '.properties[]') => { + const property = expectRecord(entry, entryLabel); + return { + name: readNonBlankExplicitRequestString( + property, + 'name', + entryLabel + '.name', + ), + schema: decodeStudioWorkflowCapabilitySchema( + property.schema, + entryLabel + '.schema', + ), + }; + }, + ), + requiredProperties: expectArray( + record.requiredProperties, + label + '.requiredProperties', + expectString, + ), + items: + record.items == null + ? null + : decodeStudioWorkflowCapabilitySchema( + record.items, + label + '.items', + ), + allowedValues: expectArray( + record.allowedValues, + label + '.allowedValues', + expectString, + ), + additionalPropertiesAllowed: readBoolean( + record, + 'additionalPropertiesAllowed', + label + '.additionalPropertiesAllowed', + ), + }; +} + +function decodeStudioWorkflowCapabilityParameter( + value: unknown, + label = 'StudioWorkflowCapabilityParameter', +): StudioWorkflowCapabilityParameter { + const record = expectRecord(value, label); + return { + name: readNonBlankExplicitRequestString( + record, + 'name', + label + '.name', + ), + location: + readExplicitRequestEnum( + record, + 'location', + label + '.location', + ['path', 'query', 'header'], + ), + required: readBoolean(record, 'required', label + '.required'), + schema: decodeStudioWorkflowCapabilitySchema( + record.schema, + label + '.schema', + ), + }; +} + +function decodeStudioWorkflowCapabilityOperation( + value: unknown, + label = 'StudioWorkflowCapabilityOperation', +): StudioWorkflowCapabilityOperation { + const record = expectRecord(value, label); + const requestBody = + record.requestBody == null + ? null + : expectRecord(record.requestBody, label + '.requestBody'); + const responsePolicy = + record.responsePolicy == null + ? null + : expectRecord(record.responsePolicy, label + '.responsePolicy'); + const executionPolicy = + record.executionPolicy == null + ? null + : expectRecord(record.executionPolicy, label + '.executionPolicy'); + + return { + userServiceId: readNonBlankExplicitRequestString( + record, + 'userServiceId', + label + '.userServiceId', + ), + endpointId: readNonBlankExplicitRequestString( + record, + 'endpointId', + label + '.endpointId', + ), + serviceSlug: readString(record, 'serviceSlug', label + '.serviceSlug'), + httpMethod: readNonBlankExplicitRequestString( + record, + 'httpMethod', + label + '.httpMethod', + ), + pathTemplate: readNonBlankExplicitRequestString( + record, + 'pathTemplate', + label + '.pathTemplate', + ), + parameters: expectArray( + record.parameters, + label + '.parameters', + decodeStudioWorkflowCapabilityParameter, + ), + requestBody: requestBody + ? { + required: readBoolean( + requestBody, + 'required', + label + '.requestBody.required', + ), + mediaType: readString( + requestBody, + 'mediaType', + label + '.requestBody.mediaType', + ), + schema: decodeStudioWorkflowCapabilitySchema( + requestBody.schema, + label + '.requestBody.schema', + ), + } + : null, + responsePolicy: responsePolicy + ? { + textAllowed: readBoolean( + responsePolicy, + 'textAllowed', + label + '.responsePolicy.textAllowed', + ), + fileArtifactAllowed: readBoolean( + responsePolicy, + 'fileArtifactAllowed', + label + '.responsePolicy.fileArtifactAllowed', + ), + mediaTypes: expectArray( + responsePolicy.mediaTypes, + label + '.responsePolicy.mediaTypes', + expectString, + ), + } + : null, + executionPolicy: executionPolicy + ? { + risk: readExplicitRequestEnum( + executionPolicy, + 'risk', + label + '.executionPolicy.risk', + ['read_only', 'write', 'destructive'], + ), + approval: readExplicitRequestEnum( + executionPolicy, + 'approval', + label + '.executionPolicy.approval', + ['none', 'required'], + ), + enforcementOwner: readExplicitRequestEnum( + executionPolicy, + 'enforcementOwner', + label + '.executionPolicy.enforcementOwner', + ['aevatar', 'nyxid'], + ), + allowedExecutionModes: expectArray( + executionPolicy.allowedExecutionModes, + label + '.executionPolicy.allowedExecutionModes', + (entry, entryLabel = label + '.executionPolicy.allowedExecutionModes[]') => { + if (entry !== 'interactive' && entry !== 'durable') { + throw new Error(entryLabel + ' is not supported.'); + } + return entry; + }, + ), + } + : null, + }; +} + +const workflowCapabilityReadinessStatuses = [ + 'selection_required', + 'connector_not_found', + 'service_registration_required', + 'credential_connection_required', + 'service_access_denied', + 'node_binding_required', + 'node_unavailable', + 'endpoint_contract_required', + 'operation_selection_required', + 'source_stale', + 'durable_authorization_unavailable', + 'contract_drift', + 'ready', + 'admission_rebind_required', +] as const satisfies readonly StudioWorkflowCapabilityReadinessStatus[]; + +function decodeStudioWorkflowCapabilityReadiness( + value: unknown, + expectedSelector: StudioWorkflowCapabilityReadinessInput['selector'], +): StudioWorkflowCapabilityReadiness { + const label = 'StudioWorkflowCapabilityReadiness'; + const record = expectRecord(value, label); + const selectedSelector = + record.selectedSelector == null + ? null + : decodeStudioWorkflowCapabilitySelector( + record.selectedSelector, + label + '.selectedSelector', + ); + if ( + selectedSelector && + workflowCapabilitySelectorKey(selectedSelector) !== + workflowCapabilitySelectorKey(expectedSelector) + ) { + throw new Error( + 'StudioWorkflowCapabilityReadiness returned a different selectedSelector.', + ); + } + + return { + executionMode: readExplicitRequestEnum( + record, + 'executionMode', + label + '.executionMode', + ['interactive', 'durable'], + ), + status: readExplicitRequestEnum( + record, + 'status', + label + '.status', + workflowCapabilityReadinessStatuses, + ), + selectedSelector, + selectedOperation: + record.selectedOperation == null + ? null + : decodeStudioWorkflowCapabilityOperation( + record.selectedOperation, + label + '.selectedOperation', + ), + blockers: expectArray( + record.blockers, + label + '.blockers', + (entry, entryLabel = label + '.blockers[]') => { + const blocker = expectRecord(entry, entryLabel); + return { + status: + readExplicitRequestEnum( + blocker, + 'status', + entryLabel + '.status', + workflowCapabilityReadinessStatuses, + ), + code: readString(blocker, 'code', entryLabel + '.code'), + safeMessage: readString( + blocker, + 'safeMessage', + entryLabel + '.safeMessage', + ), + }; + }, + ), + remediations: expectArray( + record.remediations, + label + '.remediations', + (entry, entryLabel = label + '.remediations[]') => { + const remediation = expectRecord(entry, entryLabel); + return { + actionKind: + readExplicitRequestEnum( + remediation, + 'actionKind', + entryLabel + '.actionKind', + [ + 'select_capability', + 'configure_connector', + 'register_service', + 'connect_credential', + 'request_access', + 'bind_node', + 'restore_node', + 'publish_endpoint_contract', + 'select_operation', + 'refresh_source', + 'use_interactive_execution', + 'rebind_workflow', + ], + ), + label: readString(remediation, 'label', entryLabel + '.label'), + trustedLocator: readString( + remediation, + 'trustedLocator', + entryLabel + '.trustedLocator', + ), + }; + }, + ), + sources: expectArray( + record.sources, + label + '.sources', + decodeStudioWorkflowCapabilitySource, + ), + }; +} + function toScopeWorkflowDirectoryId(scopeId: string): string { return `scope:${scopeId}`; } @@ -3277,6 +3883,37 @@ export const studioApi = { ); }, + listWorkflowCapabilities( + scopeId: string, + ): Promise { + return requestDecodedJson( + '/api/scopes/' + + encodeURIComponent(scopeId.trim()) + + '/workflow-capabilities', + decodeStudioWorkflowCapabilityList, + ); + }, + + inspectWorkflowCapabilityReadiness( + input: StudioWorkflowCapabilityReadinessInput, + ): Promise { + return requestDecodedJson( + '/api/scopes/' + + encodeURIComponent(input.scopeId.trim()) + + '/workflow-capabilities:readiness', + (value) => + decodeStudioWorkflowCapabilityReadiness(value, input.selector), + { + method: 'POST', + headers: JSON_HEADERS, + body: JSON.stringify({ + selector: input.selector, + executionMode: input.executionMode, + }), + }, + ); + }, + previewExplicitRequests( input: StudioExplicitRequestPreviewInput, ): Promise { diff --git a/apps/aevatar-console-web/src/shared/studio/models.ts b/apps/aevatar-console-web/src/shared/studio/models.ts index d319f54784..c100fea66c 100644 --- a/apps/aevatar-console-web/src/shared/studio/models.ts +++ b/apps/aevatar-console-web/src/shared/studio/models.ts @@ -201,6 +201,200 @@ export type StudioExplicitRequestRisk = 'read_only' | 'write' | 'destructive'; export type StudioExplicitRequestExecutionMode = 'interactive' | 'durable'; +export type StudioWorkflowCapabilitySelector = + | { + readonly kind: 'nyxid_operation'; + readonly userServiceId: string; + readonly endpointId: string; + } + | { + readonly kind: 'host_connector'; + readonly connectorCapabilityRef: string; + readonly operationId: string; + readonly contractDigest: string; + } + | { + readonly kind: 'nyxid_request'; + readonly userServiceId: string; + readonly method: StudioExplicitRequestMethod; + readonly pathTemplate: string; + readonly queryParameters: readonly string[]; + readonly headerParameters: readonly string[]; + readonly bodyMode: StudioExplicitRequestBodyMode; + readonly responseMode: StudioExplicitRequestResponseMode; + readonly bodyRequired: boolean; + }; + +export type StudioWorkflowCapabilitySourceKind = + | 'connector_catalog' + | 'nyxid_user_services' + | 'nyxid_open_api' + | 'durable_authorization_catalog' + | 'nyxid_mcp_config'; + +export interface StudioWorkflowCapabilitySource { + readonly kind: StudioWorkflowCapabilitySourceKind; + readonly sourceId: string; + readonly sourceVersion: number; + readonly observedAt: string | null; + readonly freshUntil: string | null; +} + +export interface StudioWorkflowCapabilityDescriptor { + readonly displayName: string; + readonly readOnly: boolean; + readonly destructive: boolean; + readonly selector: StudioWorkflowCapabilitySelector; + readonly source: StudioWorkflowCapabilitySource | null; +} + +export type StudioWorkflowCapabilityDiagnosticCode = + | 'source_unavailable' + | 'no_exact_user_service' + | 'generic_proxy_rejected' + | 'invalid_service_identity' + | 'ambiguous_service_identity' + | 'invalid_endpoint_identity' + | 'ambiguous_endpoint_identity' + | 'unsupported_parameter' + | 'unsupported_request_body' + | 'unsupported_schema' + | 'unsupported_response'; + +export interface StudioWorkflowCapabilityDiagnostic { + readonly code: StudioWorkflowCapabilityDiagnosticCode; + readonly safeMessage: string; + readonly count: number; + readonly source: StudioWorkflowCapabilitySource | null; +} + +export interface StudioWorkflowCapabilityList { + readonly capabilities: readonly StudioWorkflowCapabilityDescriptor[]; + readonly candidateCount: number; + readonly rejectedCount: number; + readonly diagnostics: readonly StudioWorkflowCapabilityDiagnostic[]; +} + +export type StudioWorkflowCapabilityReadinessStatus = + | 'selection_required' + | 'connector_not_found' + | 'service_registration_required' + | 'credential_connection_required' + | 'service_access_denied' + | 'node_binding_required' + | 'node_unavailable' + | 'endpoint_contract_required' + | 'operation_selection_required' + | 'source_stale' + | 'durable_authorization_unavailable' + | 'contract_drift' + | 'ready' + | 'admission_rebind_required'; + +export type StudioWorkflowCapabilityRemediationAction = + | 'select_capability' + | 'configure_connector' + | 'register_service' + | 'connect_credential' + | 'request_access' + | 'bind_node' + | 'restore_node' + | 'publish_endpoint_contract' + | 'select_operation' + | 'refresh_source' + | 'use_interactive_execution' + | 'rebind_workflow'; + +export type StudioWorkflowCapabilityParameterLocation = + | 'path' + | 'query' + | 'header'; + +export type StudioWorkflowCapabilityValueKind = + | 'string' + | 'integer' + | 'number' + | 'boolean' + | 'object' + | 'array'; + +export interface StudioWorkflowCapabilitySchema { + readonly valueKind: StudioWorkflowCapabilityValueKind; + readonly properties: readonly StudioWorkflowCapabilitySchemaProperty[]; + readonly requiredProperties: readonly string[]; + readonly items: StudioWorkflowCapabilitySchema | null; + readonly allowedValues: readonly string[]; + readonly additionalPropertiesAllowed: boolean; +} + +export interface StudioWorkflowCapabilitySchemaProperty { + readonly name: string; + readonly schema: StudioWorkflowCapabilitySchema; +} + +export interface StudioWorkflowCapabilityParameter { + readonly name: string; + readonly location: StudioWorkflowCapabilityParameterLocation; + readonly required: boolean; + readonly schema: StudioWorkflowCapabilitySchema; +} + +export interface StudioWorkflowCapabilityOperation { + readonly userServiceId: string; + readonly endpointId: string; + readonly serviceSlug: string; + readonly httpMethod: string; + readonly pathTemplate: string; + readonly parameters: readonly StudioWorkflowCapabilityParameter[]; + readonly requestBody: { + readonly required: boolean; + readonly mediaType: string; + readonly schema: StudioWorkflowCapabilitySchema; + } | null; + readonly responsePolicy: { + readonly textAllowed: boolean; + readonly fileArtifactAllowed: boolean; + readonly mediaTypes: readonly string[]; + } | null; + readonly executionPolicy: { + readonly risk: StudioExplicitRequestRisk; + readonly approval: 'none' | 'required'; + readonly enforcementOwner: 'aevatar' | 'nyxid'; + readonly allowedExecutionModes: readonly StudioExplicitRequestExecutionMode[]; + } | null; +} + +export interface StudioWorkflowCapabilityBlocker { + readonly status: StudioWorkflowCapabilityReadinessStatus; + readonly code: string; + readonly safeMessage: string; +} + +export interface StudioWorkflowCapabilityRemediation { + readonly actionKind: StudioWorkflowCapabilityRemediationAction; + readonly label: string; + readonly trustedLocator: string; +} + +export interface StudioWorkflowCapabilityReadiness { + readonly executionMode: StudioExplicitRequestExecutionMode; + readonly status: StudioWorkflowCapabilityReadinessStatus; + readonly selectedSelector: StudioWorkflowCapabilitySelector | null; + readonly selectedOperation: StudioWorkflowCapabilityOperation | null; + readonly blockers: readonly StudioWorkflowCapabilityBlocker[]; + readonly remediations: readonly StudioWorkflowCapabilityRemediation[]; + readonly sources: readonly StudioWorkflowCapabilitySource[]; +} + +export interface StudioWorkflowCapabilityReadinessInput { + readonly scopeId: string; + readonly selector: Extract< + StudioWorkflowCapabilitySelector, + { readonly kind: 'nyxid_operation' } + >; + readonly executionMode: 'interactive'; +} + export interface StudioExplicitRequestPreviewInput { readonly scopeId: string; readonly workflowId: string; From 93904886bd508e4b33f945afb8e3e0cc43aa4282 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:09:10 +0800 Subject: [PATCH 077/167] Preserve workflow capability in inspector drafts --- .../src/shared/studio/document.test.ts | 108 ++++++++++++++++++ .../src/shared/studio/document.ts | 14 ++- .../src/shared/studio/graph.test.ts | 26 +++++ .../src/shared/studio/graph.ts | 8 ++ .../src/shared/studio/models.ts | 38 ++++++ 5 files changed, 193 insertions(+), 1 deletion(-) diff --git a/apps/aevatar-console-web/src/shared/studio/document.test.ts b/apps/aevatar-console-web/src/shared/studio/document.test.ts index a2905f2d2b..567646880d 100644 --- a/apps/aevatar-console-web/src/shared/studio/document.test.ts +++ b/apps/aevatar-console-web/src/shared/studio/document.test.ts @@ -2,6 +2,7 @@ import { applyRoleInspectorDraft, applyStepInspectorDraft, connectStepToTarget, + createStepInspectorDraft, insertStepAfter, insertStepByType, materializeImplicitSequentialTransitions, @@ -15,6 +16,110 @@ import { import type { StudioWorkflowDocument } from './models'; describe('studio document helpers', () => { + it('round-trips an exact external operation capability independently from parameters', () => { + const draft = createStepInspectorDraft({ + id: 'posthog_step', + type: 'tool_call', + targetRole: '', + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, + }, + parameters: { + tool: 'nyxid_proxy', + arguments: '{"query":{"limit":10}}', + }, + next: null, + branches: {}, + }); + + expect(draft.capability).toEqual({ + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, + }); + + const result = applyStepInspectorDraft( + { + steps: [ + { + id: 'posthog_step', + type: 'tool_call', + capability: draft.capability, + parameters: { + tool: 'nyxid_proxy', + arguments: '{"query":{"limit":10}}', + }, + }, + ], + }, + 'posthog_step', + { + ...draft, + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-beta', + endpoint_id: 'update-dashboard', + }, + }, + }, + ); + + expect(result.document.steps?.[0]).toEqual( + expect.objectContaining({ + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-beta', + endpoint_id: 'update-dashboard', + }, + }, + parameters: { + tool: 'nyxid_proxy', + arguments: '{"query":{"limit":10}}', + }, + }), + ); + }); + + it('removes an external capability without deleting runtime parameters', () => { + const result = applyStepInspectorDraft( + { + steps: [ + { + id: 'posthog_step', + type: 'tool_call', + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, + }, + parameters: { tool: 'nyxid_proxy' }, + }, + ], + }, + 'posthog_step', + { + kind: 'step', + id: 'posthog_step', + type: 'tool_call', + targetRole: '', + next: '', + branchesText: '{}', + parametersText: '{"tool":"nyxid_proxy"}', + capability: null, + }, + ); + + expect(result.document.steps?.[0]).not.toHaveProperty('capability'); + expect(result.document.steps?.[0]?.parameters).toEqual({ + tool: 'nyxid_proxy', + }); + }); + it('materializes only eligible implicit sequential transitions without mutating the document', () => { const document: StudioWorkflowDocument = { name: 'workspace-demo', @@ -222,6 +327,7 @@ describe('studio document helpers', () => { next: 'approve_step', branchesText: '{"approved":"approve_step"}', parametersText: '{"connector":"web-search","limit":3}', + capability: null, }); expect(result.nodeId).toBe('step:review_step'); @@ -276,6 +382,7 @@ describe('studio document helpers', () => { parametersText: JSON.stringify({ prompt: 'Translate the input to Japanese.', }), + capability: null, }); expect(result.document.steps?.[0]?.parameters).toEqual({ @@ -311,6 +418,7 @@ describe('studio document helpers', () => { parametersText: JSON.stringify({ prompt: 'Approve the generated response?', }), + capability: null, }); expect(result.document.steps?.[0]?.parameters).toEqual({ diff --git a/apps/aevatar-console-web/src/shared/studio/document.ts b/apps/aevatar-console-web/src/shared/studio/document.ts index 91af54258f..1e478dacbc 100644 --- a/apps/aevatar-console-web/src/shared/studio/document.ts +++ b/apps/aevatar-console-web/src/shared/studio/document.ts @@ -1,9 +1,11 @@ import type { StudioConnectorDefinition, + StudioWorkflowCapability, StudioWorkflowDocument, StudioWorkflowRoleDocument, StudioWorkflowStepDocument, } from './models'; +import { normalizeStudioWorkflowCapability } from './models'; export type StudioStepInspectorDraft = { readonly kind: 'step'; @@ -13,6 +15,7 @@ export type StudioStepInspectorDraft = { readonly next: string; readonly branchesText: string; readonly parametersText: string; + readonly capability: StudioWorkflowCapability | null; }; export type StudioRoleInspectorDraft = { @@ -317,6 +320,7 @@ export function createStepInspectorDraft(step: { id: string; type: string; targetRole: string; + capability?: StudioWorkflowCapability | null; parameters: Record; next: string | null; branches?: Record; @@ -329,6 +333,7 @@ export function createStepInspectorDraft(step: { next: step.next ?? '', branchesText: formatInspectorBranches(step.branches), parametersText: formatInspectorParameters(step.parameters), + capability: normalizeStudioWorkflowCapability(step.capability), }; } @@ -372,7 +377,7 @@ export function applyStepInspectorDraft( const stepId = normalizeString(step.id); if (stepId === currentStepId) { delete step.target_role; - return { + const updatedStep = { ...step, id: nextId, type: nextType, @@ -382,6 +387,13 @@ export function applyStepInspectorDraft( next: nextStepId || null, branches: nextBranches, } satisfies StudioWorkflowStepDocument; + const nextCapability = normalizeStudioWorkflowCapability(draft.capability); + if (nextCapability) { + updatedStep.capability = nextCapability; + } else { + delete updatedStep.capability; + } + return updatedStep; } const updatedBranches = Object.fromEntries( diff --git a/apps/aevatar-console-web/src/shared/studio/graph.test.ts b/apps/aevatar-console-web/src/shared/studio/graph.test.ts index 4d62d533ce..ef5c5b2f8b 100644 --- a/apps/aevatar-console-web/src/shared/studio/graph.test.ts +++ b/apps/aevatar-console-web/src/shared/studio/graph.test.ts @@ -1,6 +1,32 @@ import { buildStudioGraphElements, formatStudioStepTypeLabel } from './graph'; describe('studio graph helpers', () => { + it('preserves an exact external operation capability for inspector selection', () => { + const graph = buildStudioGraphElements({ + name: 'workflow-demo', + steps: [ + { + id: 'posthog_step', + type: 'tool_call', + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, + }, + parameters: { tool: 'nyxid_proxy' }, + }, + ], + }); + + expect(graph.steps[0]?.capability).toEqual({ + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'list-dashboards', + }, + }); + }); + it('formats step type labels without leaking backend identifiers', () => { expect(formatStudioStepTypeLabel('llm_call')).toBe('LLM call'); expect(formatStudioStepTypeLabel('workflow_yaml_validate')).toBe( diff --git a/apps/aevatar-console-web/src/shared/studio/graph.ts b/apps/aevatar-console-web/src/shared/studio/graph.ts index fb7ef1ad4f..72d004e71e 100644 --- a/apps/aevatar-console-web/src/shared/studio/graph.ts +++ b/apps/aevatar-console-web/src/shared/studio/graph.ts @@ -6,6 +6,10 @@ import { type XYPosition, } from '@xyflow/react'; import { t } from '@/shared/i18n/messages'; +import { + normalizeStudioWorkflowCapability, + type StudioWorkflowCapability, +} from './models'; export type StudioGraphRole = { readonly id: string; @@ -20,6 +24,7 @@ export type StudioGraphStep = { readonly id: string; readonly type: string; readonly targetRole: string; + readonly capability: StudioWorkflowCapability | null; readonly parameters: Record; readonly next: string | null; readonly branches: Record; @@ -285,6 +290,9 @@ function normalizeSteps(document: WorkflowDocumentLike): StudioGraphStep[] { id, type, targetRole, + capability: normalizeStudioWorkflowCapability( + (payload as { capability?: unknown }).capability, + ), parameters: normalizeParameters( (payload as { parameters?: unknown }).parameters, ), diff --git a/apps/aevatar-console-web/src/shared/studio/models.ts b/apps/aevatar-console-web/src/shared/studio/models.ts index c100fea66c..8e57ade5e5 100644 --- a/apps/aevatar-console-web/src/shared/studio/models.ts +++ b/apps/aevatar-console-web/src/shared/studio/models.ts @@ -19,6 +19,7 @@ export type StudioWorkflowStepDocument = Record & { id?: string; type?: string; originalType?: string; + capability?: StudioWorkflowCapability | null; targetRole?: string | null; target_role?: string | null; toolSets?: string[] | null; @@ -27,6 +28,43 @@ export type StudioWorkflowStepDocument = Record & { branches?: Record | null; }; +export type StudioWorkflowCapability = { + readonly nyxid_operation: { + readonly user_service_id: string; + readonly endpoint_id: string; + }; +}; + +export function normalizeStudioWorkflowCapability( + value: unknown, +): StudioWorkflowCapability | null { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + return null; + } + + const operation = (value as Record).nyxid_operation; + if (!operation || typeof operation !== 'object' || Array.isArray(operation)) { + return null; + } + + const userServiceId = String( + (operation as Record).user_service_id ?? '', + ).trim(); + const endpointId = String( + (operation as Record).endpoint_id ?? '', + ).trim(); + if (!userServiceId || !endpointId) { + return null; + } + + return { + nyxid_operation: { + user_service_id: userServiceId, + endpoint_id: endpointId, + }, + }; +} + export type StudioWorkflowDocument = Record & { name?: string; description?: string; From db874492b43eb5b81152d91c4ee25007f1dae34b Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:18:02 +0800 Subject: [PATCH 078/167] Model guided tool call inputs --- .../workflows/toolCallConfiguration.test.ts | 275 ++++++++++++++++ .../workflows/toolCallConfiguration.ts | 300 ++++++++++++++++++ 2 files changed, 575 insertions(+) create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts new file mode 100644 index 0000000000..fcfd25a92b --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.test.ts @@ -0,0 +1,275 @@ +import type { + StudioWorkflowCapabilityOperation, + StudioWorkflowCapabilitySelector, +} from '@/shared/studio/models'; +import { + capabilitySelectorKey, + formatToolArguments, + listOperationInputFields, + parseToolArguments, + readOperationInputValue, + toDocumentCapability, + writeOperationInputValue, +} from './toolCallConfiguration'; + +const selector: Extract< + StudioWorkflowCapabilitySelector, + { readonly kind: 'nyxid_operation' } +> = { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', +}; + +const operation: StudioWorkflowCapabilityOperation = { + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + serviceSlug: 'posthog', + httpMethod: 'PATCH', + pathTemplate: '/api/dashboards/{dashboard_id}', + parameters: [ + { + name: 'dashboard_id', + location: 'path', + required: true, + schema: { + valueKind: 'integer', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + { + name: 'include_archived', + location: 'query', + required: false, + schema: { + valueKind: 'boolean', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + ], + requestBody: { + required: true, + mediaType: 'application/json', + schema: { + valueKind: 'object', + properties: [ + { + name: 'name', + schema: { + valueKind: 'string', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + { + name: 'filters', + schema: { + valueKind: 'array', + properties: [], + requiredProperties: [], + items: { + valueKind: 'string', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + ], + requiredProperties: ['name'], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + responsePolicy: { + textAllowed: true, + fileArtifactAllowed: false, + mediaTypes: ['application/json'], + }, + executionPolicy: { + risk: 'write', + approval: 'required', + enforcementOwner: 'aevatar', + allowedExecutionModes: ['interactive'], + }, +}; + +describe('guided tool call configuration', () => { + it('uses exact selector identities without depending on display names', () => { + expect(capabilitySelectorKey(selector)).toBe( + 'nyxid_operation\u0000us-posthog-alpha\u0000update-dashboard', + ); + expect(toDocumentCapability(selector)).toEqual({ + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'update-dashboard', + }, + }); + }); + + it('maps declared path, query, and body properties into required-first inputs', () => { + expect( + listOperationInputFields(operation).map((field) => ({ + key: field.key, + group: field.group, + required: field.required, + valueKind: field.schema.valueKind, + })), + ).toEqual([ + { + key: 'path:dashboard_id', + group: 'path', + required: true, + valueKind: 'integer', + }, + { + key: 'body:name', + group: 'body', + required: true, + valueKind: 'string', + }, + { + key: 'query:include_archived', + group: 'query', + required: false, + valueKind: 'boolean', + }, + { + key: 'body:filters', + group: 'body', + required: false, + valueKind: 'array', + }, + ]); + }); + + it('preserves unknown argument keys while updating one declared input', () => { + const parsed = parseToolArguments( + JSON.stringify({ + query: { existing: 'keep-me' }, + future_envelope: { version: 2 }, + response_mode: 'text', + }), + ); + expect(parsed.error).toBeNull(); + const queryField = listOperationInputFields(operation).find( + (field) => field.key === 'query:include_archived', + ); + expect(queryField).toBeDefined(); + + const updated = writeOperationInputValue( + parsed.arguments, + queryField!, + true, + ); + + expect(updated.error).toBeNull(); + expect(updated.arguments).toEqual({ + query: { existing: 'keep-me', include_archived: true }, + future_envelope: { version: 2 }, + response_mode: 'text', + }); + expect(JSON.parse(formatToolArguments(updated.arguments))).toEqual( + updated.arguments, + ); + }); + + it('keeps workflow expressions as strings even for numeric inputs', () => { + const workflowExpression = '$' + '{steps.lookup.output}'; + const pathField = listOperationInputFields(operation).find( + (field) => field.key === 'path:dashboard_id', + ); + const updated = writeOperationInputValue( + {}, + pathField!, + workflowExpression, + ); + + expect(updated.error).toBeNull(); + expect(readOperationInputValue(updated.arguments, pathField!)).toBe( + workflowExpression, + ); + }); + + it.each([ + ['integer', '42', 42], + ['array', '[\"one\",\"two\"]', ['one', 'two']], + ])('coerces valid %s literals', (kind, rawValue, expectedValue) => { + const field = listOperationInputFields(operation).find( + (entry) => entry.schema.valueKind === kind, + ); + expect(field).toBeDefined(); + + const updated = writeOperationInputValue({}, field!, rawValue); + + expect(updated.error).toBeNull(); + expect(readOperationInputValue(updated.arguments, field!)).toEqual( + expectedValue, + ); + }); + + it('coerces a boolean literal', () => { + const field = listOperationInputFields(operation).find( + (entry) => entry.schema.valueKind === 'boolean', + ); + const updated = writeOperationInputValue({}, field!, 'true'); + + expect(updated.error).toBeNull(); + expect(readOperationInputValue(updated.arguments, field!)).toBe(true); + }); + + it('reports invalid JSON without replacing the original argument text', () => { + const parsed = parseToolArguments('{not-json'); + + expect(parsed).toEqual({ + arguments: {}, + error: 'Action inputs must be a JSON object.', + originalText: '{not-json', + }); + }); + + it('removes an empty optional value and reports an empty required value', () => { + const [requiredField, , optionalField] = + listOperationInputFields(operation); + const initial = { + path_params: { dashboard_id: 42 }, + query: { include_archived: true }, + }; + + const optionalResult = writeOperationInputValue( + initial, + optionalField!, + '', + ); + expect(optionalResult.error).toBeNull(); + expect(optionalResult.arguments).toEqual({ + path_params: { dashboard_id: 42 }, + }); + + const requiredResult = writeOperationInputValue( + initial, + requiredField!, + '', + ); + expect(requiredResult.error).toBe('Dashboard id is required.'); + expect(requiredResult.arguments).toEqual({ + query: { include_archived: true }, + }); + }); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts new file mode 100644 index 0000000000..e8911e49bd --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/toolCallConfiguration.ts @@ -0,0 +1,300 @@ +import type { + StudioWorkflowCapability, + StudioWorkflowCapabilityOperation, + StudioWorkflowCapabilitySchema, + StudioWorkflowCapabilitySelector, +} from '@/shared/studio/models'; + +export type NyxIdOperationSelector = Extract< + StudioWorkflowCapabilitySelector, + { readonly kind: 'nyxid_operation' } +>; + +export type OperationInputField = { + readonly key: string; + readonly name: string; + readonly label: string; + readonly group: 'path' | 'query' | 'header' | 'body'; + readonly path: readonly string[]; + readonly required: boolean; + readonly schema: StudioWorkflowCapabilitySchema; +}; + +export type ToolArgumentParseResult = { + readonly arguments: Record; + readonly error: string | null; + readonly originalText: string; +}; + +export type ToolArgumentWriteResult = { + readonly arguments: Record; + readonly error: string | null; +}; + +export function capabilitySelectorKey(selector: NyxIdOperationSelector): string { + return [ + selector.kind, + selector.userServiceId, + selector.endpointId, + ].join('\u0000'); +} + +export function toDocumentCapability( + selector: NyxIdOperationSelector, +): StudioWorkflowCapability { + return { + nyxid_operation: { + user_service_id: selector.userServiceId, + endpoint_id: selector.endpointId, + }, + }; +} + +function humanizeInputName(name: string): string { + const words = name.trim().replace(/[_-]+/g, ' '); + return words ? words.charAt(0).toUpperCase() + words.slice(1) : 'Value'; +} + +function parameterPath( + location: 'path' | 'query' | 'header', + name: string, +): readonly string[] { + if (location === 'path') return ['path_params', name]; + if (location === 'header') return ['headers', name]; + return ['query', name]; +} + +export function listOperationInputFields( + operation: StudioWorkflowCapabilityOperation, +): readonly OperationInputField[] { + const parameterFields = operation.parameters.map((parameter) => ({ + key: parameter.location + ':' + parameter.name, + name: parameter.name, + label: humanizeInputName(parameter.name), + group: parameter.location, + path: parameterPath(parameter.location, parameter.name), + required: parameter.required, + schema: parameter.schema, + })); + const body = operation.requestBody; + const bodyFields: OperationInputField[] = []; + if (body) { + if (body.schema.valueKind === 'object' && body.schema.properties.length > 0) { + const requiredProperties = new Set(body.schema.requiredProperties); + for (const property of body.schema.properties) { + bodyFields.push({ + key: 'body:' + property.name, + name: property.name, + label: humanizeInputName(property.name), + group: 'body', + path: ['body', property.name], + required: requiredProperties.has(property.name), + schema: property.schema, + }); + } + } else { + bodyFields.push({ + key: 'body:body', + name: 'body', + label: 'Request body', + group: 'body', + path: ['body'], + required: body.required, + schema: body.schema, + }); + } + } + + return [...parameterFields, ...bodyFields].sort( + (left, right) => Number(right.required) - Number(left.required), + ); +} + +function isRecord(value: unknown): value is Record { + return Boolean(value) && typeof value === 'object' && !Array.isArray(value); +} + +export function parseToolArguments(value: unknown): ToolArgumentParseResult { + const originalText = + typeof value === 'string' + ? value + : value == null + ? '' + : JSON.stringify(value); + if (value == null || (typeof value === 'string' && !value.trim())) { + return { arguments: {}, error: null, originalText }; + } + if (isRecord(value)) { + return { arguments: { ...value }, error: null, originalText }; + } + + try { + const parsed = JSON.parse(originalText) as unknown; + if (!isRecord(parsed)) { + throw new Error('not an object'); + } + return { arguments: parsed, error: null, originalText }; + } catch { + return { + arguments: {}, + error: 'Action inputs must be a JSON object.', + originalText, + }; + } +} + +export function formatToolArguments( + argumentsValue: Record, +): string { + return JSON.stringify(argumentsValue); +} + +export function readOperationInputValue( + argumentsValue: Record, + field: OperationInputField, +): unknown { + let current: unknown = argumentsValue; + for (const segment of field.path) { + if (!isRecord(current)) return undefined; + current = current[segment]; + } + return current; +} + +function isWorkflowExpression(value: string): boolean { + const normalized = value.trim(); + return normalized.startsWith('$' + '{') && normalized.endsWith('}'); +} + +function coerceInputValue( + value: unknown, + field: OperationInputField, +): { value?: unknown; error: string | null } { + if (typeof value === 'string' && isWorkflowExpression(value)) { + return { value, error: null }; + } + if (field.schema.allowedValues.length > 0) { + const normalized = String(value); + if (!field.schema.allowedValues.includes(normalized)) { + return { + error: field.label + ' must be one of the available values.', + }; + } + return { value: normalized, error: null }; + } + + if (field.schema.valueKind === 'string') { + return { value: String(value), error: null }; + } + if (field.schema.valueKind === 'boolean') { + if (typeof value === 'boolean') return { value, error: null }; + if (value === 'true') return { value: true, error: null }; + if (value === 'false') return { value: false, error: null }; + return { error: field.label + ' must be true or false.' }; + } + if ( + field.schema.valueKind === 'integer' || + field.schema.valueKind === 'number' + ) { + const numberValue = + typeof value === 'number' ? value : Number(String(value).trim()); + const valid = + Number.isFinite(numberValue) && + (field.schema.valueKind !== 'integer' || Number.isInteger(numberValue)); + return valid + ? { value: numberValue, error: null } + : { + error: + field.label + + (field.schema.valueKind === 'integer' + ? ' must be a whole number.' + : ' must be a number.'), + }; + } + + let structuredValue = value; + if (typeof value === 'string') { + try { + structuredValue = JSON.parse(value); + } catch { + return { error: field.label + ' must be valid JSON.' }; + } + } + const valid = + field.schema.valueKind === 'array' + ? Array.isArray(structuredValue) + : isRecord(structuredValue); + return valid + ? { value: structuredValue, error: null } + : { + error: + field.label + + (field.schema.valueKind === 'array' + ? ' must be a JSON array.' + : ' must be a JSON object.'), + }; +} + +function deletePath( + argumentsValue: Record, + path: readonly string[], +): Record { + const next = { ...argumentsValue }; + if (path.length === 1) { + delete next[path[0]]; + return next; + } + + const [parentKey, childKey] = path; + const parent = isRecord(next[parentKey]) ? { ...next[parentKey] } : {}; + delete parent[childKey]; + if (Object.keys(parent).length > 0) { + next[parentKey] = parent; + } else { + delete next[parentKey]; + } + return next; +} + +function writePath( + argumentsValue: Record, + path: readonly string[], + value: unknown, +): Record { + const next = { ...argumentsValue }; + if (path.length === 1) { + next[path[0]] = value; + return next; + } + + const [parentKey, childKey] = path; + const parent = isRecord(next[parentKey]) ? { ...next[parentKey] } : {}; + parent[childKey] = value; + next[parentKey] = parent; + return next; +} + +export function writeOperationInputValue( + argumentsValue: Record, + field: OperationInputField, + rawValue: unknown, +): ToolArgumentWriteResult { + const empty = + rawValue == null || + (typeof rawValue === 'string' && rawValue.trim().length === 0); + if (empty) { + return { + arguments: deletePath(argumentsValue, field.path), + error: field.required ? field.label + ' is required.' : null, + }; + } + + const coerced = coerceInputValue(rawValue, field); + if (coerced.error) { + return { arguments: argumentsValue, error: coerced.error }; + } + return { + arguments: writePath(argumentsValue, field.path, coerced.value), + error: null, + }; +} From 1271d0d9702c379af3f0bf8479c340b427cf040d Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:30:25 +0800 Subject: [PATCH 079/167] Guide external tool call configuration --- .../WorkflowToolCallConfiguration.test.tsx | 269 ++++++++++ .../WorkflowToolCallConfiguration.tsx | 476 ++++++++++++++++++ 2 files changed, 745 insertions(+) create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx new file mode 100644 index 0000000000..c9bde2bf94 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx @@ -0,0 +1,269 @@ +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query'; +import React from 'react'; +import { studioApi } from '@/shared/studio/api'; +import type { + StudioWorkflowCapability, + StudioWorkflowCapabilityList, + StudioWorkflowCapabilityReadiness, +} from '@/shared/studio/models'; +import WorkflowToolCallConfiguration, { + type WorkflowToolCallConfigurationChange, +} from './WorkflowToolCallConfiguration'; + +const capabilityList: StudioWorkflowCapabilityList = { + capabilities: [ + { + displayName: 'PostHog / Update dashboard', + readOnly: false, + destructive: false, + selector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + source: { + kind: 'nyxid_user_services', + sourceId: 'source-posthog-alpha', + sourceVersion: 7, + observedAt: '2026-09-02T08:00:00Z', + freshUntil: '2026-09-02T08:05:00Z', + }, + }, + ], + candidateCount: 1, + rejectedCount: 0, + diagnostics: [], +}; + +const readyCapability: StudioWorkflowCapabilityReadiness = { + executionMode: 'interactive', + status: 'ready', + selectedSelector: { + kind: 'nyxid_operation', + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + }, + selectedOperation: { + userServiceId: 'us-posthog-alpha', + endpointId: 'update-dashboard', + serviceSlug: 'posthog', + httpMethod: 'PATCH', + pathTemplate: '/api/dashboards/{dashboard_id}', + parameters: [ + { + name: 'dashboard_id', + location: 'path', + required: true, + schema: { + valueKind: 'integer', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + { + name: 'include_archived', + location: 'query', + required: false, + schema: { + valueKind: 'boolean', + properties: [], + requiredProperties: [], + items: null, + allowedValues: [], + additionalPropertiesAllowed: false, + }, + }, + ], + requestBody: null, + responsePolicy: { + textAllowed: true, + fileArtifactAllowed: false, + mediaTypes: ['application/json'], + }, + executionPolicy: { + risk: 'write', + approval: 'required', + enforcementOwner: 'aevatar', + allowedExecutionModes: ['interactive'], + }, + }, + blockers: [], + remediations: [], + sources: [], +}; + +function renderConfiguration(input?: { + readonly capability?: StudioWorkflowCapability | null; + readonly parameters?: Record; + readonly onChange?: jest.Mock; +}) { + const onChange = input?.onChange ?? jest.fn(); + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + + function Harness() { + const [capability, setCapability] = React.useState( + input?.capability ?? null, + ); + const [parameters, setParameters] = React.useState( + input?.parameters ?? {}, + ); + return ( + + { + onChange(change); + setCapability(change.capability); + setParameters(change.parameters); + }} + onErrorChange={jest.fn()} + parameters={parameters} + scopeId="scope-alpha" + /> + + ); + } + + return { ...render(), onChange }; +} + +describe('WorkflowToolCallConfiguration', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('leads with an action picker and writes the exact selected operation', async () => { + jest + .spyOn(studioApi, 'listWorkflowCapabilities') + .mockResolvedValue(capabilityList); + jest + .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') + .mockResolvedValue(readyCapability); + const { onChange } = renderConfiguration({ + parameters: { tool: 'nyxid_proxy' }, + }); + + const actionPicker = await screen.findByRole('combobox', { + name: 'Action', + }); + await waitFor(() => expect(actionPicker).not.toBeDisabled()); + expect(screen.queryByDisplayValue('nyxid_proxy')).not.toBeInTheDocument(); + fireEvent.mouseDown(actionPicker); + fireEvent.click(await screen.findByText('PostHog / Update dashboard')); + + await waitFor(() => + expect(onChange).toHaveBeenCalledWith({ + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'update-dashboard', + }, + }, + parameters: { + tool: 'nyxid_proxy', + arguments: '{}', + }, + }), + ); + expect(await screen.findByText('Ready')).toBeInTheDocument(); + expect(screen.getByText('Writes data')).toBeInTheDocument(); + expect(screen.getByText('Approval required')).toBeInTheDocument(); + + fireEvent.change(screen.getByLabelText('Dashboard id'), { + target: { value: '42' }, + }); + await waitFor(() => { + const lastChange = onChange.mock.calls.at(-1)?.[0]; + expect(lastChange?.parameters.tool).toBe('nyxid_proxy'); + expect(JSON.parse(String(lastChange?.parameters.arguments))).toEqual({ + path_params: { dashboard_id: 42 }, + }); + }); + }); + + it('shows backend setup guidance instead of treating readiness as input validation', async () => { + jest + .spyOn(studioApi, 'listWorkflowCapabilities') + .mockResolvedValue(capabilityList); + jest + .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') + .mockResolvedValue({ + ...readyCapability, + status: 'credential_connection_required', + selectedOperation: null, + blockers: [ + { + status: 'credential_connection_required', + code: 'credential_connection_required', + safeMessage: 'Reconnect the PostHog service.', + }, + ], + remediations: [ + { + actionKind: 'connect_credential', + label: 'Reconnect PostHog', + trustedLocator: '/settings/connections/posthog', + }, + ], + }); + + renderConfiguration({ + capability: { + nyxid_operation: { + user_service_id: 'us-posthog-alpha', + endpoint_id: 'update-dashboard', + }, + }, + parameters: { tool: 'nyxid_proxy', arguments: '{}' }, + }); + + expect(await screen.findByText('Needs setup')).toBeInTheDocument(); + expect( + screen.getByText('Reconnect the PostHog service.'), + ).toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Reconnect PostHog' }), + ).toHaveAttribute('href', '/settings/connections/posthog'); + }); + + it('distinguishes an empty connected-action catalog from loading', async () => { + jest.spyOn(studioApi, 'listWorkflowCapabilities').mockResolvedValue({ + capabilities: [], + candidateCount: 0, + rejectedCount: 0, + diagnostics: [], + }); + + renderConfiguration(); + + expect( + await screen.findByText('No connected actions are available yet.'), + ).toBeInTheDocument(); + }); + + it('keeps discovery failures recoverable with retry', async () => { + const listCapabilities = jest + .spyOn(studioApi, 'listWorkflowCapabilities') + .mockRejectedValueOnce(new Error('network unavailable')) + .mockResolvedValueOnce(capabilityList); + + renderConfiguration(); + + expect( + await screen.findByText('Connected actions could not be loaded.'), + ).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Retry' })); + await waitFor(() => expect(listCapabilities).toHaveBeenCalledTimes(2)); + expect(await screen.findByLabelText('Action')).toBeInTheDocument(); + }); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx new file mode 100644 index 0000000000..15238fc026 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx @@ -0,0 +1,476 @@ +import { + CheckCircleOutlined, + ExclamationCircleOutlined, + ReloadOutlined, + WarningOutlined, +} from '@ant-design/icons'; +import { useQuery } from '@tanstack/react-query'; +import { Alert, Button, Input, Select, Switch, Tag, Typography } from 'antd'; +import React from 'react'; +import { studioApi } from '@/shared/studio/api'; +import type { + StudioWorkflowCapability, + StudioWorkflowCapabilityDescriptor, +} from '@/shared/studio/models'; +import { + capabilitySelectorKey, + formatToolArguments, + listOperationInputFields, + parseToolArguments, + readOperationInputValue, + toDocumentCapability, + writeOperationInputValue, + type NyxIdOperationSelector, + type OperationInputField, +} from './toolCallConfiguration'; + +export type WorkflowToolCallConfigurationChange = { + readonly capability: StudioWorkflowCapability | null; + readonly parameters: Record; +}; + +type WorkflowToolCallConfigurationProps = { + readonly capability: StudioWorkflowCapability | null; + readonly disabled: boolean; + readonly onActionNameChange?: (name: string) => void; + readonly onChange: (change: WorkflowToolCallConfigurationChange) => void; + readonly onErrorChange: (error: string) => void; + readonly parameters: Record; + readonly scopeId: string; +}; + +function selectorFromCapability( + capability: StudioWorkflowCapability | null, +): NyxIdOperationSelector | null { + const operation = capability?.nyxid_operation; + if (!operation) return null; + return { + kind: 'nyxid_operation', + userServiceId: operation.user_service_id, + endpointId: operation.endpoint_id, + }; +} + +function riskLabel( + descriptor: StudioWorkflowCapabilityDescriptor | undefined, +): string | null { + if (!descriptor) return null; + if (descriptor.destructive) return 'Destructive'; + return descriptor.readOnly ? 'Read only' : 'Writes data'; +} + +function currentInputText(value: unknown): string { + if (value == null) return ''; + if (typeof value === 'string') return value; + if (typeof value === 'object') return JSON.stringify(value, null, 2); + return String(value); +} + +function StructuredOperationInput({ + disabled, + field, + value, + onCommit, +}: { + readonly disabled: boolean; + readonly field: OperationInputField; + readonly value: unknown; + readonly onCommit: (value: string) => void; +}) { + const [draft, setDraft] = React.useState(() => currentInputText(value)); + React.useEffect(() => setDraft(currentInputText(value)), [value]); + + return ( + onCommit(draft)} + onChange={(event) => setDraft(event.target.value)} + placeholder={field.schema.valueKind === 'array' ? '[ ]' : '{ }'} + spellCheck={false} + value={draft} + /> + ); +} + +function OperationInputControl({ + disabled, + field, + value, + onCommit, +}: { + readonly disabled: boolean; + readonly field: OperationInputField; + readonly value: unknown; + readonly onCommit: (value: unknown) => void; +}) { + if (field.schema.allowedValues.length > 0) { + return ( + onCommit(event.target.value)} + placeholder={ + field.schema.valueKind === 'integer' || + field.schema.valueKind === 'number' + ? 'Enter a number or workflow expression' + : 'Enter a value or workflow expression' + } + value={currentInputText(value)} + /> + ); +} + +export default function WorkflowToolCallConfiguration({ + capability, + disabled, + onActionNameChange, + onChange, + onErrorChange, + parameters, + scopeId, +}: WorkflowToolCallConfigurationProps) { + const selector = React.useMemo( + () => selectorFromCapability(capability), + [capability], + ); + const selectorKey = selector ? capabilitySelectorKey(selector) : ''; + const [fieldErrors, setFieldErrors] = React.useState>( + {}, + ); + const discovery = useQuery({ + queryKey: ['workflow-capabilities', scopeId], + queryFn: () => studioApi.listWorkflowCapabilities(scopeId), + enabled: Boolean(scopeId.trim()), + retry: false, + }); + const descriptors = React.useMemo( + () => + (discovery.data?.capabilities ?? []).filter( + ( + descriptor, + ): descriptor is StudioWorkflowCapabilityDescriptor & { + readonly selector: NyxIdOperationSelector; + } => descriptor.selector.kind === 'nyxid_operation', + ), + [discovery.data], + ); + const selectedDescriptor = descriptors.find( + (descriptor) => capabilitySelectorKey(descriptor.selector) === selectorKey, + ); + const readiness = useQuery({ + queryKey: ['workflow-capability-readiness', scopeId, selectorKey], + queryFn: () => + studioApi.inspectWorkflowCapabilityReadiness({ + scopeId, + selector: selector!, + executionMode: 'interactive', + }), + enabled: Boolean(scopeId.trim() && selector), + retry: false, + }); + const parsedArguments = React.useMemo( + () => parseToolArguments(parameters.arguments), + [parameters.arguments], + ); + const operation = readiness.data?.selectedOperation ?? null; + const fields = React.useMemo( + () => (operation ? listOperationInputFields(operation) : []), + [operation], + ); + const configurationError = + parsedArguments.error ?? + Object.values(fieldErrors).find(Boolean) ?? + ''; + + React.useEffect(() => onErrorChange(configurationError), [ + configurationError, + onErrorChange, + ]); + React.useEffect(() => { + onActionNameChange?.(selectedDescriptor?.displayName ?? ''); + }, [onActionNameChange, selectedDescriptor?.displayName]); + React.useEffect(() => setFieldErrors({}), [selectorKey]); + + const selectAction = (nextKey: string | undefined) => { + if (!nextKey) { + const nextParameters = { ...parameters }; + delete nextParameters.tool; + delete nextParameters.arguments; + onChange({ capability: null, parameters: nextParameters }); + return; + } + const descriptor = descriptors.find( + (entry) => capabilitySelectorKey(entry.selector) === nextKey, + ); + if (!descriptor) return; + onChange({ + capability: toDocumentCapability(descriptor.selector), + parameters: { + ...parameters, + tool: 'nyxid_proxy', + arguments: + parameters.arguments == null ? '{}' : parameters.arguments, + }, + }); + }; + + const commitField = (field: OperationInputField, rawValue: unknown) => { + const result = writeOperationInputValue( + parsedArguments.arguments, + field, + rawValue, + ); + setFieldErrors((current) => ({ + ...current, + [field.key]: result.error ?? '', + })); + if (result.error && result.arguments === parsedArguments.arguments) return; + onChange({ + capability, + parameters: { + ...parameters, + tool: 'nyxid_proxy', + arguments: formatToolArguments(result.arguments), + }, + }); + }; + + const actionOptions = descriptors.map((descriptor) => ({ + label: descriptor.displayName, + value: capabilitySelectorKey(descriptor.selector), + })); + if (selector && !selectedDescriptor) { + actionOptions.unshift({ + label: 'Saved action', + value: selectorKey, + }); + } + + return ( +
+
+ + + Choose what external service action this step should run. + + ({ - label: entry, + label: allowedValueLabel(field, entry), value: entry, }))} - placeholder="Choose a value" + placeholder={t( + 'workflowActivityVNext.nodeInspector.tool.chooseValue', + 'Choose a value', + )} + status={status} value={value == null ? undefined : String(value)} /> ); } if (field.schema.valueKind === 'boolean') { return ( - + onCommit(nextValue === undefined ? undefined : nextValue === 'true') + } + options={[ + { + label: t('workflowActivityVNext.common.yes', 'Yes'), + value: 'true', + }, + { + label: t('workflowActivityVNext.common.no', 'No'), + value: 'false', + }, + ]} + placeholder={t( + 'workflowActivityVNext.nodeInspector.tool.chooseValue', + 'Choose a value', + )} + status={status} + value={value == null ? undefined : String(value)} /> ); } @@ -136,8 +257,13 @@ function OperationInputControl({ ) { return ( @@ -145,15 +271,26 @@ function OperationInputControl({ } return ( onCommit(event.target.value)} placeholder={ field.schema.valueKind === 'integer' || field.schema.valueKind === 'number' - ? 'Enter a number or workflow expression' - : 'Enter a value or workflow expression' + ? t( + 'workflowActivityVNext.nodeInspector.tool.numberPlaceholder', + 'Enter a number or workflow expression', + ) + : t( + 'workflowActivityVNext.nodeInspector.tool.valuePlaceholder', + 'Enter a value or workflow expression', + ) } + status={status} value={currentInputText(value)} /> ); @@ -173,9 +310,10 @@ export default function WorkflowToolCallConfiguration({ [capability], ); const selectorKey = selector ? capabilitySelectorKey(selector) : ''; - const [fieldErrors, setFieldErrors] = React.useState>( - {}, - ); + const [transientFieldErrors, setTransientFieldErrors] = React.useState<{ + readonly stateKey: string; + readonly errors: Readonly>; + }>({ stateKey: '', errors: {} }); const discovery = useQuery({ queryKey: ['workflow-capabilities', scopeId], queryFn: () => studioApi.listWorkflowCapabilities(scopeId), @@ -198,12 +336,14 @@ export default function WorkflowToolCallConfiguration({ ); const readiness = useQuery({ queryKey: ['workflow-capability-readiness', scopeId, selectorKey], - queryFn: () => - studioApi.inspectWorkflowCapabilityReadiness({ + queryFn: () => { + if (!selector) throw new Error('Capability selector is required.'); + return studioApi.inspectWorkflowCapabilityReadiness({ scopeId, - selector: selector!, + selector, executionMode: 'interactive', - }), + }); + }, enabled: Boolean(scopeId.trim() && selector), retry: false, }); @@ -211,24 +351,55 @@ export default function WorkflowToolCallConfiguration({ () => parseToolArguments(parameters.arguments), [parameters.arguments], ); - const operation = readiness.data?.selectedOperation ?? null; + const settledReadiness = readiness.isFetching ? null : readiness.data; + const operation = settledReadiness?.selectedOperation ?? null; const fields = React.useMemo( () => (operation ? listOperationInputFields(operation) : []), [operation], ); + const fieldStateKey = `${selectorKey}\u0000${parsedArguments.originalText}`; + const fieldErrors = + transientFieldErrors.stateKey === fieldStateKey + ? transientFieldErrors.errors + : {}; + const fieldValidation = React.useMemo( + () => + parsedArguments.error + ? { blockingErrors: {}, missingRequired: {} } + : validateOperationFields(parsedArguments.arguments, fields), + [fields, parsedArguments], + ); const configurationError = parsedArguments.error ?? Object.values(fieldErrors).find(Boolean) ?? + Object.values(fieldValidation.blockingErrors).find(Boolean) ?? ''; + const hasMissingRequiredInputs = + Object.keys(fieldValidation.missingRequired).length > 0; - React.useEffect(() => onErrorChange(configurationError), [ - configurationError, - onErrorChange, - ]); + React.useEffect( + () => onErrorChange(configurationError), + [configurationError, onErrorChange], + ); React.useEffect(() => { onActionNameChange?.(selectedDescriptor?.displayName ?? ''); }, [onActionNameChange, selectedDescriptor?.displayName]); - React.useEffect(() => setFieldErrors({}), [selectorKey]); + React.useEffect(() => { + if (!operation || parsedArguments.error) return; + const result = reconcileOperationResponseMode( + parsedArguments.arguments, + operation, + ); + if (!result.changed) return; + onChange({ + capability, + parameters: { + ...parameters, + tool: 'nyxid_proxy', + arguments: formatToolArguments(result.arguments), + }, + }); + }, [capability, onChange, operation, parameters, parsedArguments]); const selectAction = (nextKey: string | undefined) => { if (!nextKey) { @@ -247,8 +418,7 @@ export default function WorkflowToolCallConfiguration({ parameters: { ...parameters, tool: 'nyxid_proxy', - arguments: - parameters.arguments == null ? '{}' : parameters.arguments, + arguments: parameters.arguments == null ? '{}' : parameters.arguments, }, }); }; @@ -259,9 +429,12 @@ export default function WorkflowToolCallConfiguration({ field, rawValue, ); - setFieldErrors((current) => ({ - ...current, - [field.key]: result.error ?? '', + setTransientFieldErrors((current) => ({ + stateKey: fieldStateKey, + errors: { + ...(current.stateKey === fieldStateKey ? current.errors : {}), + [field.key]: result.error ?? '', + }, })); if (result.error && result.arguments === parsedArguments.arguments) return; onChange({ @@ -274,13 +447,26 @@ export default function WorkflowToolCallConfiguration({ }); }; - const actionOptions = descriptors.map((descriptor) => ({ - label: descriptor.displayName, - value: capabilitySelectorKey(descriptor.selector), - })); + const actionOptions = descriptors.map((descriptor) => { + const risk = riskLabel(descriptor); + return { + displayName: descriptor.displayName, + label: risk + ? `${descriptor.displayName} · ${risk}` + : descriptor.displayName, + risk, + value: capabilitySelectorKey(descriptor.selector), + }; + }); if (selector && !selectedDescriptor) { + const savedActionLabel = t( + 'workflowActivityVNext.nodeInspector.tool.savedAction', + 'Saved action', + ); actionOptions.unshift({ - label: 'Saved action', + displayName: savedActionLabel, + label: savedActionLabel, + risk: null, value: selectorKey, }); } @@ -288,18 +474,42 @@ export default function WorkflowToolCallConfiguration({ return (
- + - Choose what external service action this step should run. + {t( + 'workflowActivityVNext.nodeInspector.tool.actionHelp', + 'Choose what external service action this step should run.', + )} updateFieldValue(field.name, nextValue)} @@ -537,6 +513,7 @@ const WorkflowNodeInspector = React.forwardRef< return ( @@ -555,6 +532,7 @@ const WorkflowNodeInspector = React.forwardRef< return ( @@ -573,6 +551,7 @@ const WorkflowNodeInspector = React.forwardRef< return ( updateFieldValue(field.name, event.target.value)} @@ -587,6 +566,39 @@ const WorkflowNodeInspector = React.forwardRef< ); }; + const renderField = (field: StudioStructuredNodeConfigField) => { + const guidance = field.description + ? formatConsoleMessage(field.description) + : field.placeholder + ? t( + 'workflowActivityVNext.nodeInspector.fieldExample', + 'Example: {value}', + { value: formatConsoleMessage(field.placeholder) }, + ) + : ''; + + return ( +
+
+ {formatConsoleMessage(field.label)} + + {field.required + ? t( + 'workflowActivityVNext.nodeInspector.fieldRequired', + 'Required', + ) + : t( + 'workflowActivityVNext.nodeInspector.fieldOptional', + 'Optional', + )} + +
+ {renderFieldControl(field)} + {guidance ? {guidance} : null} +
+ ); + }; + return ( <>
{stepPurpose(stepDraft.type)} -
+
- {usesGuidedToolCall ? ( - - ) : ( -
- {schema.fields.map((field) => ( -
- {formatConsoleMessage(field.label)} - {renderFieldControl(field)} - {field.description ? ( - {formatConsoleMessage(field.description)} - ) : null} -
- ))} -
- )} +
+ {schema.fields.length > 0 ? ( + schema.fields.map(renderField) + ) : ( + + {t( + 'workflowActivityVNext.nodeInspector.noSettings', + 'No settings are needed for this step.', + )} + + )} +
{validationError ? (
{summarizeBranches(stepDraft.branchesText)}
- {stepDraft.type.trim().toLowerCase() === 'tool_call' ? ( - <> -
-
- {t( - 'workflowActivityVNext.nodeInspector.runtimeTool', - 'Runtime tool', - )} -
-
{displayValue(runtimeTool)}
-
-
-
- {t( - 'workflowActivityVNext.nodeInspector.userServiceId', - 'User service ID', - )} -
-
- {displayValue( - capability?.nyxid_operation?.user_service_id ?? - '', - )} -
-
-
-
- {t( - 'workflowActivityVNext.nodeInspector.endpointId', - 'Endpoint ID', - )} -
-
- {displayValue( - capability?.nyxid_operation?.endpoint_id ?? '', - )} -
-
- - ) : null} ), key: 'step-details', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx deleted file mode 100644 index 5cd8227411..0000000000 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.test.tsx +++ /dev/null @@ -1,557 +0,0 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import React from 'react'; -import { studioApi } from '@/shared/studio/api'; -import type { - StudioWorkflowCapability, - StudioWorkflowCapabilityList, - StudioWorkflowCapabilityReadiness, -} from '@/shared/studio/models'; -import { capabilitySelectorKey } from './toolCallConfiguration'; -import WorkflowToolCallConfiguration, { - type WorkflowToolCallConfigurationChange, -} from './WorkflowToolCallConfiguration'; - -const capabilityList: StudioWorkflowCapabilityList = { - capabilities: [ - { - displayName: 'PostHog / Update dashboard', - readOnly: false, - destructive: false, - selector: { - kind: 'nyxid_operation', - userServiceId: 'us-posthog-alpha', - endpointId: 'update-dashboard', - }, - source: { - kind: 'nyxid_user_services', - sourceId: 'source-posthog-alpha', - sourceVersion: 7, - observedAt: '2026-09-02T08:00:00Z', - freshUntil: '2026-09-02T08:05:00Z', - }, - }, - ], - candidateCount: 1, - rejectedCount: 0, - diagnostics: [], -}; - -const readyCapability: StudioWorkflowCapabilityReadiness = { - executionMode: 'interactive', - status: 'ready', - selectedSelector: { - kind: 'nyxid_operation', - userServiceId: 'us-posthog-alpha', - endpointId: 'update-dashboard', - }, - selectedOperation: { - userServiceId: 'us-posthog-alpha', - endpointId: 'update-dashboard', - serviceSlug: 'posthog', - httpMethod: 'PATCH', - pathTemplate: '/api/dashboards/{dashboard_id}', - parameters: [ - { - name: 'dashboard_id', - location: 'path', - required: true, - schema: { - valueKind: 'integer', - properties: [], - requiredProperties: [], - items: null, - allowedValues: [], - additionalPropertiesAllowed: false, - }, - }, - { - name: 'include_archived', - location: 'query', - required: false, - schema: { - valueKind: 'boolean', - properties: [], - requiredProperties: [], - items: null, - allowedValues: [], - additionalPropertiesAllowed: false, - }, - }, - ], - requestBody: null, - responsePolicy: { - textAllowed: true, - fileArtifactAllowed: false, - mediaTypes: ['application/json'], - }, - executionPolicy: { - risk: 'write', - approval: 'required', - enforcementOwner: 'aevatar', - allowedExecutionModes: ['interactive'], - }, - }, - blockers: [], - remediations: [], - sources: [], -}; - -function renderConfiguration(input?: { - readonly capability?: StudioWorkflowCapability | null; - readonly onErrorChange?: jest.Mock; - readonly parameters?: Record; - readonly onChange?: jest.Mock; - readonly queryClient?: QueryClient; -}) { - const onChange = input?.onChange ?? jest.fn(); - const onErrorChange = input?.onErrorChange ?? jest.fn(); - const queryClient = - input?.queryClient ?? - new QueryClient({ - defaultOptions: { queries: { retry: false } }, - }); - - function Harness() { - const [capability, setCapability] = React.useState( - input?.capability ?? null, - ); - const [parameters, setParameters] = React.useState(input?.parameters ?? {}); - return ( - - { - onChange(change); - setCapability(change.capability); - setParameters(change.parameters); - }} - onErrorChange={onErrorChange} - parameters={parameters} - scopeId="scope-alpha" - /> - - ); - } - - return { ...render(), onChange, onErrorChange }; -} - -describe('WorkflowToolCallConfiguration', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('leads with an action picker and writes the exact selected operation', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue(readyCapability); - const { onChange } = renderConfiguration({ - parameters: { tool: 'nyxid_proxy' }, - }); - - const actionPicker = await screen.findByRole('combobox', { - name: 'Action', - }); - await waitFor(() => expect(actionPicker).not.toBeDisabled()); - expect(screen.queryByDisplayValue('nyxid_proxy')).not.toBeInTheDocument(); - fireEvent.mouseDown(actionPicker); - expect(await screen.findByText('Writes data')).toBeInTheDocument(); - expect( - screen.getByRole('option', { - name: /PostHog \/ Update dashboard.*Writes data/, - }), - ).toBeInTheDocument(); - fireEvent.click(await screen.findByText('PostHog / Update dashboard')); - - await waitFor(() => - expect(onChange).toHaveBeenCalledWith({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - parameters: { - tool: 'nyxid_proxy', - arguments: '{}', - }, - }), - ); - expect(await screen.findByText('Ready')).toBeInTheDocument(); - expect( - screen.getByText('Writes data', { selector: '.ant-tag' }), - ).toBeInTheDocument(); - expect(screen.getByText('Approval required')).toBeInTheDocument(); - - const optionalBoolean = screen.getByRole('combobox', { - name: 'Include archived', - }); - expect(optionalBoolean).toHaveValue(''); - fireEvent.mouseDown(optionalBoolean); - fireEvent.click(await screen.findByText('No')); - await waitFor(() => { - const lastChange = onChange.mock.calls.at(-1)?.[0]; - expect(JSON.parse(String(lastChange?.parameters.arguments))).toEqual({ - query: { include_archived: false }, - response_mode: 'text', - }); - }); - - fireEvent.change(screen.getByLabelText('Dashboard id'), { - target: { value: '42' }, - }); - await waitFor(() => { - const lastChange = onChange.mock.calls.at(-1)?.[0]; - expect(lastChange?.parameters.tool).toBe('nyxid_proxy'); - expect(JSON.parse(String(lastChange?.parameters.arguments))).toEqual({ - path_params: { dashboard_id: 42 }, - query: { include_archived: false }, - response_mode: 'text', - }); - }); - }); - - it('shows backend setup guidance instead of treating readiness as input validation', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue({ - ...readyCapability, - status: 'credential_connection_required', - selectedOperation: null, - blockers: [ - { - status: 'credential_connection_required', - code: 'credential_connection_required', - safeMessage: 'Reconnect the PostHog service.', - }, - ], - remediations: [ - { - actionKind: 'connect_credential', - label: 'Reconnect PostHog', - trustedLocator: 'nyxid:services', - }, - ], - }); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - }); - - expect(await screen.findByText('Needs setup')).toBeInTheDocument(); - expect( - screen.getByText('Reconnect the PostHog service.'), - ).toBeInTheDocument(); - expect( - screen.getByRole('link', { name: 'Reconnect PostHog' }), - ).toHaveAttribute( - 'href', - '/scopes/scope-alpha/workflow-activity-vnext/settings?section=account', - ); - }); - - it('distinguishes an empty connected-action catalog from loading', async () => { - jest.spyOn(studioApi, 'listWorkflowCapabilities').mockResolvedValue({ - capabilities: [], - candidateCount: 1, - rejectedCount: 1, - diagnostics: [ - { - code: 'unsupported_schema', - safeMessage: 'One connected action uses an unsupported input shape.', - count: 1, - source: null, - }, - ], - }); - - renderConfiguration(); - - expect( - await screen.findByText('No connected actions are available yet.'), - ).toBeInTheDocument(); - expect( - screen.getByText('One connected action uses an unsupported input shape.'), - ).toBeInTheDocument(); - }); - - it('labels capability discovery as loading before an action can be chosen', () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockReturnValue( - new Promise(() => undefined), - ); - - renderConfiguration(); - - expect(screen.getByText('Loading connected actions')).toBeInTheDocument(); - expect(screen.queryByText('Choose an action')).not.toBeInTheDocument(); - }); - - it('keeps discovery failures recoverable with retry', async () => { - const listCapabilities = jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockRejectedValueOnce(new Error('network unavailable')) - .mockResolvedValueOnce(capabilityList); - - renderConfiguration(); - - expect( - await screen.findByText('Connected actions could not be loaded.'), - ).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: 'Retry' })); - await waitFor(() => expect(listCapabilities).toHaveBeenCalledTimes(2)); - expect(await screen.findByLabelText('Action')).toBeInTheDocument(); - }); - - it('shows a fresh availability check instead of cached readiness', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - let resolveReadiness: (value: StudioWorkflowCapabilityReadiness) => void = - () => undefined; - jest.spyOn(studioApi, 'inspectWorkflowCapabilityReadiness').mockReturnValue( - new Promise((resolve) => { - resolveReadiness = resolve; - }), - ); - const selector = capabilityList.capabilities[0].selector; - if (selector.kind !== 'nyxid_operation') { - throw new Error('Expected a NyxID operation selector.'); - } - const queryClient = new QueryClient({ - defaultOptions: { queries: { retry: false } }, - }); - queryClient.setQueryData( - [ - 'workflow-capability-readiness', - 'scope-alpha', - capabilitySelectorKey(selector), - ], - readyCapability, - ); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: selector.userServiceId, - endpoint_id: selector.endpointId, - }, - }, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - queryClient, - }); - - expect(await screen.findByText('Checking availability')).toBeVisible(); - expect(screen.queryByText('Ready')).not.toBeInTheDocument(); - - resolveReadiness(readyCapability); - expect(await screen.findByText('Ready')).toBeVisible(); - }); - - it('writes the only supported file response mode automatically', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue({ - ...readyCapability, - selectedOperation: readyCapability.selectedOperation - ? { - ...readyCapability.selectedOperation, - responsePolicy: { - textAllowed: false, - fileArtifactAllowed: true, - mediaTypes: ['application/pdf'], - }, - } - : null, - }); - const { onChange } = renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - }); - - await waitFor(() => { - const lastChange = onChange.mock.calls.at(-1)?.[0]; - expect(JSON.parse(String(lastChange?.parameters.arguments))).toEqual({ - response_mode: 'file_artifact', - }); - }); - }); - - it('offers a response-format choice when text and files are both supported', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue({ - ...readyCapability, - selectedOperation: readyCapability.selectedOperation - ? { - ...readyCapability.selectedOperation, - responsePolicy: { - textAllowed: true, - fileArtifactAllowed: true, - mediaTypes: ['application/json', 'application/pdf'], - }, - } - : null, - }); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - }); - - expect( - await screen.findByRole('combobox', { name: 'Result format' }), - ).toBeVisible(); - }); - - it('associates a generated field error with its input', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue(readyCapability); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - }); - - const dashboardId = await screen.findByLabelText('Dashboard id'); - fireEvent.change(dashboardId, { target: { value: 'not-a-number' } }); - - expect( - await screen.findByText('Dashboard id must be a whole number.'), - ).toBeVisible(); - expect(dashboardId).toHaveAttribute('aria-invalid', 'true'); - expect(dashboardId.getAttribute('aria-describedby')).toContain( - 'workflow-tool-field-error-path:dashboard_id', - ); - }); - - it('shows missing required inputs as saveable draft guidance', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue(readyCapability); - const onErrorChange = jest.fn(); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - onErrorChange, - parameters: { tool: 'nyxid_proxy', arguments: '{}' }, - }); - - const dashboardId = await screen.findByLabelText('Dashboard id'); - expect(screen.getByText('Dashboard id is required.')).toBeVisible(); - expect( - screen.getByText( - 'Complete the required inputs before this step can run. You can still apply this draft.', - ), - ).toBeVisible(); - expect(dashboardId).toHaveAttribute('aria-required', 'true'); - expect(dashboardId).toHaveAttribute('aria-invalid', 'false'); - await waitFor(() => expect(onErrorChange.mock.calls.at(-1)?.[0]).toBe('')); - }); - - it('blocks an invalid saved response mode and clears the error after repair', async () => { - jest - .spyOn(studioApi, 'listWorkflowCapabilities') - .mockResolvedValue(capabilityList); - jest - .spyOn(studioApi, 'inspectWorkflowCapabilityReadiness') - .mockResolvedValue({ - ...readyCapability, - selectedOperation: readyCapability.selectedOperation - ? { - ...readyCapability.selectedOperation, - responsePolicy: { - textAllowed: true, - fileArtifactAllowed: true, - mediaTypes: ['application/json', 'application/pdf'], - }, - } - : null, - }); - const onErrorChange = jest.fn(); - - renderConfiguration({ - capability: { - nyxid_operation: { - user_service_id: 'us-posthog-alpha', - endpoint_id: 'update-dashboard', - }, - }, - onErrorChange, - parameters: { - tool: 'nyxid_proxy', - arguments: - '{"path_params":{"dashboard_id":42},"response_mode":"binary"}', - }, - }); - - const responseMode = await screen.findByRole('combobox', { - name: 'Result format', - }); - expect( - await screen.findByText( - 'Result format must be one of the available values.', - ), - ).toBeVisible(); - expect(responseMode).toHaveAttribute('aria-invalid', 'true'); - await waitFor(() => - expect(onErrorChange.mock.calls.at(-1)?.[0]).toBe( - 'Result format must be one of the available values.', - ), - ); - - fireEvent.mouseDown(responseMode); - fireEvent.click(await screen.findByText('Text')); - - await waitFor(() => expect(onErrorChange.mock.calls.at(-1)?.[0]).toBe('')); - expect(responseMode).toHaveAttribute('aria-invalid', 'false'); - }); -}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx deleted file mode 100644 index 04e74c7536..0000000000 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowToolCallConfiguration.tsx +++ /dev/null @@ -1,802 +0,0 @@ -import { - CheckCircleOutlined, - ExclamationCircleOutlined, - ReloadOutlined, - WarningOutlined, -} from '@ant-design/icons'; -import { useQuery } from '@tanstack/react-query'; -import { Alert, Button, Input, Select, Tag, Typography } from 'antd'; -import React from 'react'; -import { t } from '@/shared/i18n/messages'; -import { studioApi } from '@/shared/studio/api'; -import type { - StudioWorkflowCapability, - StudioWorkflowCapabilityDescriptor, -} from '@/shared/studio/models'; -import { - capabilitySelectorKey, - formatToolArguments, - listOperationInputFields, - type NyxIdOperationSelector, - type OperationInputField, - parseToolArguments, - readOperationInputValue, - reconcileOperationResponseMode, - toDocumentCapability, - writeOperationInputValue, -} from './toolCallConfiguration'; - -export type WorkflowToolCallConfigurationChange = { - readonly capability: StudioWorkflowCapability | null; - readonly parameters: Record; -}; - -type WorkflowToolCallConfigurationProps = { - readonly capability: StudioWorkflowCapability | null; - readonly disabled: boolean; - readonly onActionNameChange?: (name: string) => void; - readonly onChange: (change: WorkflowToolCallConfigurationChange) => void; - readonly onErrorChange: (error: string) => void; - readonly parameters: Record; - readonly scopeId: string; -}; - -type OperationFieldValidation = { - readonly blockingErrors: Readonly>; - readonly missingRequired: Readonly>; -}; - -type OperationInputStatus = 'error' | 'warning' | undefined; - -function selectorFromCapability( - capability: StudioWorkflowCapability | null, -): NyxIdOperationSelector | null { - const operation = capability?.nyxid_operation; - if (!operation) return null; - return { - kind: 'nyxid_operation', - userServiceId: operation.user_service_id, - endpointId: operation.endpoint_id, - }; -} - -function riskLabel( - descriptor: StudioWorkflowCapabilityDescriptor | undefined, -): string | null { - if (!descriptor) return null; - if (descriptor.destructive) { - return t( - 'workflowActivityVNext.nodeInspector.tool.risk.destructive', - 'Destructive', - ); - } - return descriptor.readOnly - ? t('workflowActivityVNext.nodeInspector.tool.risk.readOnly', 'Read only') - : t('workflowActivityVNext.nodeInspector.tool.risk.write', 'Writes data'); -} - -function currentInputText(value: unknown): string { - if (value == null) return ''; - if (typeof value === 'string') return value; - if (typeof value === 'object') return JSON.stringify(value, null, 2); - return String(value); -} - -function isEmptyInputValue(value: unknown): boolean { - return ( - value == null || (typeof value === 'string' && value.trim().length === 0) - ); -} - -function validateOperationFields( - argumentsValue: Record, - fields: readonly OperationInputField[], -): OperationFieldValidation { - const blockingErrors: Record = {}; - const missingRequired: Record = {}; - for (const field of fields) { - const value = readOperationInputValue(argumentsValue, field); - const result = writeOperationInputValue(argumentsValue, field, value); - if (!result.error) continue; - if (field.required && isEmptyInputValue(value)) { - missingRequired[field.key] = result.error; - } else { - blockingErrors[field.key] = result.error; - } - } - return { blockingErrors, missingRequired }; -} - -function inputLocationLabel(group: 'path' | 'query' | 'header'): string { - if (group === 'path') { - return t('workflowActivityVNext.nodeInspector.tool.location.path', 'Path'); - } - if (group === 'header') { - return t( - 'workflowActivityVNext.nodeInspector.tool.location.header', - 'Header', - ); - } - return t('workflowActivityVNext.nodeInspector.tool.location.query', 'Query'); -} - -function allowedValueLabel(field: OperationInputField, value: string): string { - if (field.group !== 'response') return value; - return value === 'file_artifact' - ? t('workflowActivityVNext.nodeInspector.tool.response.file', 'File') - : t('workflowActivityVNext.nodeInspector.tool.response.text', 'Text'); -} - -function remediationHref(locator: string, scopeId: string): string | null { - if (locator.trim() !== 'nyxid:services') return null; - return `/scopes/${encodeURIComponent(scopeId.trim())}/workflow-activity-vnext/settings?section=account`; -} - -function StructuredOperationInput({ - controlId, - describedBy, - disabled, - field, - invalid, - required, - status, - value, - onCommit, -}: { - readonly controlId: string; - readonly describedBy: string; - readonly disabled: boolean; - readonly field: OperationInputField; - readonly invalid: boolean; - readonly required: boolean; - readonly status: OperationInputStatus; - readonly value: unknown; - readonly onCommit: (value: string) => void; -}) { - const [draft, setDraft] = React.useState(() => currentInputText(value)); - React.useEffect(() => setDraft(currentInputText(value)), [value]); - - return ( - onCommit(draft)} - onChange={(event) => setDraft(event.target.value)} - placeholder={field.schema.valueKind === 'array' ? '[ ]' : '{ }'} - spellCheck={false} - status={status} - value={draft} - /> - ); -} - -function OperationInputControl({ - controlId, - describedBy, - disabled, - field, - invalid, - required, - status, - value, - onCommit, -}: { - readonly controlId: string; - readonly describedBy: string; - readonly disabled: boolean; - readonly field: OperationInputField; - readonly invalid: boolean; - readonly required: boolean; - readonly status: OperationInputStatus; - readonly value: unknown; - readonly onCommit: (value: unknown) => void; -}) { - if (field.schema.allowedValues.length > 0) { - return ( - - onCommit(nextValue === undefined ? undefined : nextValue === 'true') - } - options={[ - { - label: t('workflowActivityVNext.common.yes', 'Yes'), - value: 'true', - }, - { - label: t('workflowActivityVNext.common.no', 'No'), - value: 'false', - }, - ]} - placeholder={t( - 'workflowActivityVNext.nodeInspector.tool.chooseValue', - 'Choose a value', - )} - status={status} - value={value == null ? undefined : String(value)} - /> - ); - } - if ( - field.schema.valueKind === 'object' || - field.schema.valueKind === 'array' - ) { - return ( - - ); - } - return ( - onCommit(event.target.value)} - placeholder={ - field.schema.valueKind === 'integer' || - field.schema.valueKind === 'number' - ? t( - 'workflowActivityVNext.nodeInspector.tool.numberPlaceholder', - 'Enter a number or workflow expression', - ) - : t( - 'workflowActivityVNext.nodeInspector.tool.valuePlaceholder', - 'Enter a value or workflow expression', - ) - } - status={status} - value={currentInputText(value)} - /> - ); -} - -export default function WorkflowToolCallConfiguration({ - capability, - disabled, - onActionNameChange, - onChange, - onErrorChange, - parameters, - scopeId, -}: WorkflowToolCallConfigurationProps) { - const selector = React.useMemo( - () => selectorFromCapability(capability), - [capability], - ); - const selectorKey = selector ? capabilitySelectorKey(selector) : ''; - const [transientFieldErrors, setTransientFieldErrors] = React.useState<{ - readonly stateKey: string; - readonly errors: Readonly>; - }>({ stateKey: '', errors: {} }); - const discovery = useQuery({ - queryKey: ['workflow-capabilities', scopeId], - queryFn: () => studioApi.listWorkflowCapabilities(scopeId), - enabled: Boolean(scopeId.trim()), - retry: false, - }); - const descriptors = React.useMemo( - () => - (discovery.data?.capabilities ?? []).filter( - ( - descriptor, - ): descriptor is StudioWorkflowCapabilityDescriptor & { - readonly selector: NyxIdOperationSelector; - } => descriptor.selector.kind === 'nyxid_operation', - ), - [discovery.data], - ); - const selectedDescriptor = descriptors.find( - (descriptor) => capabilitySelectorKey(descriptor.selector) === selectorKey, - ); - const readiness = useQuery({ - queryKey: ['workflow-capability-readiness', scopeId, selectorKey], - queryFn: () => { - if (!selector) throw new Error('Capability selector is required.'); - return studioApi.inspectWorkflowCapabilityReadiness({ - scopeId, - selector, - executionMode: 'interactive', - }); - }, - enabled: Boolean(scopeId.trim() && selector), - retry: false, - }); - const parsedArguments = React.useMemo( - () => parseToolArguments(parameters.arguments), - [parameters.arguments], - ); - const settledReadiness = readiness.isFetching ? null : readiness.data; - const operation = settledReadiness?.selectedOperation ?? null; - const fields = React.useMemo( - () => (operation ? listOperationInputFields(operation) : []), - [operation], - ); - const fieldStateKey = `${selectorKey}\u0000${parsedArguments.originalText}`; - const fieldErrors = - transientFieldErrors.stateKey === fieldStateKey - ? transientFieldErrors.errors - : {}; - const fieldValidation = React.useMemo( - () => - parsedArguments.error - ? { blockingErrors: {}, missingRequired: {} } - : validateOperationFields(parsedArguments.arguments, fields), - [fields, parsedArguments], - ); - const configurationError = - parsedArguments.error ?? - Object.values(fieldErrors).find(Boolean) ?? - Object.values(fieldValidation.blockingErrors).find(Boolean) ?? - ''; - const hasMissingRequiredInputs = - Object.keys(fieldValidation.missingRequired).length > 0; - - React.useEffect( - () => onErrorChange(configurationError), - [configurationError, onErrorChange], - ); - React.useEffect(() => { - onActionNameChange?.(selectedDescriptor?.displayName ?? ''); - }, [onActionNameChange, selectedDescriptor?.displayName]); - React.useEffect(() => { - if (!operation || parsedArguments.error) return; - const result = reconcileOperationResponseMode( - parsedArguments.arguments, - operation, - ); - if (!result.changed) return; - onChange({ - capability, - parameters: { - ...parameters, - tool: 'nyxid_proxy', - arguments: formatToolArguments(result.arguments), - }, - }); - }, [capability, onChange, operation, parameters, parsedArguments]); - - const selectAction = (nextKey: string | undefined) => { - if (!nextKey) { - const nextParameters = { ...parameters }; - delete nextParameters.tool; - delete nextParameters.arguments; - onChange({ capability: null, parameters: nextParameters }); - return; - } - const descriptor = descriptors.find( - (entry) => capabilitySelectorKey(entry.selector) === nextKey, - ); - if (!descriptor) return; - onChange({ - capability: toDocumentCapability(descriptor.selector), - parameters: { - ...parameters, - tool: 'nyxid_proxy', - arguments: parameters.arguments == null ? '{}' : parameters.arguments, - }, - }); - }; - - const commitField = (field: OperationInputField, rawValue: unknown) => { - const result = writeOperationInputValue( - parsedArguments.arguments, - field, - rawValue, - ); - setTransientFieldErrors((current) => ({ - stateKey: fieldStateKey, - errors: { - ...(current.stateKey === fieldStateKey ? current.errors : {}), - [field.key]: result.error ?? '', - }, - })); - if (result.error && result.arguments === parsedArguments.arguments) return; - onChange({ - capability, - parameters: { - ...parameters, - tool: 'nyxid_proxy', - arguments: formatToolArguments(result.arguments), - }, - }); - }; - - const actionOptions = descriptors.map((descriptor) => { - const risk = riskLabel(descriptor); - return { - displayName: descriptor.displayName, - label: risk - ? `${descriptor.displayName} · ${risk}` - : descriptor.displayName, - risk, - value: capabilitySelectorKey(descriptor.selector), - }; - }); - if (selector && !selectedDescriptor) { - const savedActionLabel = t( - 'workflowActivityVNext.nodeInspector.tool.savedAction', - 'Saved action', - ); - actionOptions.unshift({ - displayName: savedActionLabel, - label: savedActionLabel, - risk: null, - value: selectorKey, - }); - } - - return ( -
-
- - - {t( - 'workflowActivityVNext.nodeInspector.tool.actionHelp', - 'Choose what external service action this step should run.', - )} - - + commitRoot( + updateNode(root, node.id, (current) => ({ + ...current, + stringValue: event.target.value, + })), + ) + } + value={node.stringValue ?? ''} + /> + ); + case 'number': + return ( + + commitRoot( + updateNode(root, node.id, (current) => ({ + ...current, + numberValue: nextText.trim() ? nextText : '0', + })), + ) + } + onChange={(nextValue) => + commitRoot( + updateNode(root, node.id, (current) => ({ + ...current, + numberValue: String(nextValue ?? 0), + })), + ) + } + stringMode + value={node.numberValue ?? '0'} + /> + ); + case 'boolean': + return ( + + commitRoot( + updateNode(root, node.id, (current) => ({ + ...current, + booleanValue: checked, + })), + ) + } + /> + ); + case 'null': + return ( + + {t('workflowActivityVNext.arguments.nullValue', 'No value')} + + ); + case 'object': + return renderObject(node, path); + case 'array': + return renderArray(node, path); + } + }; + + const renderTypeSelect = (node: JsonNode, path: string) => ( + + aria-label={t( + 'workflowActivityVNext.arguments.valueTypeAria', + 'Value type for {path}', + { path }, + )} + disabled={disabled} + onChange={(nextType) => + commitRoot(updateNode(root, node.id, () => createNode(nextType))) + } + options={typeOptions} + value={node.type} + /> + ); + + const renderObject = (node: JsonNode, path: string): React.ReactNode => { + const entries = node.objectEntries ?? []; + return ( +
+ {entries.length === 0 ? ( + + {t( + 'workflowActivityVNext.arguments.emptyObject', + 'No properties yet', + )} + + ) : null} + {entries.length > 0 ? ( +
+ + {t('workflowActivityVNext.arguments.columnProperty', 'Property')} + + + {t('workflowActivityVNext.arguments.columnType', 'Type')} + + +
+ ) : null} + {entries.map((entry) => { + const entryPath = path ? `${path}.${entry.key}` : entry.key; + const displayPath = + entryPath || + t( + 'workflowActivityVNext.arguments.unnamedProperty', + 'unnamed property', + ); + return ( +
+
+ { + const nextKey = event.target.value; + if (hasSiblingProperty(root, entry.id, nextKey)) { + setNotice({ + controlId: entry.id, + kind: 'error', + message: t( + 'workflowActivityVNext.arguments.duplicateProperty', + 'Property names must be unique. "{property}" already exists in this object.', + { property: nextKey }, + ), + source: 'field', + }); + return; + } + commitRoot( + updateObjectEntry(root, entry.id, (current) => ({ + ...current, + key: nextKey, + })), + ); + }} + placeholder={t( + 'workflowActivityVNext.arguments.propertyName', + 'Property name', + )} + value={entry.key} + /> + {renderTypeSelect(entry.value, displayPath)} +
+
+ {renderValue(entry.value, displayPath)} +
+
+ ); + })} + +
+ ); + }; + + const renderArray = (node: JsonNode, path: string): React.ReactNode => { + const items = node.arrayItems ?? []; + return ( +
+ {items.length === 0 ? ( + + {t('workflowActivityVNext.arguments.emptyArray', 'No items yet')} + + ) : null} + {items.map((item, index) => { + const itemPath = `${path}[${index + 1}]`; + return ( +
+
+ + {t('workflowActivityVNext.arguments.item', 'Item {number}', { + number: index + 1, + })} + + {renderTypeSelect(item, itemPath)} +
+
+ {renderValue(item, itemPath)} +
+
+ ); + })} + +
+ ); + }; + + return ( +
+ + block + disabled={disabled} + onChange={changeMode} + options={[ + { + label: t('workflowActivityVNext.arguments.mode.fields', 'Fields'), + value: 'fields', + }, + { + label: t('workflowActivityVNext.arguments.mode.json', 'JSON'), + value: 'json', + }, + ]} + value={mode} + /> + {mode === 'fields' ? ( + renderObject(root, '') + ) : ( +
+ changeRawText(event.target.value)} + spellCheck={false} + status={notice?.kind === 'error' ? 'error' : undefined} + value={rawText} + /> +
+ )} + {notice ? ( + + {notice.message} + + ) : null} +
+ ); +}; + +export default StructuredJsonArgumentsEditor; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx index 024e2b3c8c..60eadbe091 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.test.tsx @@ -91,14 +91,16 @@ describe('WorkflowNodeInspector', () => { expect(screen.getByRole('textbox', { name: 'Tool name' })).toHaveValue( 'nyxid_proxy', ); - expect(screen.getByRole('textbox', { name: 'Arguments JSON' })).toHaveValue( - '{"query":{"request":"$input"}}', - ); + expect(screen.getByRole('radio', { name: 'Fields' })).toBeChecked(); + expect( + screen.getByRole('textbox', { name: 'Value for query.request' }), + ).toHaveValue('$input'); + expect(screen.getByText('Arguments')).toBeVisible(); expect(screen.getByText('Required')).toBeVisible(); expect(screen.getByText('Optional')).toBeVisible(); expect( screen.getByText( - 'Use the property names documented by this tool. The value is passed as JSON text.', + "Property names and value types must match the tool's expected input.", ), ).toBeVisible(); }); @@ -107,19 +109,64 @@ describe('WorkflowNodeInspector', () => { const onConfigurationChange = jest.fn().mockReturnValue(true); renderInspector(toolDraft, onConfigurationChange); - fireEvent.change(screen.getByRole('textbox', { name: 'Arguments JSON' }), { - target: { value: '{"query":{"request":"$result"}}' }, - }); + fireEvent.change( + screen.getByRole('textbox', { name: 'Value for query.request' }), + { target: { value: '$result' } }, + ); fireEvent.click(screen.getByRole('button', { name: 'Apply step' })); await waitFor(() => expect(onConfigurationChange).toHaveBeenCalledTimes(1)); const parametersText = onConfigurationChange.mock.calls[0][0] as string; - expect(JSON.parse(parametersText)).toEqual({ - arguments: '{"query":{"request":"$result"}}', - tool: 'nyxid_proxy', + const parameters = JSON.parse(parametersText) as Record; + expect(parameters.tool).toBe('nyxid_proxy'); + expect(typeof parameters.arguments).toBe('string'); + expect(JSON.parse(parameters.arguments as string)).toEqual({ + query: { request: '$result' }, }); }); + it('does not apply a tool call while its arguments JSON is invalid', async () => { + const onConfigurationChange = jest.fn().mockReturnValue(true); + renderInspector(toolDraft, onConfigurationChange); + + fireEvent.click(screen.getByRole('radio', { name: 'JSON' })); + fireEvent.change(screen.getByRole('textbox', { name: 'Arguments JSON' }), { + target: { value: '{"query":' }, + }); + + await waitFor(() => + expect(screen.getByRole('button', { name: 'Apply step' })).toBeDisabled(), + ); + expect(onConfigurationChange).not.toHaveBeenCalled(); + }); + + it('does not apply a tool call after a duplicate argument name is rejected', async () => { + const onConfigurationChange = jest.fn().mockReturnValue(true); + const duplicateDraft = { + ...toolDraft, + parametersText: + '{\n "tool": "nyxid_proxy",\n "arguments": "{\\"name\\":\\"Ada\\",\\"count\\":2}"\n}', + }; + renderInspector(duplicateDraft, onConfigurationChange); + + fireEvent.change( + screen.getByRole('textbox', { name: 'Property name for count' }), + { target: { value: 'name' } }, + ); + + await waitFor(() => + expect(screen.getByRole('button', { name: 'Apply step' })).toBeDisabled(), + ); + expect( + screen + .getAllByRole('alert') + .every((alert) => + alert.textContent?.includes('Property names must be unique.'), + ), + ).toBe(true); + expect(onConfigurationChange).not.toHaveBeenCalled(); + }); + it('states when a step has no settings instead of leaving an empty panel', () => { renderInspector(voteDraft); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx index 00fa9aa76e..4a245cb236 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowNodeInspector.tsx @@ -30,6 +30,7 @@ import { } from '@/shared/studio/nodeConfigFields'; import AevatarTooltip from '@/shared/ui/AevatarTooltip'; import TechnicalDetails from '../TechnicalDetails'; +import StructuredJsonArgumentsEditor from './StructuredJsonArgumentsEditor'; export type WorkflowNodeInspectorHandle = { requestDiscardOrProceed: (proceed: () => void) => void; @@ -327,6 +328,7 @@ const WorkflowNodeInspector = React.forwardRef< const [applying, setApplying] = React.useState(false); const [discardConfirmationOpen, setDiscardConfirmationOpen] = React.useState(false); + const [argumentsEditorError, setArgumentsEditorError] = React.useState(''); const [pendingDiscardAction, setPendingDiscardAction] = React.useState< (() => void) | null >(null); @@ -348,8 +350,19 @@ const WorkflowNodeInspector = React.forwardRef< } = useConfigurationDraft(stepDraft); React.useEffect(() => { - onConfigurationErrorChange(structuredError || rawError); - }, [onConfigurationErrorChange, rawError, structuredError]); + onConfigurationErrorChange( + structuredError || argumentsEditorError || rawError, + ); + }, [ + argumentsEditorError, + onConfigurationErrorChange, + rawError, + structuredError, + ]); + + React.useEffect(() => { + setArgumentsEditorError(''); + }, [stepDraft?.id, stepDraft?.type]); React.useEffect(() => { onUnappliedChangesChange?.(hasUnappliedChanges); @@ -380,7 +393,7 @@ const WorkflowNodeInspector = React.forwardRef< stepDraft.type, schemaParameters, ); - const validationError = structuredError || rawError; + const validationError = structuredError || argumentsEditorError || rawError; const nodeTypeLabel = formatStudioStepTypeLabel(stepDraft.type); const controlsDisabled = disabled || applying; @@ -437,7 +450,7 @@ const WorkflowNodeInspector = React.forwardRef< }; const applyConfiguration = () => { - if (controlsDisabled) return; + if (controlsDisabled || argumentsEditorError) return; const result = applyStudioNodeConfigurationValuesWithValidation( stepDraft.type, parameters, @@ -488,6 +501,20 @@ const WorkflowNodeInspector = React.forwardRef< const renderFieldControl = (field: StudioStructuredNodeConfigField) => { const value = configurationValues[field.name] ?? ''; const control = field.control ?? field.kind; + if ( + stepDraft.type.trim().toLowerCase() === 'tool_call' && + field.parameterName === 'arguments' + ) { + return ( + updateFieldValue(field.name, nextValue)} + onErrorChange={setArgumentsEditorError} + value={value} + /> + ); + } if (control === 'select') { return (
+ + + + + + + + + + {t('channels.manage', 'Manage')} + +
+ + + {[ + ['channel', 'Channel'], + ['skill', 'Skill'], + ['inbound', 'Inbound'], + ['delivery', 'Workflow delivery'], + ['actions', 'Actions'], + ].map(([key, label]) => ( + + ))} + + + + {registrations.data.map((registration) => ( + + ))} + +
+ {t(`channels.column.${key}`, label)} +
+
+ ) : ( +
+ +

{t('channels.empty.title', 'No channels connected yet')}

+

+ {t( + 'channels.empty.description', + 'Connect Lark or Telegram above to start talking to your agent.', + )} +

+
+ )} + + + ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts new file mode 100644 index 0000000000..4a7c369a79 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts @@ -0,0 +1,16 @@ +const DEFAULT_ONBOARDING_URL = + 'https://aevatar-console-backend-api.aevatar.ai/channels'; + +export function getChannelOnboardingUrl(): string | null { + const configured = process.env.AEVATAR_CHANNELS_ONBOARDING_URL; + try { + const url = new URL( + configured === undefined ? DEFAULT_ONBOARDING_URL : configured, + ); + return /^https?:$/.test(url.protocol) && !url.username && !url.password + ? url.href + : null; + } catch { + return null; + } +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx new file mode 100644 index 0000000000..9846986b7a --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx @@ -0,0 +1,285 @@ +import { + ApiOutlined, + LinkOutlined, + MessageOutlined, + SendOutlined, +} from '@ant-design/icons'; +import { Button } from 'antd'; +import * as React from 'react'; +import { + ChannelApiError, + type ChannelRegistration, +} from '@/shared/api/channelsApi'; +import { t } from '@/shared/i18n/messages'; +import { history } from '@/shared/navigation/history'; +import { getOrnnRuntimeConfig } from '@/shared/studio/ornnConfig'; + +export function ChannelLink({ + href, + children, + className, + ...rest +}: React.AnchorHTMLAttributes & { href: string }) { + return ( + { + if ( + event.button || + event.metaKey || + event.ctrlKey || + event.shiftKey || + event.altKey + ) + return; + event.preventDefault(); + history.push(href); + }} + > + {children} + + ); +} + +export function platformName(platform: string): string { + switch (platform.toLowerCase()) { + case 'lark': + return 'Lark'; + case 'feishu': + return t('channels.platform.feishu', 'Feishu'); + case 'telegram': + return 'Telegram'; + case 'discord': + return 'Discord'; + case 'slack': + return 'Slack'; + default: + return platform; + } +} + +export function ChannelIcon({ platform }: { readonly platform: string }) { + return ( + + ); +} + +export function compactChannelIdentifier(value: string): string { + return value.length > 24 ? `${value.slice(0, 8)}…${value.slice(-6)}` : value; +} + +export function ChannelIdentity({ + registration, +}: { + readonly registration: ChannelRegistration; +}) { + const identifier = registration.botId ?? registration.id; + return ( +
+ +
+ + {t('channels.bot', '{platform} bot', { + platform: platformName(registration.platform), + })} + + + {compactChannelIdentifier(identifier)} + +
+
+ ); +} + +export function ChannelSkill({ + skill, +}: { + readonly skill: ChannelRegistration['skill']; +}) { + if (!skill) + return ( + + {t('channels.skill.notSet', 'Not set')} + + ); + const config = getOrnnRuntimeConfig(); + // Ornn's published web route accepts an exact ID or name. The channel API + // supplies the name; do not derive a GUID or substitute a sample skill. + const href = config.configurationError + ? null + : `${config.baseUrl}/skills/${encodeURIComponent(skill.name)}`; + return ( +
+ {href ? ( + + {skill.name} + ) : ( + {skill.name} + )} + {skill.version ? ( + + {t('channels.skill.version', 'Version {version}', { + version: skill.version, + })} + + ) : null} +
+ ); +} + +type Tone = 'success' | 'warning' | 'danger' | 'neutral'; +export function ChannelBadge({ + children, + tone = 'neutral', +}: { + readonly children: React.ReactNode; + readonly tone?: Tone; +}) { + return ( + + + ); +} + +export function InboundStatus({ + value, + pending = false, +}: { + readonly value?: string; + readonly pending?: boolean; +}) { + if (pending) + return ( + {t('channels.status.checking', 'Checking…')} + ); + switch (value?.toLowerCase()) { + case 'active': + return ( + + {t('channels.status.active', 'Active')} + + ); + case 'pending': + case 'pending_webhook': + return ( + + {t('channels.status.pending', 'Waiting for message')} + + ); + case 'error': + case 'failed': + return ( + + {t('channels.status.error', 'Needs attention')} + + ); + default: + return ( + {t('channels.status.unknown', 'Unknown')} + ); + } +} + +export function DeliveryStatus({ + value, + platform, +}: { + readonly value: string | null; + readonly platform: string; +}) { + switch (value) { + case 'enabled': + return ( + + {t('channels.delivery.ready', 'Ready')} + + ); + case 'repair_failed': + return ( + + {t('channels.delivery.failed', 'Repair failed')} + + ); + case 'repairing': + return ( + + {t('channels.delivery.repairing', 'Repairing')} + + ); + case 'repair_required': + return platform === 'lark' ? ( + + {t('channels.delivery.repairRequired', 'Needs repair')} + + ) : ( + + {t('channels.delivery.notEnabled', 'Not enabled')} + + ); + case 'missing': + case 'not_enabled': + return ( + + {t('channels.delivery.notEnabled', 'Not enabled')} + + ); + default: + return ( + {t('channels.status.unknown', 'Unknown')} + ); + } +} + +export function ChannelLoadError({ + error, + retry, + pending, +}: { + readonly error: unknown; + readonly retry: () => void; + readonly pending: boolean; +}) { + const unavailable = + error instanceof ChannelApiError && [403, 404].includes(error.status); + return ( +
+

+ {unavailable + ? t( + 'channels.error.unavailable', + 'This channel is unavailable or you do not have access.', + ) + : t( + 'channels.error.load', + 'Could not load channels. Please try again.', + )} +

+ +
+ ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts new file mode 100644 index 0000000000..d2edf91f9b --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts @@ -0,0 +1,32 @@ +import { useQuery } from '@tanstack/react-query'; +import { channelsApi } from '@/shared/api/channelsApi'; + +export const channelKeys = { + list: (scopeId: string) => ['channels', scopeId, 'registrations'] as const, + status: (scopeId: string, registrationId: string) => + ['channels', scopeId, 'status', registrationId] as const, +}; + +export function useChannelRegistrations(scopeId: string, observeUntil = 0) { + return useQuery({ + queryKey: channelKeys.list(scopeId), + queryFn: ({ signal }) => channelsApi.list(signal), + enabled: Boolean(scopeId), + retry: false, + staleTime: 0, + refetchOnWindowFocus: true, + refetchInterval: () => (Date.now() < observeUntil ? 2000 : false), + }); +} + +export function useChannelStatus(scopeId: string, registrationId: string) { + return useQuery({ + queryKey: channelKeys.status(scopeId, registrationId), + queryFn: ({ signal }) => channelsApi.status(registrationId, signal), + enabled: Boolean(scopeId && registrationId), + retry: false, + staleTime: 15_000, + refetchOnWindowFocus: true, + refetchInterval: 30_000, + }); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts new file mode 100644 index 0000000000..7a75148851 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -0,0 +1,111 @@ +export const channelsCss = ` +.channels__main { + --channels-space: 24px; + --channels-border: #e4e7ec; + background: var(--wa-subtle); +} +.channels__main .wa-vnext__header { padding-top: 40px; } +.channels__main .wa-vnext__header h1 { font-size: 24px; line-height: 32px; } +.channels__content { max-width: 1160px; padding-top: 0; } +.channels__intro { color: var(--wa-muted); line-height: 20px; margin: 4px 0 28px; } +.channels__section-label { color: var(--wa-muted); font-size: 12px; font-weight: 500; margin: 0 0 12px; } +.channels__platforms { display: grid; gap: 16px; grid-template-columns: repeat(4, minmax(0, 1fr)); } +.channels__platform { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); display: flex; flex-direction: column; min-height: 224px; min-width: 0; padding: 20px; } +.channels__platform-top { align-items: flex-start; display: flex; gap: 8px; justify-content: space-between; } +.channels__icon { align-items: center; background: var(--wa-blue); border-radius: var(--wa-radius); color: white; display: inline-flex; flex: 0 0 auto; font-size: 22px; height: 42px; justify-content: center; width: 42px; } +.channels__platform h3 { font-size: 15px; font-weight: 600; line-height: 22px; margin: 16px 0 8px; } +.channels__platform p { color: var(--wa-muted); flex: 1; font-size: 12px; line-height: 19px; margin: 0 0 24px; } +.channels__platform--soon { background: transparent; } +.channels__platform--soon .channels__icon { background: var(--wa-faint); } +.channels__platform--soon h3 { color: var(--wa-muted); } +.channels__connect { align-items: center; color: var(--wa-blue); display: inline-flex; font-weight: 500; gap: 8px; min-height: 28px; width: fit-content; } +.channels__connections { margin-top: 32px; } +.channels__section-heading { align-items: center; display: flex; gap: 16px; justify-content: space-between; margin-bottom: 16px; } +.channels__section-heading h2 { align-items: center; display: flex; font-size: 16px; gap: 10px; line-height: 24px; margin: 0; } +.channels__section-heading p { color: var(--wa-muted); line-height: 20px; margin: 4px 0 0; } +.channels__count { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: 5px; color: var(--wa-muted); font-size: 11px; font-weight: 500; line-height: 20px; min-width: 24px; padding: 0 6px; text-align: center; } +.channels__table-wrap { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); overflow: hidden; } +.channels__table { border-collapse: collapse; table-layout: fixed; width: 100%; } +.channels__table th { background: var(--wa-subtle); border-bottom: 1px solid var(--channels-border); color: var(--wa-muted); font-size: 10px; font-weight: 600; line-height: 16px; padding: 12px 16px; text-align: left; text-transform: uppercase; } +.channels__table th:nth-child(1) { width: 29%; } +.channels__table th:nth-child(2) { width: 25%; } +.channels__table th:nth-child(3) { width: 16%; } +.channels__table th:nth-child(4) { width: 19%; } +.channels__table th:last-child { text-align: right; width: 11%; } +.channels__table td { border-bottom: 1px solid var(--channels-border); height: 92px; overflow-wrap: anywhere; padding: 16px; vertical-align: middle; } +.channels__table tr:last-child td { border-bottom: 0; } +.channels__table tbody tr:hover { background: var(--wa-subtle); } +.channels__identity { align-items: center; display: flex; gap: 12px; min-width: 0; } +.channels__identity .channels__icon { font-size: 18px; height: 36px; width: 36px; } +.channels__identity-copy { display: flex; flex: 1; flex-direction: column; gap: 4px; min-width: 0; } +.channels__identity-copy strong { font-size: 13px; font-weight: 600; } +.channels__identifier { color: var(--wa-muted); font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace; font-size: 11px; overflow-wrap: anywhere; } +.channels__identity .channels__identifier { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.channels__skill { display: flex; flex-direction: column; gap: 6px; overflow-wrap: anywhere; } +.channels__skill a { color: var(--wa-blue); font-weight: 500; } +.channels__skill .anticon { font-size: 10px; } +.channels__muted { color: var(--wa-muted); font-size: 11px; } +.channels__badge { align-items: center; background: var(--wa-subtle); border: 1px solid var(--channels-border); border-radius: 5px; color: var(--wa-muted); display: inline-flex; font-size: 10px; gap: 5px; line-height: 16px; max-width: 100%; padding: 1px 6px; } +.channels__badge--success { background: var(--wa-green-bg); border-color: transparent; color: var(--wa-green); } +.channels__badge--warning { background: var(--wa-amber-bg); border-color: transparent; color: var(--wa-amber); } +.channels__badge--danger { background: var(--wa-red-bg); border-color: transparent; color: var(--wa-red); } +.channels__dot { background: currentColor; border-radius: 50%; flex: 0 0 auto; height: 4px; width: 4px; } +.channels__row-action { text-align: right; } +.channels__manage { align-items: center; color: var(--wa-ink); display: inline-flex; font-weight: 600; gap: 6px; min-height: 32px; } +.channels__manage .anticon { font-size: 10px; } +.channels__state { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); color: var(--wa-muted); padding: 36px var(--channels-space); text-align: center; } +.channels__state h3 { color: var(--wa-ink); margin: 16px 0 8px; } +.channels__state p { line-height: 20px; margin: 0 0 16px; } +.channels__main--detail .wa-vnext__header { display: none; } +.channels__main--detail .channels__content { padding-top: 40px; } +.channels__breadcrumb { align-items: center; color: var(--wa-muted); display: flex; flex-wrap: wrap; font-size: 12px; gap: 12px; margin-bottom: 24px; } +.channels__breadcrumb a { align-items: center; color: var(--wa-blue); display: inline-flex; gap: 8px; min-height: 28px; } +.channels__detail { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); padding: var(--channels-space); } +.channels__detail-heading { align-items: center; display: flex; gap: 14px; margin-bottom: 24px; } +.channels__detail-heading h1 { font-size: 16px; font-weight: 600; line-height: 24px; margin: 0; overflow-wrap: anywhere; } +.channels__detail-heading p { margin: 0; } +.channels__facts { margin: 0; } +.channels__facts > div { align-items: baseline; display: grid; gap: 16px; grid-template-columns: 160px minmax(0, 1fr); padding: 10px 0; } +.channels__facts dt { color: var(--wa-muted); font-size: 12px; } +.channels__facts dd { font-size: 12px; margin: 0; overflow-wrap: anywhere; text-align: right; } +.channels__facts .channels__badge { background: none; border: none; color: var(--wa-ink); font-size: 12px; padding: 0; } +.channels__facts .channels__dot { display: none; } +.channels__detail-actions { margin-top: 20px; } +.channels__detail-actions .ant-btn { background: transparent; } +.channels__removal { align-items: center; display: flex; flex-wrap: wrap; gap: 16px; margin-top: 20px; } +.channels__removal p { color: var(--wa-muted); line-height: 20px; margin: 0; } +.channels__content a:focus-visible { border-radius: 3px; outline: 2px solid var(--wa-blue); outline-offset: 4px; } +@media (max-width: 1199px) { + .channels__platforms { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .channels__table th, .channels__table td { padding-left: 12px; padding-right: 12px; } + .channels__table th:nth-child(1) { width: 27%; } + .channels__table th:nth-child(2) { width: 22%; } + .channels__table th:nth-child(3) { width: 18%; } + .channels__table th:nth-child(4) { width: 20%; } + .channels__table th:last-child { width: 13%; } +} +@media (max-width: 767px) { + .channels__main { --channels-space: 20px; } + .channels__main .wa-vnext__header { padding-top: 24px; } + .channels__main .wa-vnext__header h1 { font-size: 22px; line-height: 30px; } + .channels__platforms { gap: 12px; } + .channels__platform { min-height: 210px; padding: 16px; } + .channels__platform-top { flex-wrap: wrap; } + .channels__table, .channels__table tbody, .channels__table tr, .channels__table td { display: block; width: 100%; } + .channels__table thead { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; width: 1px; } + .channels__table tr { border-bottom: 1px solid var(--channels-border); padding: 16px; } + .channels__table tr:last-child { border-bottom: none; } + .channels__table td { border: 0; height: auto; padding: 6px 0; } + .channels__table td[data-label]:not(:first-child) { align-items: baseline; display: grid; gap: 12px; grid-template-columns: 110px minmax(0, 1fr); } + .channels__table td[data-label]:not(:first-child)::before { color: var(--wa-muted); content: attr(data-label); font-size: 11px; } + .channels__table .channels__row-action { padding-top: 8px; } + .channels__main--detail .channels__content { padding-top: 20px; } + .channels__detail-heading { align-items: flex-start; flex-wrap: wrap; } + .channels__detail-heading .channels__badge { margin-left: 56px; } + .channels__facts > div { grid-template-columns: 108px minmax(0, 1fr); } + .channels__detail-heading h1 { font-size: 14px; } +} +@media (max-width: 359px) { + .channels__platforms { grid-template-columns: minmax(0, 1fr); } +} +`; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx index d3a3409087..8efd460b32 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { t } from '@/shared/i18n/messages'; import ActivityPage from './activity/ActivityPage'; import RunDetailPage from './activity/RunDetailPage'; +import ChannelDetailsPage from './channels/ChannelDetailsPage'; +import ChannelsPage from './channels/ChannelsPage'; import { useConsoleLocation } from './hooks/useConsoleLocation'; import SettingsPage from './settings/SettingsPage'; import WorkflowActivityVNextShell from './WorkflowActivityVNextShell'; @@ -54,6 +56,20 @@ const WorkflowActivityVNextPage: React.FC = () => { return ; } + const channelMatch = /\/channels\/([^/]+)$/.exec(pathname); + if (channelMatch) { + return ( + + ); + } + if (pathname.endsWith('/channels')) { + return ; + } + if (pathname.endsWith('/settings')) { return ; } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts index 1f1a6ac858..2ede6af1d9 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts @@ -1,4 +1,8 @@ -export type WorkflowActivitySection = 'workflows' | 'activity' | 'settings'; +export type WorkflowActivitySection = + | 'workflows' + | 'activity' + | 'channels' + | 'settings'; export type WorkflowActivitySettingsSection = 'ai' | 'account' | 'advanced'; function encode(value: string): string { @@ -72,3 +76,10 @@ export function buildWorkflowActivityRunHref( const suffix = buildQuerySuffix(query); return suffix ? `${base}?${suffix}` : base; } + +export function buildChannelDetailsHref( + scopeId: string, + registrationId: string, +): string { + return `${buildWorkflowActivitySectionHref(scopeId, 'channels')}/${encodeURIComponent(registrationId)}`; +} diff --git a/apps/aevatar-console-web/src/routesConfig.test.ts b/apps/aevatar-console-web/src/routesConfig.test.ts index b3958ff3e1..9514bc02ec 100644 --- a/apps/aevatar-console-web/src/routesConfig.test.ts +++ b/apps/aevatar-console-web/src/routesConfig.test.ts @@ -214,6 +214,8 @@ describe('console routes', () => { `${namespace}/workflows/:workflowId`, `${namespace}/activity`, `${namespace}/activity/:runId`, + `${namespace}/channels`, + `${namespace}/channels/:registrationId`, `${namespace}/settings`, ]; diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts new file mode 100644 index 0000000000..fb09fef043 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts @@ -0,0 +1,135 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { ChannelApiError, channelsApi } from './channelsApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +const fetchMock = jest.mocked(authFetch); +const response = (value: unknown, status = 200) => + ({ + ok: status >= 200 && status < 300, + status, + json: async () => value, + }) as Response; + +const registration = { + id: 'registration-alpha', + platform: 'telegram', + scope_id: 'scope-alpha', + owned: true, + nyx_channel_bot_id: 'bot-alpha', + nyx_provider_slug: 'telegram-provider', + nyx_agent_api_key_id: 'key-alpha', + workflow_result_delivery_status: 'enabled', +}; + +afterEach(() => fetchMock.mockReset()); + +describe('channel API boundary', () => { + it('reads the owner list and keeps only safe display fields with the exact typed skill', async () => { + fetchMock.mockResolvedValue( + response([ + { + ...registration, + default_skill: { name: 'review.skill', version: '2.4' }, + agent_key: { api_key_id: 'key-current', raw_key: 'TEST_ONLY_SECRET' }, + runtime_config: { instructions: 'private instructions' }, + access_token: 'TEST_ONLY_SECRET', + webhook_url: 'https://example.invalid/private', + }, + { ...registration, id: 'registration-other-owner', owned: false }, + ]), + ); + const result = await channelsApi.list(); + expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); + expect(result).toEqual([ + { + id: 'registration-alpha', + platform: 'telegram', + scopeId: 'scope-alpha', + botId: 'bot-alpha', + providerSlug: 'telegram-provider', + agentKeyId: 'key-current', + skill: { name: 'review.skill', version: '2.4' }, + workflowDeliveryStatus: 'enabled', + owned: true, + }, + ]); + expect(JSON.stringify(result)).not.toMatch( + /TEST_ONLY_SECRET|instructions|webhook/, + ); + }); + + it('accepts the deployed name-only contract without inventing a version', async () => { + fetchMock.mockResolvedValue( + response([ + { ...registration, default_skill_name: 'legacy-skill' }, + { + ...registration, + id: 'registration-unset', + default_skill: { name: '', version: '7' }, + }, + ]), + ); + expect((await channelsApi.list()).map((row) => row.skill)).toEqual([ + { name: 'legacy-skill', version: null }, + null, + ]); + }); + + it('rejects malformed list responses instead of presenting an empty collection', async () => { + fetchMock.mockResolvedValue(response({ registrations: [] })); + await expect(channelsApi.list()).rejects.toThrow(); + }); + + it('encodes opaque registration IDs and rejects a status for a different registration', async () => { + const id = 'registration:alpha/one + two'; + fetchMock.mockResolvedValue( + response({ + registration_id: id, + status: 'new_backend_state', + workflow_result_delivery_status: 'enabled', + }), + ); + expect(await channelsApi.status(id)).toEqual({ + registrationId: id, + status: 'new_backend_state', + workflowDeliveryStatus: 'enabled', + }); + expect(fetchMock.mock.calls[0][0]).toBe( + '/api/channels/registrations/registration%3Aalpha%2Fone%20%2B%20two/status', + ); + fetchMock.mockResolvedValue( + response({ registration_id: 'registration-other', status: 'active' }), + ); + await expect(channelsApi.status(id)).rejects.toThrow('identity mismatch'); + }); + + it('uses the registration identity for deletion and discards cleanup diagnostics', async () => { + fetchMock.mockResolvedValue( + response({ status: 'deleted', warnings: ['TEST_ONLY_SECRET'] }), + ); + await expect(channelsApi.remove('registration:alpha/one')).resolves.toEqual( + { hasWarnings: true }, + ); + expect(fetchMock).toHaveBeenCalledWith( + '/api/channels/registrations/registration%3Aalpha%2Fone', + expect.objectContaining({ method: 'DELETE' }), + ); + fetchMock.mockResolvedValue(response({ status: 'accepted' }, 202)); + await expect(channelsApi.remove('registration-alpha')).rejects.toThrow( + 'not confirmed', + ); + }); + + it('preserves HTTP failure classification without retaining backend secret material', async () => { + const json = jest.fn().mockResolvedValue({ message: 'TEST_ONLY_SECRET' }); + fetchMock.mockResolvedValue({ + ok: false, + status: 404, + json, + } as unknown as Response); + await expect(channelsApi.status('registration-alpha')).rejects.toEqual( + new ChannelApiError(404), + ); + expect(json).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts new file mode 100644 index 0000000000..2e9e3a1f87 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -0,0 +1,160 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { + expectArray, + expectBoolean, + expectRecord, + readOptionalString, + readString, +} from './http/decoders'; + +export interface ChannelRegistration { + readonly id: string; + readonly platform: string; + readonly scopeId: string; + readonly botId: string | null; + readonly providerSlug: string | null; + readonly agentKeyId: string | null; + readonly skill: { + readonly name: string; + readonly version: string | null; + } | null; + readonly workflowDeliveryStatus: string | null; + readonly owned: boolean; +} + +export interface ChannelStatus { + readonly registrationId: string; + readonly status: string; + readonly workflowDeliveryStatus: string | null; +} + +export class ChannelApiError extends Error { + constructor(readonly status: number) { + // Backend error bodies can contain provisioning diagnostics. Keep them out + // of the query cache and use localized recovery copy in the page. + super(`Channel request failed (${status}).`); + this.name = 'ChannelApiError'; + } +} + +function decodeRegistration(value: unknown): ChannelRegistration { + const row = expectRecord(value, 'Channel registration'); + const skill = + row.default_skill == null + ? null + : expectRecord(row.default_skill, 'Channel default skill'); + const key = + row.agent_key == null + ? null + : expectRecord(row.agent_key, 'Channel agent key'); + const skillName = skill + ? readOptionalString(skill, 'name', 'Skill name')?.trim() + : readOptionalString(row, 'default_skill_name', 'Skill name')?.trim(); + const id = readString(row, 'id', 'Registration ID'); + if (!id.trim()) throw new Error('Missing channel registration ID.'); + // Explicitly project safe display fields. Never retain the transport object, + // webhook URL, runtime configuration, or unexpected credential fields. + return { + id, + platform: readString(row, 'platform', 'Channel platform'), + scopeId: readString(row, 'scope_id', 'Channel scope'), + botId: readOptionalString(row, 'nyx_channel_bot_id', 'Bot ID') || null, + providerSlug: + readOptionalString(row, 'nyx_provider_slug', 'Provider') || null, + agentKeyId: + (key + ? readOptionalString(key, 'api_key_id', 'Agent key ID') + : readOptionalString(row, 'nyx_agent_api_key_id', 'Agent key ID')) || + null, + skill: skillName + ? { + name: skillName, + version: skill + ? readOptionalString(skill, 'version', 'Skill version')?.trim() || + null + : null, + } + : null, + workflowDeliveryStatus: + readOptionalString( + row, + 'workflow_result_delivery_status', + 'Workflow delivery status', + ) || null, + owned: expectBoolean(row.owned, 'Channel ownership'), + }; +} + +async function request( + path: string, + decode: (value: unknown) => T, + init?: RequestInit, +): Promise { + const response = await authFetch(path, { + ...init, + headers: { Accept: 'application/json' }, + }); + if (!response.ok) throw new ChannelApiError(response.status); + return decode(await response.json()); +} + +function registrationPath(registrationId: string): string { + if (!registrationId.trim()) + throw new Error('Missing channel registration ID.'); + return `/api/channels/registrations/${encodeURIComponent(registrationId)}`; +} + +export const channelsApi = { + list(signal?: AbortSignal): Promise { + return request( + '/api/channels/registrations', + (value) => + expectArray(value, 'Channel registrations', decodeRegistration).filter( + (row) => row.owned, + ), + { signal }, + ); + }, + status(registrationId: string, signal?: AbortSignal): Promise { + return request( + `${registrationPath(registrationId)}/status`, + (value) => { + const row = expectRecord(value, 'Channel status'); + const returnedId = readString( + row, + 'registration_id', + 'Registration ID', + ); + if (returnedId !== registrationId) + throw new Error('Channel status identity mismatch.'); + return { + registrationId: returnedId, + status: readString(row, 'status', 'Inbound status'), + workflowDeliveryStatus: + readOptionalString( + row, + 'workflow_result_delivery_status', + 'Workflow delivery status', + ) || null, + }; + }, + { signal }, + ); + }, + async remove( + registrationId: string, + ): Promise<{ readonly hasWarnings: boolean }> { + return request( + registrationPath(registrationId), + (value) => { + const row = expectRecord(value, 'Channel removal'); + if (row.status !== 'deleted') + throw new Error('Channel removal was not confirmed.'); + return { + hasWarnings: Array.isArray(row.warnings) && row.warnings.length > 0, + }; + }, + { method: 'DELETE' }, + ); + }, +}; From 6d5bd2a7eccd72ed95f7792a2a047b45e3ec9b04 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:53:03 +0800 Subject: [PATCH 128/167] Add Telegram connection form with explicit service selection --- apps/aevatar-console-web/config/config.ts | 4 +- apps/aevatar-console-web/config/routes.ts | 5 + .../docs/features/2026-09-14-channel-pages.md | 77 ++- .../src/locales/channelMessages.en-US.ts | 64 ++- .../src/locales/channelMessages.zh-CN.ts | 58 ++- .../channels/ChannelServicePicker.tsx | 155 ++++++ .../channels/ChannelsPage.test.tsx | 9 +- .../channels/ChannelsPage.tsx | 27 +- .../channels/TelegramConnectionPage.test.tsx | 289 +++++++++++ .../channels/TelegramConnectionPage.tsx | 456 ++++++++++++++++++ .../channels/config.ts | 18 +- .../channels/connectionStyles.ts | 55 +++ .../pages/workflow-activity-vnext/index.tsx | 4 + .../workflow-activity-vnext/navigation.ts | 4 + .../src/routesConfig.test.ts | 1 + .../shared/api/channelConnectionApi.test.ts | 148 ++++++ .../src/shared/api/channelServicesApi.ts | 64 +++ .../src/shared/api/channelsApi.ts | 90 ++++ 18 files changed, 1483 insertions(+), 45 deletions(-) create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts create mode 100644 apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts create mode 100644 apps/aevatar-console-web/src/shared/api/channelServicesApi.ts diff --git a/apps/aevatar-console-web/config/config.ts b/apps/aevatar-console-web/config/config.ts index eeb03bfaf8..48e1007401 100644 --- a/apps/aevatar-console-web/config/config.ts +++ b/apps/aevatar-console-web/config/config.ts @@ -164,8 +164,8 @@ const config: ReturnType = defineConfig({ process.env.NYXID_REDIRECT_URI, ), 'process.env.NYXID_SCOPE': JSON.stringify(process.env.NYXID_SCOPE), - 'process.env.AEVATAR_CHANNELS_ONBOARDING_URL': JSON.stringify( - process.env.AEVATAR_CHANNELS_ONBOARDING_URL, + 'process.env.AEVATAR_CHANNEL_WEBHOOK_BASE_URL': JSON.stringify( + process.env.AEVATAR_CHANNEL_WEBHOOK_BASE_URL, ), 'process.env.ORNN_BASE_URL': JSON.stringify(process.env.ORNN_BASE_URL), 'process.env.AEVATAR_CONSOLE_TEAM_FIRST_ENABLED': JSON.stringify( diff --git a/apps/aevatar-console-web/config/routes.ts b/apps/aevatar-console-web/config/routes.ts index 54faaa8807..15d5b3cab8 100644 --- a/apps/aevatar-console-web/config/routes.ts +++ b/apps/aevatar-console-web/config/routes.ts @@ -80,6 +80,11 @@ export default [ component: './workflow-activity-vnext', hideInMenu: true, }, + { + path: '/scopes/:scopeId/workflow-activity-vnext/channels/connect/telegram', + component: './workflow-activity-vnext', + hideInMenu: true, + }, { path: '/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId', component: './workflow-activity-vnext', diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 9badf96cff..242eaf4548 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -3,8 +3,8 @@ Channels is a first-level destination in the Workflow Activity vNext sidebar. It lets the signed-in owner view their connected bots and inspect a connection. -The two pages follow the latest simplified [Figma design](https://www.figma.com/design/FaVJx5IZeQX9jHcUi55ndB?node-id=6-2) -(list frame `6:2`, detail frame `6:131`). The current design intentionally keeps +The three pages follow the latest simplified [Figma design](https://www.figma.com/design/FaVJx5IZeQX9jHcUi55ndB?node-id=7-226) +(list frame `7:2`, detail frame `6:131`, Telegram form `7:226`). The current design intentionally keeps channel details read-only with Remove as the sole resource action. The earlier runtime-configuration editor is outside this iteration of issue #3617. @@ -14,16 +14,69 @@ runtime-configuration editor is outside this iteration of issue #3617. owner connection list. - `/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId`: exact connection details. Registration IDs are opaque and encoded as one segment. -- Connect Lark and Connect Telegram open the existing backend `/channels` - onboarding page. That page owns credentials, registration, platform setup, - reply model, and verification; it currently has no platform-specific deep - link. Feishu, Discord, and Slack remain visibly unavailable. - -`AEVATAR_CHANNELS_ONBOARDING_URL` optionally sets the existing setup page URL -at build time. The default is the deployed -`https://aevatar-console-backend-api.aevatar.ai/channels`. Configure this value -for other deployments; an invalid value disables setup links. No credential is -added to the URL and the existing onboarding authentication flow is preserved. +- `/scopes/:scopeId/workflow-activity-vnext/channels/connect/telegram`: the + single-page Telegram connection form. Connect Telegram navigates here. + Lark, Feishu, Discord, and Slack are marked Soon in the current design. + The older backend `/channels` onboarding page remains independently available. + +`AEVATAR_CHANNEL_WEBHOOK_BASE_URL` optionally sets the public backend callback +base URL at build time. The default is +`https://aevatar-console-backend-api.aevatar.ai`. It must be HTTPS without +credentials, query, or fragment. An invalid value disables registration with +an inline deployment-configuration message. It is never inferred from the +separate Console frontend origin or shown as an editable form field. + +## Telegram connection form + +The form accepts a masked bot token, Label, Skill name, and an explicit service +selection. The token reveal control is keyboard accessible. Unsaved form +navigation asks whether to discard; form data is never persisted. Only the +authenticated registration POST carries the token, and successful admission +or an uncertain result clears it. No registration mutation cache stores it. + +Services come from the current NyxID session's +`GET /api/v1/user-services`, resolved against the existing NyxID authority. +Its `{services: [...]}` response differs from the Aevatar runtime-config +inventory: it uses `is_active` and `credential_source.type` (`personal` or +`org`, with an `allowed` flag on the organization variant). Unknown variants, +inactive services, and disallowed organization services cannot be selected. +Only safe display fields enter the query cache. + +Options use label, then catalog service name, then slug. Search matches label +or slug and preserves selections outside the current search. Each selection +uses the exact `services[].id`; no default selection is made. The registration +POST always supplies `authorization_mode: explicit_service_allowlist` and +`service_ids`, including `[]` for no service access. It never substitutes a +slug, endpoint ID, API key ID, or omitted field. If a selected service becomes +unavailable, submission is blocked until it is deselected. Load failure has +inline retry, while a genuine empty inventory can connect without service access. + +Registration uses existing `POST /api/channels/registrations` with +`platform`, `bot_token`, `label`, `default_skill_name`, `webhook_base_url`, and +the explicit selection above. A `202`/`status: accepted` response must contain +the registration ID. The page waits for that exact Telegram registration in +the owner list before showing success and opening details; inbound activity +continues to use its real status. Observation is bounded to 20 seconds and +Check again performs GET only. Network/server failures with uncertain effects +require checking Channels before another setup, preventing automatic duplicates. + +### Outstanding bot-name contract + +The design asks for pre-registration token recognition and independent bot-name +fallbacks for empty Label and Skill name. The reviewed `feature/integrate` +backend and #3617/#3620 do not expose a non-mutating token/name lookup route. +The deployed registration behavior also does not supply these fallbacks: it +generates a generic label and forwards the skill name unchanged. NyxID's +existing create/verify operations mutate registrations and cannot serve as a +read-only preview. + +Until that server contract exists, the form explicitly reports that automatic +lookup is unavailable and requires both names. It does not claim a bot has +been recognized or submit a sample name. This is the remaining difference from +the requested design; the frontend does not add a backend route or call Telegram +directly with a token in its request URL. A future lookup must return the bot's +`first_name`, preserve user edits, reject stale token results, and fall back +independently for each name (a custom label must never supply the skill name). ## API and ownership diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 7932b4c0de..df4a715155 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -2,7 +2,7 @@ export default { 'workflowActivityVNext.nav.channels': 'Channels', 'channels.title': 'Connect your channels to your agent', 'channels.description': - 'Choose a channel and follow the setup: credentials → register → platform setup → reply model → verify.', + 'Choose a channel to connect your bot and select the services it can use.', 'channels.intro': 'Once connected, talk to your Aevatar bot directly from the channel.', 'channels.available': 'Available channels', @@ -10,7 +10,6 @@ export default { 'channels.soon': 'Soon', 'channels.connect': 'Connect', 'channels.connectPlatform': 'Connect {platform}', - 'channels.setupUnavailable': 'Setup unavailable', 'channels.platform.feishu': 'Feishu', 'channels.platform.lark.description': 'Connect your bot to chats and groups.', 'channels.platform.feishu.description': 'Feishu, using the same local flow.', @@ -45,7 +44,7 @@ export default { 'channels.delivery.notEnabled': 'Not enabled', 'channels.empty.title': 'No channels connected yet', 'channels.empty.description': - 'Connect Lark or Telegram above to start talking to your agent.', + 'Connect Telegram above to start talking to your agent.', 'channels.retry': 'Try again', 'channels.error.load': 'Could not load channels. Please try again.', 'channels.error.refresh': 'Could not refresh channels. Try again.', @@ -74,4 +73,63 @@ export default { 'channels.remove.success': 'Channel removed.', 'channels.remove.warning': 'Channel removed. Some cleanup needs attention in NyxID.', + 'channels.connect.title': 'Connect Telegram', + 'channels.connect.description': + 'Add your bot and choose the services it can use. Telegram webhook setup is automatic.', + 'channels.connect.botDetails': 'Bot details', + 'channels.connect.botToken': 'Bot token', + 'channels.connect.showToken': 'Show bot token', + 'channels.connect.hideToken': 'Hide bot token', + 'channels.connect.botFather': 'Get token from BotFather', + 'channels.connect.label': 'Label', + 'channels.connect.skillName': 'Skill name', + 'channels.connect.nameLookupUnavailable': + 'Automatic bot-name lookup is not available yet. Enter a Label and Skill name below to connect.', + 'channels.connect.nameHelp': 'Use the Telegram bot name or enter your own.', + 'channels.connect.tokenRequired': 'Enter the bot token from BotFather.', + 'channels.connect.nameRequired': + 'Enter a name while automatic lookup is unavailable.', + 'channels.connect.services': 'Services', + 'channels.connect.selected': '{count} selected', + 'channels.connect.servicesHelp': + 'Select the services this bot can use from your NyxID account.', + 'channels.connect.servicesLoading': 'Loading services', + 'channels.connect.servicesError': + 'Could not load your services. Retry before connecting.', + 'channels.connect.servicesEmpty': + 'No services are available in your NyxID account. You can connect this bot without service access.', + 'channels.connect.searchServices': 'Search services by name or slug', + 'channels.connect.serviceUnavailable': 'Unavailable', + 'channels.connect.personal': 'Personal', + 'channels.connect.organization': 'Organization', + 'channels.connect.noMatches': 'No services match your search.', + 'channels.connect.selectionHelp': + 'Only selected services will be available to this bot.', + 'channels.connect.selectionChanged': + 'Some selected services are no longer available. Deselect them before connecting.', + 'channels.connect.pending': + 'Telegram setup was submitted. Waiting for your channel to appear.', + 'channels.connect.success': + 'Telegram added. Send your bot a message to get started.', + 'channels.connect.error.token': + 'Check the bot token from BotFather and try again.', + 'channels.connect.error.services': + 'The selected services are no longer available. Review your selection and try again.', + 'channels.connect.error.skill': + 'The Skill name could not be used. Check the name and your access in Ornn.', + 'channels.connect.error.authorization': + 'Your session or service access needs attention. Sign in again and review your NyxID access.', + 'channels.connect.error.conflict': + 'This bot may already be connected. Check your channels before trying again.', + 'channels.connect.error.configuration': + 'Channel setup is unavailable. Ask your administrator to check the deployment configuration.', + 'channels.connect.error.rejected': + 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', + 'channels.connect.error.uncertain': + 'The connection result could not be confirmed. Check your channels before starting another connection.', + 'channels.connect.discardTitle': 'Discard this connection setup?', + 'channels.connect.discard': 'Discard', + 'channels.connect.stay': 'Stay', + 'channels.connect.discardHelp': + 'Your bot token and unsaved choices will be cleared.', }; diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 9a0d5a84c4..ad618fc15c 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -1,15 +1,13 @@ export default { 'workflowActivityVNext.nav.channels': '渠道', 'channels.title': '将渠道连接到你的 Agent', - 'channels.description': - '选择渠道并完成设置:凭据 → 注册 → 平台设置 → 回复模型 → 验证。', + 'channels.description': '选择渠道,连接你的机器人并选择它可以使用的服务。', 'channels.intro': '连接后,即可直接在渠道中与你的 Aevatar 机器人对话。', 'channels.available': '可用渠道', 'channels.availableNow': '可用', 'channels.soon': '即将支持', 'channels.connect': '连接', 'channels.connectPlatform': '连接 {platform}', - 'channels.setupUnavailable': '接入暂不可用', 'channels.platform.feishu': '飞书', 'channels.platform.lark.description': '将机器人接入聊天和群组。', 'channels.platform.feishu.description': '面向飞书的渠道接入。', @@ -43,8 +41,7 @@ export default { 'channels.delivery.repairRequired': '需要修复', 'channels.delivery.notEnabled': '未启用', 'channels.empty.title': '尚未连接渠道', - 'channels.empty.description': - '在上方连接 Lark 或 Telegram,开始与你的 Agent 对话。', + 'channels.empty.description': '在上方连接 Telegram,开始与你的 Agent 对话。', 'channels.retry': '重试', 'channels.error.load': '无法加载渠道,请重试。', 'channels.error.refresh': '无法刷新渠道,请重试。', @@ -70,4 +67,55 @@ export default { 'channels.remove.check': '重新检查', 'channels.remove.success': '渠道已移除。', 'channels.remove.warning': '渠道已移除,部分清理操作需要在 NyxID 中处理。', + 'channels.connect.title': '连接 Telegram', + 'channels.connect.description': + '添加机器人,选择它可以使用的服务。Telegram Webhook 将自动配置。', + 'channels.connect.botDetails': '机器人信息', + 'channels.connect.botToken': '机器人令牌', + 'channels.connect.showToken': '显示机器人令牌', + 'channels.connect.hideToken': '隐藏机器人令牌', + 'channels.connect.botFather': '从 BotFather 获取令牌', + 'channels.connect.label': '名称', + 'channels.connect.skillName': '技能名称', + 'channels.connect.nameLookupUnavailable': + '自动识别机器人名称暂不可用。请填写下方的名称和技能名称后连接。', + 'channels.connect.nameHelp': '使用 Telegram 机器人名称,或填写自定义名称。', + 'channels.connect.tokenRequired': '请输入从 BotFather 获取的机器人令牌。', + 'channels.connect.nameRequired': '自动识别暂不可用,请填写名称。', + 'channels.connect.services': '服务', + 'channels.connect.selected': '已选 {count} 项', + 'channels.connect.servicesHelp': + '从你的 NyxID 账户中选择此机器人可以使用的服务。', + 'channels.connect.servicesLoading': '正在加载服务', + 'channels.connect.servicesError': '无法加载服务,请重试后再连接。', + 'channels.connect.servicesEmpty': + '你的 NyxID 账户暂无可用服务。可以连接机器人,但不授予服务访问权限。', + 'channels.connect.searchServices': '按名称或标识搜索服务', + 'channels.connect.serviceUnavailable': '不可用', + 'channels.connect.personal': '个人', + 'channels.connect.organization': '组织', + 'channels.connect.noMatches': '没有匹配的服务。', + 'channels.connect.selectionHelp': '此机器人只能使用选中的服务。', + 'channels.connect.selectionChanged': + '部分选中服务已不可用,请取消勾选后再连接。', + 'channels.connect.pending': '已提交 Telegram 接入请求,正在等待渠道显示。', + 'channels.connect.success': 'Telegram 已添加,给机器人发送消息即可开始。', + 'channels.connect.error.token': '请检查 BotFather 提供的令牌后重试。', + 'channels.connect.error.services': '所选服务已不可用,请重新检查选择后重试。', + 'channels.connect.error.skill': + '无法使用此技能名称,请在 Ornn 中检查名称和访问权限。', + 'channels.connect.error.authorization': + '登录状态或服务权限需要处理,请重新登录并检查 NyxID 访问权限。', + 'channels.connect.error.conflict': + '此机器人可能已连接,请先查看渠道列表再重试。', + 'channels.connect.error.configuration': + '渠道接入暂不可用,请联系管理员检查部署配置。', + 'channels.connect.error.rejected': + '无法连接 Telegram,请检查机器人令牌、技能名称和所选服务后重试。', + 'channels.connect.error.uncertain': + '暂时无法确认连接结果,请先查看渠道列表,再尝试新的连接。', + 'channels.connect.discardTitle': '放弃此次连接设置?', + 'channels.connect.discard': '放弃', + 'channels.connect.stay': '继续填写', + 'channels.connect.discardHelp': '机器人令牌和未保存的选择将被清除。', }; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx new file mode 100644 index 0000000000..5c64936c6f --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx @@ -0,0 +1,155 @@ +import { SearchOutlined } from '@ant-design/icons'; +import { Button, Checkbox, Input } from 'antd'; +import * as React from 'react'; +import type { ChannelServiceChoice } from '@/shared/api/channelServicesApi'; +import { t } from '@/shared/i18n/messages'; +import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; + +export default function ChannelServicePicker({ + services, + selectedIds, + onChange, + loading, + failed, + refreshing, + disabled, + retry, +}: { + readonly services: readonly ChannelServiceChoice[]; + readonly selectedIds: readonly string[]; + readonly onChange: (ids: string[]) => void; + readonly loading: boolean; + readonly failed: boolean; + readonly refreshing: boolean; + readonly disabled: boolean; + readonly retry: () => void; +}) { + const [search, setSearch] = React.useState(''); + const term = search.trim().toLocaleLowerCase(); + const visible = services.filter((service) => + `${service.label} ${service.slug}`.toLocaleLowerCase().includes(term), + ); + return ( +
+
+

+ {t('channels.connect.services', 'Services')} +

+ + {t('channels.connect.selected', '{count} selected', { + count: selectedIds.length, + })} + +
+

+ {t( + 'channels.connect.servicesHelp', + 'Select the services this bot can use from your NyxID account.', + )} +

+ {loading ? ( + + ) : failed ? ( +
+

+ {t( + 'channels.connect.servicesError', + 'Could not load your services. Retry before connecting.', + )} +

+ +
+ ) : !services.length ? ( +
+ {t( + 'channels.connect.servicesEmpty', + 'No services are available in your NyxID account. You can connect this bot without service access.', + )} +
+ ) : ( +
+ } + value={search} + onChange={(event) => setSearch(event.target.value)} + disabled={disabled} + /> +
+ {visible.length ? ( + visible.map((service) => { + const selected = selectedIds.includes(service.id); + const unavailable = !service.active || !service.allowed; + return ( +
+ + onChange( + event.target.checked + ? [...selectedIds, service.id] + : selectedIds.filter((id) => id !== service.id), + ) + } + > + + {service.label} + + + {service.slug} + + + + {unavailable + ? t( + 'channels.connect.serviceUnavailable', + 'Unavailable', + ) + : service.source === 'personal' + ? t('channels.connect.personal', 'Personal') + : service.organizationName || + t('channels.connect.organization', 'Organization')} + +
+ ); + }) + ) : ( +

+ {t( + 'channels.connect.noMatches', + 'No services match your search.', + )} +

+ )} +
+
+ )} +

+ {t( + 'channels.connect.selectionHelp', + 'Only selected services will be available to this bot.', + )} +

+
+ ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index 106c9d9a6d..aebbd42425 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -91,8 +91,11 @@ describe('Channel pages', () => { screen.getByRole('link', { name: 'Connect Telegram' }), ).toHaveAttribute( 'href', - 'https://aevatar-console-backend-api.aevatar.ai/channels', + '/scopes/scope-alpha/workflow-activity-vnext/channels/connect/telegram', ); + expect( + screen.queryByRole('link', { name: 'Connect Lark' }), + ).not.toBeInTheDocument(); expect( screen.queryByRole('link', { name: 'Connect Slack' }), ).not.toBeInTheDocument(); @@ -106,6 +109,10 @@ describe('Channel pages', () => { ), ); expect(await within(table).findByText('Active')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('link', { name: 'Connect Telegram' })); + expect(history.push).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels/connect/telegram', + ); fireEvent.click(within(table).getByRole('link', { name: 'Manage' })); expect(history.push).toHaveBeenCalledWith( '/scopes/scope-alpha/workflow-activity-vnext/channels/registration%3Aalpha%2Fone', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 31d1388ccf..60473a45a6 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -6,9 +6,11 @@ import type { ChannelRegistration } from '@/shared/api/channelsApi'; import { t } from '@/shared/i18n/messages'; import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; -import { buildChannelDetailsHref } from '../navigation'; +import { + buildChannelDetailsHref, + buildTelegramConnectionHref, +} from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; -import { getChannelOnboardingUrl } from './config'; import { ChannelBadge, ChannelIcon, @@ -78,7 +80,6 @@ export default function ChannelsPage({ const toast = useConsoleToast(); const queryClient = useQueryClient(); const [refreshing, setRefreshing] = React.useState(false); - const onboardingUrl = getChannelOnboardingUrl(); async function refresh() { setRefreshing(true); try { @@ -104,7 +105,7 @@ export default function ChannelsPage({ title={t('channels.title', 'Connect your channels to your agent')} description={t( 'channels.description', - 'Choose a channel and follow the setup: credentials → register → platform setup → reply model → verify.', + 'Choose a channel to connect your bot and select the services it can use.', )} mainClassName="channels__main" contentClassName="channels__content" @@ -122,7 +123,7 @@ export default function ChannelsPage({
{platforms.map((platform) => { - const available = platform === 'lark' || platform === 'telegram'; + const available = platform === 'telegram'; return (
- {available && onboardingUrl ? ( - {t('channels.connect', 'Connect')} + ) : ( - {available - ? t('channels.setupUnavailable', 'Setup unavailable') - : t('channels.soon', 'Soon')} + {t('channels.soon', 'Soon')} )}
@@ -258,7 +255,7 @@ export default function ChannelsPage({

{t( 'channels.empty.description', - 'Connect Lark or Telegram above to start talking to your agent.', + 'Connect Telegram above to start talking to your agent.', )}

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx new file mode 100644 index 0000000000..90e4a3e411 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -0,0 +1,289 @@ +import { + act, + fireEvent, + screen, + waitFor, + within, +} from '@testing-library/react'; +import * as React from 'react'; +import { authFetch } from '@/shared/auth/fetch'; +import { history } from '@/shared/navigation/history'; +import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; +import WorkflowActivityVNextPage from '../index'; +import TelegramConnectionPage from './TelegramConnectionPage'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), +})); +jest.mock('@/shared/studio/api', () => ({ + studioApi: { + getAuthSession: jest.fn().mockResolvedValue({ authenticated: false }), + }, +})); +const mockToast = { + success: jest.fn(), + warning: jest.fn(), + error: jest.fn(), + info: jest.fn(), +}; +jest.mock('@/shared/ui/ConsoleToast', () => ({ + ...jest.requireActual('@/shared/ui/ConsoleToast'), + useConsoleToast: () => mockToast, +})); +const fetchMock = jest.mocked(authFetch); +const response = (value: unknown, status = 200) => + ({ + ok: status >= 200 && status < 300, + status, + json: async () => value, + }) as Response; +const service = { + id: 'user-service-github', + slug: 'api-github', + label: 'GitHub work', + is_active: true, + credential_source: { type: 'personal' }, +}; +const servicePath = 'https://nyx.example.test/api/v1/user-services'; +const listPath = '/api/channels/registrations'; +function enterNames() { + fireEvent.change(screen.getByLabelText(/^Bot token/), { + target: { value: 'TEST_ONLY_TOKEN' }, + }); + fireEvent.change(screen.getByLabelText(/^Label/), { + target: { value: 'My team bot' }, + }); + fireEvent.change(screen.getByLabelText(/^Skill name/), { + target: { value: 'review.skill' }, + }); +} +beforeEach(() => { + fetchMock.mockReset(); + Object.values(mockToast).forEach((mock) => { + mock.mockReset(); + }); + jest.spyOn(history, 'push').mockImplementation(() => {}); + jest.spyOn(history, 'replace').mockImplementation(() => {}); + fetchMock.mockImplementation(async (input) => + input === servicePath ? response({ services: [service] }) : response([]), + ); +}); + +it('searches services, enforces permissions, submits selected IDs once and waits for the new registration', async () => { + let resolvePost!: (value: Response) => void; + const post = new Promise((resolve) => { + resolvePost = resolve; + }); + let observed = false; + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') return post; + if (input === servicePath) + return response({ + services: [ + service, + { + ...service, + id: 'user-service-org', + slug: 'slack', + label: 'Team Slack', + credential_source: { type: 'org', allowed: false }, + }, + { + ...service, + id: 'user-service-inactive', + slug: 'drive', + label: 'Drive', + is_active: false, + }, + ], + }); + return response( + observed + ? [ + { + id: 'registration-created', + platform: 'telegram', + scope_id: 'scope-alpha', + owned: true, + }, + ] + : [], + ); + }); + window.history.replaceState( + {}, + '', + '/scopes/scope-alpha/workflow-activity-vnext/channels/connect/telegram', + ); + const view = renderWithQueryClient(); + expect( + await screen.findByRole('checkbox', { name: /GitHub work/ }), + ).not.toBeChecked(); + expect(screen.getByRole('checkbox', { name: /Team Slack/ })).toBeDisabled(); + expect(screen.getByRole('checkbox', { name: /Drive/ })).toBeDisabled(); + fireEvent.click(screen.getByRole('checkbox', { name: /GitHub work/ })); + fireEvent.change( + screen.getByRole('textbox', { name: 'Search services by name or slug' }), + { target: { value: 'slack' } }, + ); + expect( + screen.queryByRole('checkbox', { name: /GitHub work/ }), + ).not.toBeInTheDocument(); + expect(screen.getByText('1 selected')).toBeInTheDocument(); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); + const posts = fetchMock.mock.calls.filter( + ([, init]) => init?.method === 'POST', + ); + expect(posts).toHaveLength(1); + expect(posts[0][0]).toBe(listPath); + expect(JSON.parse(String(posts[0][1]?.body))).toEqual({ + platform: 'telegram', + bot_token: 'TEST_ONLY_TOKEN', + label: 'My team bot', + default_skill_name: 'review.skill', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], + webhook_base_url: 'https://aevatar-console-backend-api.aevatar.ai', + }); + await act(async () => + resolvePost( + response( + { + status: 'accepted', + registration_id: 'registration-created', + webhook_url: 'TEST_ONLY_SECRET', + }, + 202, + ), + ), + ); + expect( + await screen.findByText(/Telegram setup was submitted/), + ).toBeInTheDocument(); + expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); + expect(mockToast.success).not.toHaveBeenCalled(); + expect(history.replace).not.toHaveBeenCalled(); + expect( + JSON.stringify( + view.queryClient + .getQueryCache() + .getAll() + .map((query) => query.state.data), + ), + ).not.toContain('TEST_ONLY'); + expect(view.queryClient.getMutationCache().getAll()).toHaveLength(0); + observed = true; + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + await waitFor(() => + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-created', + ), + ); + expect(mockToast.success).toHaveBeenCalledTimes(1); +}); + +it('recovers service loading failure, validates missing fields and allows an explicit empty selection', async () => { + let serviceReads = 0; + fetchMock.mockImplementation(async (input, init) => { + if (input === servicePath) + return ++serviceReads === 1 + ? response({}, 503) + : response({ services: [] }); + if (init?.method === 'POST') + return response( + { error: 'missing_bot_token', note: 'TEST_ONLY_SECRET' }, + 400, + ); + return response([]); + }); + renderWithQueryClient(); + expect(await screen.findByRole('alert')).toHaveTextContent( + 'Could not load your services', + ); + expect( + screen.getByRole('button', { name: 'Connect Telegram' }), + ).toBeDisabled(); + fireEvent.click(screen.getByRole('button', { name: 'Try again' })); + expect( + await screen.findByText(/No services are available/), + ).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + expect( + screen.getByText('Enter the bot token from BotFather.'), + ).toBeInTheDocument(); + expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'POST')).toBe( + false, + ); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + expect( + await screen.findByText( + 'Check the bot token from BotFather and try again.', + ), + ).toBeInTheDocument(); + expect( + JSON.parse( + String( + fetchMock.mock.calls.find(([, init]) => init?.method === 'POST')?.[1] + ?.body, + ), + ).service_ids, + ).toEqual([]); + expect(screen.getByLabelText(/^Bot token/)).toHaveValue('TEST_ONLY_TOKEN'); + expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); + expect( + await screen.findByRole('button', { name: 'Connect Telegram' }), + ).toBeEnabled(); +}); + +it('keeps an uncertain submission from creating a duplicate and directs the owner back to their channels', async () => { + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') throw new Error('TEST_ONLY_SECRET'); + return input === servicePath ? response({ services: [] }) : response([]); + }); + renderWithQueryClient(); + await screen.findByText(/No services are available/); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + expect(await screen.findByRole('alert')).toHaveTextContent( + 'The connection result could not be confirmed', + ); + expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); + expect( + await screen.findByRole('button', { name: 'Connect Telegram' }), + ).toBeDisabled(); + expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); + expect(mockToast.success).not.toHaveBeenCalled(); + fireEvent.click(screen.getByRole('button', { name: 'Back to channels' })); + expect(history.push).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ); +}); + +it('confirms discarding an edited setup without issuing a registration request', async () => { + renderWithQueryClient(); + await screen.findByRole('checkbox', { name: /GitHub work/ }); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + const dialog = await screen.findByRole('dialog', { + name: 'Discard this connection setup?', + }); + expect(history.push).not.toHaveBeenCalled(); + fireEvent.click(within(dialog).getByRole('button', { name: 'Stay' })); + expect(screen.getByLabelText(/^Label/)).toHaveValue('My team bot'); + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + fireEvent.click( + within(await screen.findByRole('dialog')).getByRole('button', { + name: 'Discard', + }), + ); + expect(history.push).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ); + expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'POST')).toBe( + false, + ); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx new file mode 100644 index 0000000000..f1903fbffd --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -0,0 +1,456 @@ +import { + ArrowLeftOutlined, + ExportOutlined, + EyeInvisibleOutlined, + EyeOutlined, +} from '@ant-design/icons'; +import { useQuery } from '@tanstack/react-query'; +import { Button, Input, Modal } from 'antd'; +import * as React from 'react'; +import { listChannelServices } from '@/shared/api/channelServicesApi'; +import { + ChannelRegistrationError, + type ChannelRegistrationFailure, + channelsApi, +} from '@/shared/api/channelsApi'; +import { t } from '@/shared/i18n/messages'; +import { history } from '@/shared/navigation/history'; +import { useConsoleToast } from '@/shared/ui/ConsoleToast'; +import { + buildChannelDetailsHref, + buildWorkflowActivitySectionHref, +} from '../navigation'; +import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; +import ChannelServicePicker from './ChannelServicePicker'; +import { getChannelWebhookBaseUrl } from './config'; +import { channelConnectionCss } from './connectionStyles'; +import { useChannelRegistrations } from './queries'; +import { channelsCss } from './styles'; + +function registrationErrorMessage(reason: ChannelRegistrationFailure) { + const messages = { + token: [ + 'channels.connect.error.token', + 'Check the bot token from BotFather and try again.', + ], + services: [ + 'channels.connect.error.services', + 'The selected services are no longer available. Review your selection and try again.', + ], + skill: [ + 'channels.connect.error.skill', + 'The Skill name could not be used. Check the name and your access in Ornn.', + ], + authorization: [ + 'channels.connect.error.authorization', + 'Your session or service access needs attention. Sign in again and review your NyxID access.', + ], + conflict: [ + 'channels.connect.error.conflict', + 'This bot may already be connected. Check your channels before trying again.', + ], + configuration: [ + 'channels.connect.error.configuration', + 'Channel setup is unavailable. Ask your administrator to check the deployment configuration.', + ], + rejected: [ + 'channels.connect.error.rejected', + 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', + ], + uncertain: [ + 'channels.connect.error.uncertain', + 'The connection result could not be confirmed. Check your channels before starting another connection.', + ], + } as const; + const [key, fallback] = messages[reason]; + return t(key, fallback); +} + +export default function TelegramConnectionPage({ + scopeId, +}: { + readonly scopeId: string; +}) { + // Secret input stays in this component only, never in Query mutation state, + // a persisted draft, URL, or navigation state. + const [botToken, setBotToken] = React.useState(''); + const [label, setLabel] = React.useState(''); + const [skillName, setSkillName] = React.useState(''); + const [selectedIds, setSelectedIds] = React.useState([]); + const [submitting, setSubmitting] = React.useState(false); + const [submittedId, setSubmittedId] = React.useState(null); + const [observeUntil, setObserveUntil] = React.useState(0); + const [error, setError] = React.useState( + null, + ); + const [validationAttempted, setValidationAttempted] = React.useState(false); + const [leaveTarget, setLeaveTarget] = React.useState(null); + const inFlight = React.useRef(false); + const mounted = React.useRef(true); + const completed = React.useRef(false); + const toast = useConsoleToast(); + const webhookBaseUrl = getChannelWebhookBaseUrl(); + const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); + const registrations = useChannelRegistrations(scopeId, observeUntil); + const services = useQuery({ + queryKey: ['channels', scopeId, 'service-choices'], + queryFn: ({ signal }) => listChannelServices(signal), + enabled: Boolean(scopeId), + retry: false, + staleTime: 0, + }); + const invalidSelection = selectedIds.some( + (id) => + !services.data?.some( + (service) => service.id === id && service.active && service.allowed, + ), + ); + const locked = submitting || submittedId !== null || error === 'uncertain'; + const dirty = Boolean(botToken || label || skillName || selectedIds.length); + + React.useEffect(() => { + mounted.current = true; + return () => { + mounted.current = false; + }; + }, []); + React.useEffect(() => { + if (!dirty && !submitting) return; + const warn = (event: BeforeUnloadEvent) => { + event.preventDefault(); + event.returnValue = ''; + }; + window.addEventListener('beforeunload', warn); + return () => window.removeEventListener('beforeunload', warn); + }, [dirty, submitting]); + React.useEffect(() => { + if ( + !submittedId || + !registrations.isSuccess || + registrations.isFetching || + completed.current + ) + return; + const registration = registrations.data.find( + (row) => row.id === submittedId && row.platform === 'telegram', + ); + if (!registration) return; + completed.current = true; + toast.success( + t( + 'channels.connect.success', + 'Telegram added. Send your bot a message to get started.', + ), + ); + history.replace(buildChannelDetailsHref(scopeId, registration.id)); + }, [ + submittedId, + registrations.data, + registrations.isSuccess, + registrations.isFetching, + scopeId, + toast, + ]); + + function navigate(target: string) { + if (submitting) return; + if (dirty && !submittedId && error !== 'uncertain') setLeaveTarget(target); + else history.push(target); + } + + async function connect(event: React.FormEvent) { + event.preventDefault(); + if (inFlight.current || locked) return; + setValidationAttempted(true); + if ( + !botToken.trim() || + !label.trim() || + !skillName.trim() || + !webhookBaseUrl || + !services.isSuccess || + invalidSelection + ) + return; + inFlight.current = true; + setSubmitting(true); + setError(null); + try { + const receipt = await channelsApi.registerTelegram({ + botToken, + label, + skillName, + serviceIds: selectedIds, + webhookBaseUrl, + }); + if (!mounted.current) return; + setBotToken(''); + setSubmittedId(receipt.registrationId); + setObserveUntil(Date.now() + 20_000); + await registrations.refetch(); + } catch (failure) { + if (!mounted.current) return; + const reason = + failure instanceof ChannelRegistrationError + ? failure.reason + : 'uncertain'; + setError(reason); + if (reason === 'uncertain') setBotToken(''); + if (reason === 'services') void services.refetch(); + toast.error(registrationErrorMessage(reason)); + } finally { + inFlight.current = false; + if (mounted.current) setSubmitting(false); + } + } + + return ( + + +
+ +

{t('channels.connect.title', 'Connect Telegram')}

+

+ {t( + 'channels.connect.description', + 'Add your bot and choose the services it can use. Telegram webhook setup is automatic.', + )} +

+
+
void connect(event)} + noValidate + aria-label={t('channels.connect.title', 'Connect Telegram')} + > +

+ {t('channels.connect.botDetails', 'Bot details')} +

+
+
+ + + {t('channels.connect.botFather', 'Get token from BotFather')}{' '} + +
+ ( + + )} + aria-describedby="telegram-token-help" + aria-invalid={ + validationAttempted && !botToken.trim() && !submittedId + } + onChange={(event) => setBotToken(event.target.value)} + /> +

+ {t( + 'channels.connect.nameLookupUnavailable', + 'Automatic bot-name lookup is not available yet. Enter a Label and Skill name below to connect.', + )} +

+ {validationAttempted && !botToken.trim() && !locked ? ( +

+ {t( + 'channels.connect.tokenRequired', + 'Enter the bot token from BotFather.', + )} +

+ ) : null} +
+
+ {[ + { + id: 'telegram-label', + key: 'label', + title: 'Label', + value: label, + change: setLabel, + }, + { + id: 'telegram-skill', + key: 'skillName', + title: 'Skill name', + value: skillName, + change: setSkillName, + }, + ].map((field) => ( +
+
+ +
+ field.change(event.target.value)} + /> +

+ {t( + 'channels.connect.nameHelp', + 'Use the Telegram bot name or enter your own.', + )} +

+ {validationAttempted && !field.value.trim() ? ( +

+ {t( + 'channels.connect.nameRequired', + 'Enter a name while automatic lookup is unavailable.', + )} +

+ ) : null} +
+ ))} +
+ void services.refetch()} + /> + {invalidSelection && !locked ? ( +

+ {t( + 'channels.connect.selectionChanged', + 'Some selected services are no longer available. Deselect them before connecting.', + )} +

+ ) : null} + {!webhookBaseUrl ? ( +

+ {registrationErrorMessage('configuration')} +

+ ) : null} + {error ? ( +

+ {registrationErrorMessage(error)} +

+ ) : null} + {submittedId ? ( +
+

+ {t( + 'channels.connect.pending', + 'Telegram setup was submitted. Waiting for your channel to appear.', + )} +

+ +
+ ) : null} +
+ + +
+ + setLeaveTarget(null)} + onOk={() => { + if (leaveTarget) history.push(leaveTarget); + setLeaveTarget(null); + }} + okText={t('channels.connect.discard', 'Discard')} + cancelText={t('channels.connect.stay', 'Stay')} + > + {t( + 'channels.connect.discardHelp', + 'Your bot token and unsaved choices will be cleared.', + )} + +
+ ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts index 4a7c369a79..26be2b7cce 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts @@ -1,14 +1,18 @@ -const DEFAULT_ONBOARDING_URL = - 'https://aevatar-console-backend-api.aevatar.ai/channels'; +const DEFAULT_WEBHOOK_BASE_URL = + 'https://aevatar-console-backend-api.aevatar.ai'; -export function getChannelOnboardingUrl(): string | null { - const configured = process.env.AEVATAR_CHANNELS_ONBOARDING_URL; +export function getChannelWebhookBaseUrl(): string | null { + const configured = process.env.AEVATAR_CHANNEL_WEBHOOK_BASE_URL; try { const url = new URL( - configured === undefined ? DEFAULT_ONBOARDING_URL : configured, + configured === undefined ? DEFAULT_WEBHOOK_BASE_URL : configured, ); - return /^https?:$/.test(url.protocol) && !url.username && !url.password - ? url.href + return url.protocol === 'https:' && + !url.username && + !url.password && + !url.search && + !url.hash + ? url.href.replace(/\/+$/, '') : null; } catch { return null; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts new file mode 100644 index 0000000000..23d7a48519 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts @@ -0,0 +1,55 @@ +export const channelConnectionCss = ` +.channels__main--connect .wa-vnext__header { display: none; } +.channels__main--connect .channels__content { max-width: 1080px; padding-top: 32px; } +.channels__connect-heading { margin-bottom: 28px; } +.channels__connect-heading .channels__breadcrumb { margin-bottom: 8px; } +.channels__connect-heading h1 { color: var(--wa-ink); font-size: 28px; font-weight: 650; line-height: 36px; margin: 0 0 8px; } +.channels__connect-heading > p { color: var(--wa-muted); line-height: 22px; margin: 0; } +.channels__connection-form { --channel-field-height: 44px; background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); padding: 28px; } +.channels__connection-form h2 { color: var(--wa-ink); font-size: 14px; line-height: 22px; margin: 0; } +.channels__form-section-title { margin-bottom: 20px !important; } +.channels__field { margin-bottom: 24px; min-width: 0; } +.channels__field-heading { align-items: baseline; display: flex; flex-wrap: wrap; gap: 8px; justify-content: space-between; margin-bottom: 8px; } +.channels__field-heading label { color: var(--wa-ink); font-size: 12px; font-weight: 600; } +.channels__field-heading label > span { color: var(--wa-muted); font-size: 11px; font-weight: 400; margin-left: 5px; } +.channels__field-heading label > .channels__required { color: var(--wa-blue); margin-left: 2px; } +.channels__field-heading a { color: var(--wa-blue); font-size: 11px; } +.channels__connection-form .ant-input, .channels__connection-form .ant-input-affix-wrapper { border-color: var(--channels-border); border-radius: 5px; min-height: var(--channel-field-height); } +.channels__connection-form .ant-input-affix-wrapper .ant-input { min-height: auto; } +.channels__form-help { color: var(--wa-muted); font-size: 11px; line-height: 18px; margin: 8px 0 0; } +.channels__form-error { color: var(--wa-red); font-size: 12px; line-height: 20px; margin: 8px 0 0; } +.channels__connection-form button.ant-input-password-icon { background: transparent; border: 0; cursor: pointer; padding: 4px; } +.channels__connection-form button.ant-input-password-icon:focus-visible { outline: 2px solid var(--wa-blue); outline-offset: 2px; } +.channels__name-fields { display: grid; gap: 24px; grid-template-columns: repeat(2, minmax(0, 1fr)); } +.channels__services-heading { align-items: center; display: flex; gap: 16px; justify-content: space-between; } +.channels__services-heading > span { color: var(--wa-blue); font-size: 11px; } +.channels__service-picker { border: 1px solid var(--channels-border); border-radius: 5px; margin-top: 12px; padding: 10px 10px 0; } +.channels__service-picker > .ant-input-affix-wrapper { background: var(--wa-subtle); min-height: 34px; } +.channels__service-picker .ant-input { background: transparent; font-size: 12px; } +.channels__service-picker .ant-input-prefix { color: var(--wa-muted); margin-right: 8px; } +.channels__service-options { max-height: 240px; overflow-y: auto; padding-top: 8px; scrollbar-gutter: stable; } +.channels__service-option { align-items: center; border-bottom: 1px solid var(--channels-border); display: flex; gap: 12px; min-height: 56px; padding: 8px; } +.channels__service-option:last-child { border-bottom: 0; } +.channels__service-option--selected { background: var(--wa-blue-bg); } +.channels__service-option .ant-checkbox-wrapper { align-items: flex-start; flex: 1; min-width: 0; } +.channels__service-option .ant-checkbox { margin-top: 3px; } +.channels__service-name { display: block; font-size: 12px; font-weight: 600; line-height: 20px; overflow-wrap: anywhere; } +.channels__service-slug { color: var(--wa-muted); display: block; font-size: 11px; line-height: 18px; overflow-wrap: anywhere; } +.channels__service-source { color: var(--wa-muted); flex: 0 1 150px; font-size: 11px; overflow-wrap: anywhere; text-align: right; } +.channels__service-state { color: var(--wa-muted); font-size: 12px; line-height: 20px; margin: 12px 0 0; padding: 20px 12px; } +.channels__service-state p { margin: 0 0 12px; } +.channels__form-actions { align-items: center; border-top: 1px solid var(--channels-border); display: flex; gap: 12px; justify-content: space-between; margin-top: 28px; padding-top: 20px; } +.channels__form-actions .ant-btn { font-size: 12px; min-height: 40px; padding-inline: 18px; } +.channels__connection-pending { color: var(--wa-muted); line-height: 22px; margin-top: 20px; } +.channels__connection-pending p { margin: 0 0 12px; } +@media (max-width: 767px) { + .channels__main--connect .channels__content { padding-top: 20px; } + .channels__connect-heading h1 { font-size: 24px; line-height: 32px; } + .channels__connect-heading { margin-bottom: 20px; } + .channels__connection-form { padding: 20px; } + .channels__name-fields { gap: 0; grid-template-columns: minmax(0, 1fr); } + .channels__service-option { align-items: flex-start; gap: 8px; padding-inline: 2px; } + .channels__service-source { flex-basis: 80px; padding-top: 3px; } + .channels__form-actions .ant-btn { padding-inline: 12px; } +} +`; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx index 8efd460b32..9baf2a3e99 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx @@ -4,6 +4,7 @@ import ActivityPage from './activity/ActivityPage'; import RunDetailPage from './activity/RunDetailPage'; import ChannelDetailsPage from './channels/ChannelDetailsPage'; import ChannelsPage from './channels/ChannelsPage'; +import TelegramConnectionPage from './channels/TelegramConnectionPage'; import { useConsoleLocation } from './hooks/useConsoleLocation'; import SettingsPage from './settings/SettingsPage'; import WorkflowActivityVNextShell from './WorkflowActivityVNextShell'; @@ -56,6 +57,9 @@ const WorkflowActivityVNextPage: React.FC = () => { return ; } + if (pathname.endsWith('/channels/connect/telegram')) { + return ; + } const channelMatch = /\/channels\/([^/]+)$/.exec(pathname); if (channelMatch) { return ( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts index 2ede6af1d9..75ecc3d008 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts @@ -83,3 +83,7 @@ export function buildChannelDetailsHref( ): string { return `${buildWorkflowActivitySectionHref(scopeId, 'channels')}/${encodeURIComponent(registrationId)}`; } + +export function buildTelegramConnectionHref(scopeId: string): string { + return `${buildWorkflowActivitySectionHref(scopeId, 'channels')}/connect/telegram`; +} diff --git a/apps/aevatar-console-web/src/routesConfig.test.ts b/apps/aevatar-console-web/src/routesConfig.test.ts index 9514bc02ec..62831c1c7d 100644 --- a/apps/aevatar-console-web/src/routesConfig.test.ts +++ b/apps/aevatar-console-web/src/routesConfig.test.ts @@ -215,6 +215,7 @@ describe('console routes', () => { `${namespace}/activity`, `${namespace}/activity/:runId`, `${namespace}/channels`, + `${namespace}/channels/connect/telegram`, `${namespace}/channels/:registrationId`, `${namespace}/settings`, ]; diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts new file mode 100644 index 0000000000..1c9044346f --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -0,0 +1,148 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { listChannelServices } from './channelServicesApi'; +import { ChannelRegistrationError, channelsApi } from './channelsApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), +})); +const fetchMock = jest.mocked(authFetch); +const response = (value: unknown, status = 200) => + ({ + ok: status >= 200 && status < 300, + status, + json: async () => value, + }) as Response; + +afterEach(() => fetchMock.mockReset()); + +it('maps the documented NyxID credential variants and keeps exact service IDs without retaining credential data', async () => { + fetchMock.mockResolvedValue( + response({ + services: [ + { + id: 'user-service-a', + slug: 'api-github', + label: 'GitHub work', + is_active: true, + credential_source: { type: 'personal' }, + default_request_headers: [{ value: 'TEST_ONLY_SECRET' }], + }, + { + id: 'user-service-b', + slug: 'drive', + catalog_service_name: 'Google Drive', + is_active: true, + credential_source: { + type: 'org', + org_name: 'Team', + role: 'viewer', + allowed: false, + }, + }, + { + id: 'user-service-c', + slug: 'calendar', + is_active: false, + credential_source: { type: 'org', org_name: 'Team', allowed: true }, + }, + { + id: 'user-service-d', + slug: 'future', + is_active: true, + credential_source: { type: 'new_source', allowed: true }, + }, + ], + }), + ); + const result = await listChannelServices(); + expect(fetchMock.mock.calls[0][0]).toBe( + 'https://nyx.example.test/api/v1/user-services', + ); + expect( + result.map(({ id, label, allowed, active, source }) => ({ + id, + label, + allowed, + active, + source, + })), + ).toEqual([ + { + id: 'user-service-a', + label: 'GitHub work', + allowed: true, + active: true, + source: 'personal', + }, + { + id: 'user-service-b', + label: 'Google Drive', + allowed: false, + active: true, + source: 'organization', + }, + { + id: 'user-service-c', + label: 'calendar', + allowed: true, + active: false, + source: 'organization', + }, + { + id: 'user-service-d', + label: 'future', + allowed: false, + active: true, + source: 'unknown', + }, + ]); + expect(JSON.stringify(result)).not.toContain('TEST_ONLY_SECRET'); +}); + +it('preserves an explicit empty allowlist and retains only the accepted registration identity', async () => { + fetchMock.mockResolvedValue( + response( + { + status: 'accepted', + registration_id: 'registration-new', + webhook_url: 'TEST_ONLY_SECRET', + }, + 202, + ), + ); + expect( + await channelsApi.registerTelegram({ + botToken: ' TEST_ONLY_TOKEN ', + label: ' Label ', + skillName: ' Skill ', + serviceIds: [], + webhookBaseUrl: 'https://api.example.test', + }), + ).toEqual({ registrationId: 'registration-new' }); + expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); + expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toEqual({ + platform: 'telegram', + bot_token: 'TEST_ONLY_TOKEN', + label: 'Label', + default_skill_name: 'Skill', + authorization_mode: 'explicit_service_allowlist', + service_ids: [], + webhook_base_url: 'https://api.example.test', + }); + fetchMock.mockResolvedValue( + response( + { status: 'accepted', registration_id: '', note: 'TEST_ONLY_SECRET' }, + 202, + ), + ); + await expect( + channelsApi.registerTelegram({ + botToken: 'TEST_ONLY_TOKEN', + label: 'Label', + skillName: 'Skill', + serviceIds: [], + webhookBaseUrl: 'https://api.example.test', + }), + ).rejects.toEqual(new ChannelRegistrationError('uncertain')); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts new file mode 100644 index 0000000000..6aba840982 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts @@ -0,0 +1,64 @@ +import { getNyxIDRuntimeConfig } from '@/shared/auth/config'; +import { authFetch } from '@/shared/auth/fetch'; +import { ChannelApiError } from './channelsApi'; +import { + expectArray, + expectBoolean, + expectRecord, + readOptionalString, + readString, +} from './http/decoders'; + +export interface ChannelServiceChoice { + readonly id: string; + readonly slug: string; + readonly label: string; + readonly active: boolean; + readonly allowed: boolean; + readonly source: 'personal' | 'organization' | 'unknown'; + readonly organizationName: string | null; +} + +function decodeService(value: unknown): ChannelServiceChoice { + const row = expectRecord(value, 'User service'); + const source = expectRecord(row.credential_source, 'Credential source'); + const id = readString(row, 'id', 'User service ID'); + const slug = readString(row, 'slug', 'Service slug'); + if (!id.trim() || !slug.trim()) throw new Error('Missing service identity.'); + const personal = source.type === 'personal'; + const organization = source.type === 'org'; + // Only the documented personal variant has implicit permission. Unknown + // variants and organization rows without allowed=true cannot be selected. + return { + id, + slug, + label: + readOptionalString(row, 'label', 'Service label')?.trim() || + readOptionalString(row, 'catalog_service_name', 'Catalog name')?.trim() || + slug, + active: expectBoolean(row.is_active, 'Service activity'), + allowed: personal || (organization && source.allowed === true), + source: personal ? 'personal' : organization ? 'organization' : 'unknown', + organizationName: organization + ? readOptionalString(source, 'org_name', 'Organization name') || null + : null, + }; +} + +export async function listChannelServices( + signal?: AbortSignal, +): Promise { + const config = getNyxIDRuntimeConfig(); + if (config.configurationError || !config.baseUrl) + throw new Error('NyxID is unavailable.'); + const response = await authFetch(`${config.baseUrl}/api/v1/user-services`, { + signal, + headers: { Accept: 'application/json' }, + }); + if (!response.ok) throw new ChannelApiError(response.status); + const body = expectRecord(await response.json(), 'User services'); + const services = expectArray(body.services, 'User services', decodeService); + if (new Set(services.map((service) => service.id)).size !== services.length) + throw new Error('Ambiguous user service identity.'); + return services; +} diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 2e9e3a1f87..520a405389 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -37,6 +37,52 @@ export class ChannelApiError extends Error { } } +export type ChannelRegistrationFailure = + | 'token' + | 'services' + | 'skill' + | 'authorization' + | 'conflict' + | 'configuration' + | 'rejected' + | 'uncertain'; + +export class ChannelRegistrationError extends Error { + constructor(readonly reason: ChannelRegistrationFailure) { + super('Could not connect Telegram.'); + this.name = 'ChannelRegistrationError'; + } +} + +export interface TelegramRegistrationInput { + readonly botToken: string; + readonly label: string; + readonly skillName: string; + readonly serviceIds: readonly string[]; + readonly webhookBaseUrl: string; +} + +function registrationFailure(status: number, value: unknown) { + const code = + value && typeof value === 'object' && 'error' in value + ? value.error + : undefined; + if (code === 'missing_bot_token' || code === 'invalid_bot_token') + return 'token'; + if (code === 'invalid_service_ids') return 'services'; + if (code === 'invalid_default_skill' || code === 'skill_not_found') + return 'skill'; + if (status === 401 || status === 403) return 'authorization'; + if (status === 409) return 'conflict'; + if ( + code === 'missing_webhook_base_url' || + code === 'insecure_webhook_base_url' || + code === 'nyx_base_url_not_configured' + ) + return 'configuration'; + return status >= 500 ? 'uncertain' : 'rejected'; +} + function decodeRegistration(value: unknown): ChannelRegistration { const row = expectRecord(value, 'Channel registration'); const skill = @@ -105,6 +151,50 @@ function registrationPath(registrationId: string): string { } export const channelsApi = { + async registerTelegram( + input: TelegramRegistrationInput, + ): Promise<{ readonly registrationId: string }> { + // This request carries the token only in the authenticated POST body. + // Do not use a mutation cache, navigation state, or persistent draft. + let response: Response; + try { + response = await authFetch('/api/channels/registrations', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + platform: 'telegram', + bot_token: input.botToken.trim(), + label: input.label.trim(), + default_skill_name: input.skillName.trim(), + webhook_base_url: input.webhookBaseUrl, + authorization_mode: 'explicit_service_allowlist', + service_ids: [...input.serviceIds], + }), + }); + } catch { + throw new ChannelRegistrationError('uncertain'); + } + const body: unknown = await response.json().catch(() => null); + if (!response.ok) + throw new ChannelRegistrationError( + registrationFailure(response.status, body), + ); + if ( + !body || + typeof body !== 'object' || + !('status' in body) || + body.status !== 'accepted' || + !('registration_id' in body) || + typeof body.registration_id !== 'string' || + !body.registration_id.trim() + ) + throw new ChannelRegistrationError('uncertain'); + // Retain the identity needed for readback, not raw provisioning output. + return { registrationId: body.registration_id }; + }, list(signal?: AbortSignal): Promise { return request( '/api/channels/registrations', From 7ada90075e848154972eee7fabf950c5342d396d Mon Sep 17 00:00:00 2001 From: Abigail Deng Date: Mon, 14 Sep 2026 20:00:35 +0800 Subject: [PATCH 129/167] Correct canvas benchmark gestures and measurement accounting --- apps/aevatar-console-web/package.json | 2 +- .../workflowCanvas.performance.spec.ts | 49 +++++++++++++------ .../benchmarkGraph.test.ts | 41 ++++++++++++++++ .../benchmarkGraph.ts | 30 +++++++++++- .../pages/workflow-canvas-benchmark/index.tsx | 17 +++++++ 5 files changed, 123 insertions(+), 16 deletions(-) diff --git a/apps/aevatar-console-web/package.json b/apps/aevatar-console-web/package.json index a9178f1b74..3162f229b6 100644 --- a/apps/aevatar-console-web/package.json +++ b/apps/aevatar-console-web/package.json @@ -7,7 +7,7 @@ "scripts": { "analyze": "cross-env ANALYZE=1 max build", "benchmark:workflow-canvas": "playwright test --config=playwright.performance.config.ts", - "benchmark:workflow-canvas:serve": "cross-env AEVATAR_WORKFLOW_CANVAS_BENCHMARK=1 max preview --port 4174", + "benchmark:workflow-canvas:serve": "cross-env UMI_ENV=production AEVATAR_WORKFLOW_CANVAS_BENCHMARK=1 max preview --port 4174", "build": "max build", "build:web": "npm run build", "dev": "npm run start:dev", diff --git a/apps/aevatar-console-web/performance/workflowCanvas.performance.spec.ts b/apps/aevatar-console-web/performance/workflowCanvas.performance.spec.ts index 9642927a19..329693068a 100644 --- a/apps/aevatar-console-web/performance/workflowCanvas.performance.spec.ts +++ b/apps/aevatar-console-web/performance/workflowCanvas.performance.spec.ts @@ -7,6 +7,7 @@ import { createWorkflowCanvasBenchmarkProgress, WORKFLOW_CANVAS_BENCHMARK_GRAPH_SIZES, WORKFLOW_CANVAS_BENCHMARK_SCENARIOS, + type WorkflowCanvasBenchmarkMeasurement, type WorkflowCanvasBenchmarkPolicy, type WorkflowCanvasBenchmarkReactCommit, type WorkflowCanvasBenchmarkResult, @@ -221,6 +222,8 @@ async function findInteractableNodePair( } const paneBounds = pane.getBoundingClientRect(); + // Keep gestures away from clipped nodes and React Flow's edge auto-pan zone. + const gestureMargin = 80; const seenIds = new Set(); const targets: InteractableNodeTarget[] = []; const nodes = document.querySelectorAll('.react-flow__node'); @@ -239,7 +242,11 @@ async function findInteractableNodePair( }; if ( intersection.right <= intersection.left || - intersection.bottom <= intersection.top + intersection.bottom <= intersection.top || + nodeBounds.left < paneBounds.left + gestureMargin || + nodeBounds.right > paneBounds.right - gestureMargin || + nodeBounds.top < paneBounds.top + gestureMargin || + nodeBounds.bottom > paneBounds.bottom - gestureMargin ) { continue; } @@ -249,7 +256,11 @@ async function findInteractableNodePair( y: (intersection.top + intersection.bottom) / 2, }; const hitTarget = document.elementFromPoint(point.x, point.y); - if (!hitTarget || !node.contains(hitTarget)) { + if ( + !hitTarget || + !node.contains(hitTarget) || + hitTarget.closest('.nodrag, .react-flow__handle') + ) { continue; } @@ -266,7 +277,7 @@ async function findInteractableNodePair( }); } -async function readNodeScreenPosition(page: Page, nodeId: string) { +async function readNodePosition(page: Page, nodeId: string) { return page.evaluate((expectedNodeId) => { const node = Array.from( document.querySelectorAll('.react-flow__node'), @@ -274,8 +285,8 @@ async function readNodeScreenPosition(page: Page, nodeId: string) { if (!node) { throw new Error(`Workflow node ${expectedNodeId} is unavailable`); } - const bounds = node.getBoundingClientRect(); - return { x: bounds.left, y: bounds.top }; + const transform = new DOMMatrix(getComputedStyle(node).transform); + return { x: transform.e, y: transform.f }; }, nodeId); } @@ -342,7 +353,7 @@ async function runSelection(page: Page, targets: InteractableNodePair) { async function runDrag(page: Page) { const [target] = await findInteractableNodePair(page); - const before = await readNodeScreenPosition(page, target.id); + const before = await readNodePosition(page, target.id); await page.mouse.move(target.point.x, target.point.y); await page.mouse.down(); await page.mouse.move(target.point.x + 48, target.point.y + 32, { steps: 8 }); @@ -355,10 +366,10 @@ async function runDrag(page: Page) { if (!node) { return false; } - const bounds = node.getBoundingClientRect(); + const transform = new DOMMatrix(getComputedStyle(node).transform); return ( - Math.abs(bounds.left - position.x) > tolerance || - Math.abs(bounds.top - position.y) > tolerance + Math.abs(transform.e - position.x) > tolerance || + Math.abs(transform.f - position.y) > tolerance ); }, { @@ -367,7 +378,7 @@ async function runDrag(page: Page) { tolerance: POSITION_CHANGE_TOLERANCE, }, ); - const after = await readNodeScreenPosition(page, target.id); + const after = await readNodePosition(page, target.id); expect( Math.max(Math.abs(after.x - before.x), Math.abs(after.y - before.y)), ).toBeGreaterThan(POSITION_CHANGE_TOLERANCE); @@ -523,9 +534,19 @@ async function runScenarioAction( await waitForAnimationFrames(page); } -function assertSemanticResult(result: WorkflowCanvasBenchmarkResult) { - expect(result.changedNodeReferences).toBe( - EXPECTED_CHANGED_NODE_REFERENCES[result.scenario](result.graph.nodes), +function assertSemanticResult( + result: WorkflowCanvasBenchmarkResult, + measurement: WorkflowCanvasBenchmarkMeasurement, +) { + // ResizeObserver changes measured bounds on compact/full transitions and + // when virtualized nodes enter view. Keep reporting every changed reference, + // while checking the interaction contract independently of measurement-only updates. + expect( + measurement.changedNodeReferencesExcludingMeasurements, + `${result.scenario}: ${result.graph.nodes} nodes, ${JSON.stringify(result.policy)}`, + ).toBe(EXPECTED_CHANGED_NODE_REFERENCES[result.scenario](result.graph.nodes)); + expect(result.changedNodeReferences).toBeGreaterThanOrEqual( + measurement.changedNodeReferencesExcludingMeasurements, ); if (result.scenario === 'initial-load') { expect(result.renderedNodeCount).toBeGreaterThan(0); @@ -574,7 +595,7 @@ async function appendResult( scenario, usedHeapBytes: captured.measurement.usedHeapBytes, }); - assertSemanticResult(result); + assertSemanticResult(result, captured.measurement); const storedResult = await page.evaluate((value) => { const api = window.__AEVATAR_WORKFLOW_CANVAS_BENCHMARK__; if (!api) { diff --git a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.test.ts b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.test.ts index 3bcda5a107..5cc06d4bd5 100644 --- a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.test.ts +++ b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.test.ts @@ -117,6 +117,47 @@ describe('workflow canvas benchmark graph', () => { ); }); + it('separates measurement-only updates while retaining edits, topology and unnecessary clones', () => { + const graph = createWorkflowCanvasBenchmarkGraph(100); + const options = { ignoreMeasurementOnlyChanges: true }; + const measured = graph.nodes.map((node) => ({ + ...node, + measured: { width: 244, height: 61 }, + })); + expect(getChangedNodeReferenceIds(graph.nodes, measured).size).toBe(100); + expect( + getChangedNodeReferenceIds(graph.nodes, measured, options).size, + ).toBe(0); + const edited = measured.map((node, index) => + index === 0 + ? { + ...node, + data: { ...node.data, executionStatus: 'active' as const }, + position: { x: 48, y: 32 }, + } + : node, + ); + expect(getChangedNodeReferenceIds(graph.nodes, edited, options)).toEqual( + new Set([graph.nodes[0].id]), + ); + expect( + getChangedNodeReferenceIds( + measured, + [ + { ...measured[0], measured: { ...measured[0].measured } }, + ...measured.slice(1), + ], + options, + ), + ).toEqual(new Set([graph.nodes[0].id])); + expect( + getChangedNodeReferenceIds(measured, measured.slice(1), options), + ).toEqual(new Set([graph.nodes[0].id])); + expect( + getChangedNodeReferenceIds(measured.slice(1), measured, options), + ).toEqual(new Set([graph.nodes[0].id])); + }); + it('retains larger and disjoint reference deltas across later transitions', () => { const graph = createWorkflowCanvasBenchmarkGraph(100); const changedNodeIds = new Set(); diff --git a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.ts b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.ts index fa71709b32..d039b002ed 100644 --- a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.ts +++ b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/benchmarkGraph.ts @@ -49,6 +49,7 @@ export type WorkflowCanvasBenchmarkReactCommit = { }; export type WorkflowCanvasBenchmarkMeasurement = { + readonly changedNodeReferencesExcludingMeasurements: number; readonly changedNodeReferences: number; readonly longTasks: number; readonly reactCommits?: readonly WorkflowCanvasBenchmarkReactCommit[]; @@ -231,12 +232,39 @@ export function countChangedNodeReferences( export function getChangedNodeReferenceIds( previousNodes: readonly Node[], nextNodes: readonly Node[], + options: { ignoreMeasurementOnlyChanges?: boolean } = {}, ): ReadonlySet { const previousById = new Map(previousNodes.map((node) => [node.id, node])); const nextIds = new Set(nextNodes.map((node) => node.id)); const changedNodeIds = new Set( nextNodes - .filter((node) => previousById.get(node.id) !== node) + .filter((node) => { + const previous = previousById.get(node.id); + if (previous === node) return false; + if ( + !options.ignoreMeasurementOnlyChanges || + !previous || + node.measured?.width == null || + node.measured?.height == null || + (previous.measured?.width === node.measured.width && + previous.measured?.height === node.measured.height) + ) + return true; + // ResizeObserver updates measured bounds when nodes mount or switch + // compact bands. Retain all other reference changes, even empty clones. + const previousValues = new Map( + Object.entries(previous), + ); + const nextValues = new Map(Object.entries(node)); + return [ + ...new Set([...previousValues.keys(), ...nextValues.keys()]), + ].some( + (key) => + key !== 'measured' && + (previousValues.has(key) !== nextValues.has(key) || + !Object.is(previousValues.get(key), nextValues.get(key))), + ); + }) .map((node) => node.id), ); for (const node of previousNodes) { diff --git a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/index.tsx b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/index.tsx index 268bb1f8b2..b775f61194 100644 --- a/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/index.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-canvas-benchmark/index.tsx @@ -44,6 +44,7 @@ declare global { } type ActiveMeasurement = { + readonly changedNodeReferenceIdsExcludingMeasurements: Set; readonly changedNodeReferenceIds: Set; changedNodeReferences: number; readonly reactCommits: WorkflowCanvasBenchmarkReactCommit[]; @@ -168,6 +169,9 @@ function BenchmarkCanvas({ const resultsRef = React.useRef([]); const longTasksRef = React.useRef([]); const measurementRef = React.useRef({ + changedNodeReferenceIdsExcludingMeasurements: new Set( + initialGraph.nodes.map((node) => node.id), + ), changedNodeReferenceIds: new Set(initialGraph.nodes.map((node) => node.id)), changedNodeReferences: size, reactCommits: [], @@ -198,9 +202,19 @@ function BenchmarkCanvas({ const handleRenderedNodesChange = React.useCallback( (renderedNodes: readonly Node[]) => { + const previousRenderedNodes = renderedNodesRef.current; renderedNodesRef.current = renderedNodes; const baselineNodes = scenarioBaselineNodesRef.current; if (baselineNodes) { + for (const nodeId of getChangedNodeReferenceIds( + previousRenderedNodes ?? baselineNodes, + renderedNodes, + { ignoreMeasurementOnlyChanges: true }, + )) { + measurementRef.current.changedNodeReferenceIdsExcludingMeasurements.add( + nodeId, + ); + } for (const nodeId of getChangedNodeReferenceIds( baselineNodes, renderedNodes, @@ -261,6 +275,7 @@ function BenchmarkCanvas({ } scenarioBaselineNodesRef.current = renderedNodesRef.current; measurementRef.current = { + changedNodeReferenceIdsExcludingMeasurements: new Set(), changedNodeReferenceIds: new Set(), changedNodeReferences: 0, reactCommits: [], @@ -279,6 +294,8 @@ function BenchmarkCanvas({ ? [...measurement.reactCommits] : undefined; return { + changedNodeReferencesExcludingMeasurements: + measurement.changedNodeReferenceIdsExcludingMeasurements.size, changedNodeReferences: measurement.changedNodeReferences, longTasks: longTasksRef.current.filter( (sample) => sample.startTime >= measurement.startedAt, From 5dba0b78db656166171c5bd017b02b12902cdf00 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:06:02 +0800 Subject: [PATCH 130/167] Refine channel action buttons and link NyxID details --- .../docs/features/2026-09-14-channel-pages.md | 11 ++++ .../src/locales/channelMessages.en-US.ts | 2 +- .../src/locales/channelMessages.zh-CN.ts | 2 +- .../channels/ChannelDetailsPage.tsx | 54 +++++++++++++++---- .../channels/ChannelsPage.test.tsx | 52 ++++++++++++++++-- .../channels/ChannelsPage.tsx | 2 +- .../channels/styles.ts | 21 +++++--- 7 files changed, 123 insertions(+), 21 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 242eaf4548..a10813c015 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -8,6 +8,17 @@ The three pages follow the latest simplified [Figma design](https://www.figma.co channel details read-only with Remove as the sole resource action. The earlier runtime-configuration editor is outside this iteration of issue #3617. +Refresh and Manage use outlined button styling with visible hover/focus states +and larger touch targets on mobile. Manage retains navigation-link semantics. +Details omit Scope. Bot ID and Agent key ID are blue underlined external links +with an external-link icon, opening the corresponding NyxID record in a new tab. +The website origin is the design's `https://nyx.chrono-ai.fun`, separate from +the configured NyxID API/OIDC authority. Bot links use `/channel-bots/{botId}`; +Agent key links use the verified NyxID `/keys/api-key/{agentKeyId}` route. +The Figma `/keys/xxx` placeholder refers to NyxID service details, so it is not +used for Agent keys. Each complete source ID is encoded as one URL segment; +missing IDs remain unlinked placeholders. + ## Routes and existing setup - `/scopes/:scopeId/workflow-activity-vnext/channels`: platform directory and diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index df4a715155..60eac16cb8 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -56,9 +56,9 @@ export default { 'channels.registration': 'Registration', 'channels.inboundMessages': 'Inbound messages', 'channels.botId': 'Bot ID', - 'channels.scope': 'Scope', 'channels.provider': 'Provider', 'channels.keyId': 'Agent key ID', + 'channels.openInNyxID': 'Open {label} {id} in NyxID (new tab)', 'channels.back': 'Back to channels', 'channels.remove': 'Remove', 'channels.cancel': 'Cancel', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index ad618fc15c..15a601d31d 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -52,9 +52,9 @@ export default { 'channels.registration': '接入记录', 'channels.inboundMessages': '消息入站', 'channels.botId': '机器人 ID', - 'channels.scope': '空间', 'channels.provider': '提供方', 'channels.keyId': 'Agent 密钥 ID', + 'channels.openInNyxID': '在 NyxID 中打开{label} {id}(新标签页)', 'channels.back': '返回渠道', 'channels.remove': '移除', 'channels.cancel': '取消', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index 4021c8fca7..cbac9dfc6e 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -1,4 +1,8 @@ -import { ArrowLeftOutlined, DeleteOutlined } from '@ant-design/icons'; +import { + ArrowLeftOutlined, + DeleteOutlined, + ExportOutlined, +} from '@ant-design/icons'; import { Button, Modal } from 'antd'; import * as React from 'react'; import { channelsApi } from '@/shared/api/channelsApi'; @@ -20,6 +24,9 @@ import { import { useChannelRegistrations, useChannelStatus } from './queries'; import { channelsCss } from './styles'; +// Figma links to the NyxID website, separate from its API/OIDC authority. +const NYXID_WEB_BASE_URL = 'https://nyx.chrono-ai.fun'; + export default function ChannelDetailsPage({ scopeId, registrationId, @@ -200,14 +207,43 @@ export default function ChannelDetailsPage({
{[ - ['botId', 'Bot ID', registration.botId], - ['scope', 'Scope', registration.scopeId], - ['provider', 'Provider', registration.providerSlug], - ['keyId', 'Agent key ID', registration.agentKeyId], - ].map(([key, label, value]) => ( -
-
{t(`channels.${key}`, label ?? '')}
-
{value || '—'}
+ { + label: t('channels.botId', 'Bot ID'), + value: registration.botId, + path: '/channel-bots/', + }, + { + label: t('channels.provider', 'Provider'), + value: registration.providerSlug, + }, + { + label: t('channels.keyId', 'Agent key ID'), + value: registration.agentKeyId, + path: '/keys/api-key/', + }, + ].map(({ label, value, path }) => ( +
+
{label}
+
+ {value && path ? ( + + {value} + + ) : ( + value || '—' + )} +
))} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index aebbd42425..c257f194b4 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -171,13 +171,15 @@ describe('Channel pages', () => { ).toBeInTheDocument(); }); - it('shows only safe detail fields and makes Remove the only resource action', async () => { + it('links exact bot and agent key IDs to NyxID, hides Scope and keeps removal safe', async () => { fetchMock.mockImplementation(async (input) => input === statusPath ? response({ registration_id: registration.id, status: 'active' }) : response([ { ...registration, + nyx_channel_bot_id: 'bot:alpha/one', + nyx_agent_api_key_id: 'key:alpha/two', access_token: 'TEST_ONLY_SECRET', webhook_url: 'https://example.invalid/private', }, @@ -189,7 +191,26 @@ describe('Channel pages', () => { scopeId="scope-alpha" />, ); - expect(await screen.findByText('key-alpha')).toBeInTheDocument(); + const keyLink = await screen.findByRole('link', { + name: 'Open Agent key ID key:alpha/two in NyxID (new tab)', + }); + const botLink = screen.getByRole('link', { + name: 'Open Bot ID bot:alpha/one in NyxID (new tab)', + }); + expect(keyLink).toHaveAttribute( + 'href', + 'https://nyx.chrono-ai.fun/keys/api-key/key%3Aalpha%2Ftwo', + ); + expect(botLink).toHaveAttribute( + 'href', + 'https://nyx.chrono-ai.fun/channel-bots/bot%3Aalpha%2Fone', + ); + for (const link of [botLink, keyLink]) { + expect(link).toHaveAttribute('target', '_blank'); + expect(link).toHaveAttribute('rel', 'noopener noreferrer'); + } + expect(screen.queryByText('Scope')).not.toBeInTheDocument(); + expect(screen.queryByText('scope-alpha')).not.toBeInTheDocument(); expect(screen.getByText('telegram-provider')).toBeInTheDocument(); expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); expect(document.body).not.toHaveTextContent('example.invalid'); @@ -200,13 +221,38 @@ describe('Channel pages', () => { const dialog = await screen.findByRole('dialog', { name: 'Remove this channel?', }); - expect(dialog).toHaveTextContent('bot-alpha'); + expect(dialog).toHaveTextContent('bot:alpha/one'); fireEvent.click(within(dialog).getByRole('button', { name: 'Cancel' })); expect( fetchMock.mock.calls.some(([, init]) => init?.method === 'DELETE'), ).toBe(false); }); + it('keeps missing bot and agent key IDs as unlinked placeholders', async () => { + fetchMock.mockImplementation(async (input) => + input === statusPath + ? response({ registration_id: registration.id, status: 'active' }) + : response([ + { + ...registration, + nyx_channel_bot_id: null, + nyx_agent_api_key_id: null, + }, + ]), + ); + renderWithQueryClient( + , + ); + const details = await screen.findByRole('region', { + name: 'Channel details', + }); + expect(within(details).getAllByText('—')).toHaveLength(2); + expect(within(details).queryByRole('link')).not.toBeInTheDocument(); + }); + it('waits for list readback after removal and Check again never repeats DELETE', async () => { let deletedFromList = false; fetchMock.mockImplementation(async (input, init) => { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 60473a45a6..f7fab24ca6 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -196,7 +196,7 @@ export default function ChannelsPage({

)} - aria-describedby="telegram-token-help" aria-invalid={ validationAttempted && !botToken.trim() && !submittedId } onChange={(event) => setBotToken(event.target.value)} /> -

- {t( - 'channels.connect.nameLookupUnavailable', - 'Automatic bot-name lookup is not available yet. Enter a Label and Skill name below to connect.', - )} -

{validationAttempted && !botToken.trim() && !locked ? (

{t( @@ -332,35 +321,20 @@ export default function TelegramConnectionPage({

field.change(event.target.value)} /> -

- {t( - 'channels.connect.nameHelp', - 'Use the Telegram bot name or enter your own.', - )} -

- {validationAttempted && !field.value.trim() ? ( -

- {t( - 'channels.connect.nameRequired', - 'Enter a name while automatic lookup is unavailable.', - )} -

- ) : null}
))}
@@ -387,11 +361,6 @@ export default function TelegramConnectionPage({ {registrationErrorMessage('configuration')}

) : null} - {error ? ( -

- {registrationErrorMessage(error)} -

- ) : null} {submittedId ? (

@@ -413,7 +382,7 @@ export default function TelegramConnectionPage({ ) : null}

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts index 23d7a48519..45f20d8d90 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts @@ -27,6 +27,8 @@ export const channelConnectionCss = ` .channels__service-picker > .ant-input-affix-wrapper { background: var(--wa-subtle); min-height: 34px; } .channels__service-picker .ant-input { background: transparent; font-size: 12px; } .channels__service-picker .ant-input-prefix { color: var(--wa-muted); margin-right: 8px; } +.channels__service-bulk { align-items: center; border-bottom: 1px solid var(--channels-border); display: flex; min-height: var(--channel-field-height); padding: 8px; } +.channels__service-bulk .ant-checkbox-wrapper { flex: 1; font-size: 12px; font-weight: 500; } .channels__service-options { max-height: 240px; overflow-y: auto; padding-top: 8px; scrollbar-gutter: stable; } .channels__service-option { align-items: center; border-bottom: 1px solid var(--channels-border); display: flex; gap: 12px; min-height: 56px; padding: 8px; } .channels__service-option:last-child { border-bottom: 0; } @@ -49,6 +51,7 @@ export const channelConnectionCss = ` .channels__connection-form { padding: 20px; } .channels__name-fields { gap: 0; grid-template-columns: minmax(0, 1fr); } .channels__service-option { align-items: flex-start; gap: 8px; padding-inline: 2px; } + .channels__service-bulk { padding-inline: 2px; } .channels__service-source { flex-basis: 80px; padding-top: 3px; } .channels__form-actions .ant-btn { padding-inline: 12px; } } diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts index 1c9044346f..f747a8807a 100644 --- a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -7,6 +7,8 @@ jest.mock('@/shared/auth/config', () => ({ getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), })); const fetchMock = jest.mocked(authFetch); +const originalFetch = global.fetch; +const telegramFetch = jest.fn(); const response = (value: unknown, status = 200) => ({ ok: status >= 200 && status < 300, @@ -14,7 +16,14 @@ const response = (value: unknown, status = 200) => json: async () => value, }) as Response; -afterEach(() => fetchMock.mockReset()); +beforeEach(() => { + telegramFetch.mockReset(); + global.fetch = telegramFetch; +}); +afterEach(() => { + fetchMock.mockReset(); + global.fetch = originalFetch; +}); it('maps the documented NyxID credential variants and keeps exact service IDs without retaining credential data', async () => { fetchMock.mockResolvedValue( @@ -121,6 +130,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra }), ).toEqual({ registrationId: 'registration-new' }); expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); + expect(telegramFetch).not.toHaveBeenCalled(); expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toEqual({ platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', @@ -146,3 +156,116 @@ it('preserves an explicit empty allowlist and retains only the accepted registra }), ).rejects.toEqual(new ChannelRegistrationError('uncertain')); }); + +it.each([ + { + label: undefined, + skillName: undefined, + expectedLabel: 'My Bot', + expectedSkill: 'My Bot', + }, + { + label: ' Custom label ', + skillName: ' ', + expectedLabel: 'Custom label', + expectedSkill: 'My Bot', + }, + { + label: ' ', + skillName: ' Custom skill ', + expectedLabel: 'My Bot', + expectedSkill: 'Custom skill', + }, +])('defaults only blank names from Telegram first_name: $expectedLabel / $expectedSkill', async ({ + label, + skillName, + expectedLabel, + expectedSkill, +}) => { + const token = '123456:TEST_ONLY_TOKEN'; + telegramFetch.mockResolvedValue( + response({ + ok: true, + result: { + is_bot: true, + first_name: ' My Bot ', + username: 'different_username', + }, + }), + ); + fetchMock.mockResolvedValue( + response( + { status: 'accepted', registration_id: 'registration-defaults' }, + 202, + ), + ); + await channelsApi.registerTelegram({ + botToken: ` ${token} `, + label, + skillName, + serviceIds: ['user-service-a'], + webhookBaseUrl: 'https://api.example.test', + }); + expect(telegramFetch).toHaveBeenCalledTimes(1); + expect(telegramFetch).toHaveBeenCalledWith( + `https://api.telegram.org/bot${token}/getMe`, + { + method: 'POST', + credentials: 'omit', + cache: 'no-store', + referrerPolicy: 'no-referrer', + redirect: 'error', + signal: expect.any(AbortSignal), + }, + ); + expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toMatchObject({ + label: expectedLabel, + default_skill_name: expectedSkill, + bot_token: token, + service_ids: ['user-service-a'], + }); +}); + +it('does not register using a username when Telegram omits a usable bot name', async () => { + telegramFetch.mockResolvedValue( + response({ + ok: true, + result: { is_bot: true, first_name: ' ', username: 'not_the_bot_name' }, + }), + ); + await expect( + channelsApi.registerTelegram({ + botToken: '123456:TEST_ONLY_TOKEN', + serviceIds: [], + webhookBaseUrl: 'https://api.example.test', + }), + ).rejects.toEqual(new ChannelRegistrationError('botName')); + expect(fetchMock).not.toHaveBeenCalled(); +}); + +it('aborts a stalled name lookup without submitting a registration', async () => { + jest.useFakeTimers(); + try { + telegramFetch.mockImplementation( + (_input: string, init: RequestInit) => + new Promise((_resolve, reject) => { + init.signal?.addEventListener('abort', () => + reject(new DOMException('Lookup aborted', 'AbortError')), + ); + }), + ); + const registration = channelsApi.registerTelegram({ + botToken: '123456:TEST_ONLY_TOKEN', + serviceIds: [], + webhookBaseUrl: 'https://api.example.test', + }); + const rejection = expect(registration).rejects.toEqual( + new ChannelRegistrationError('botName'), + ); + await jest.advanceTimersByTimeAsync(15_000); + await rejection; + expect(fetchMock).not.toHaveBeenCalled(); + } finally { + jest.useRealTimers(); + } +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 520a405389..2151244e0c 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -39,6 +39,7 @@ export class ChannelApiError extends Error { export type ChannelRegistrationFailure = | 'token' + | 'botName' | 'services' | 'skill' | 'authorization' @@ -56,12 +57,54 @@ export class ChannelRegistrationError extends Error { export interface TelegramRegistrationInput { readonly botToken: string; - readonly label: string; - readonly skillName: string; + readonly label?: string; + readonly skillName?: string; readonly serviceIds: readonly string[]; readonly webhookBaseUrl: string; } +async function readTelegramBotName(botToken: string): Promise { + if (!/^[0-9]+:[A-Za-z0-9_-]+$/.test(botToken)) + throw new ChannelRegistrationError('token'); + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 15_000); + try { + // Telegram requires the token in its API path. Use a plain, uncached + // request without Aevatar auth; never retain or log upstream errors/URLs. + const response = await fetch( + `https://api.telegram.org/bot${botToken}/getMe`, + { + method: 'POST', + credentials: 'omit', + cache: 'no-store', + referrerPolicy: 'no-referrer', + redirect: 'error', + signal: controller.signal, + }, + ); + if ([401, 404].includes(response.status)) + throw new ChannelRegistrationError('token'); + if (!response.ok) throw new ChannelRegistrationError('botName'); + const payload = expectRecord(await response.json(), 'Telegram response'); + if (payload.ok !== true) { + throw new ChannelRegistrationError( + [401, 404].includes(Number(payload.error_code)) ? 'token' : 'botName', + ); + } + const bot = expectRecord(payload.result, 'Telegram bot'); + const botName = readString(bot, 'first_name', 'Telegram bot name').trim(); + if (bot.is_bot !== true || !botName) + throw new ChannelRegistrationError('botName'); + return botName; + } catch (error) { + throw error instanceof ChannelRegistrationError + ? error + : new ChannelRegistrationError('botName'); + } finally { + clearTimeout(timeout); + } +} + function registrationFailure(status: number, value: unknown) { const code = value && typeof value === 'object' && 'error' in value @@ -154,6 +197,11 @@ export const channelsApi = { async registerTelegram( input: TelegramRegistrationInput, ): Promise<{ readonly registrationId: string }> { + const botToken = input.botToken.trim(); + const label = input.label?.trim() ?? ''; + const skillName = input.skillName?.trim() ?? ''; + const botName = + !label || !skillName ? await readTelegramBotName(botToken) : ''; // This request carries the token only in the authenticated POST body. // Do not use a mutation cache, navigation state, or persistent draft. let response: Response; @@ -166,9 +214,9 @@ export const channelsApi = { }, body: JSON.stringify({ platform: 'telegram', - bot_token: input.botToken.trim(), - label: input.label.trim(), - default_skill_name: input.skillName.trim(), + bot_token: botToken, + label: label || botName, + default_skill_name: skillName || botName, webhook_base_url: input.webhookBaseUrl, authorization_mode: 'explicit_service_allowlist', service_ids: [...input.serviceIds], From dec8ad4470f2d60a22fb3b7480037d7ac0257e7c Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:22:32 +0800 Subject: [PATCH 132/167] Stop automatic refreshes on Channels pages --- .../docs/features/2026-09-14-channel-pages.md | 20 ++- .../channels/ChannelDetailsPage.tsx | 9 +- .../channels/ChannelsPage.test.tsx | 125 +++++++++++++----- .../channels/TelegramConnectionPage.test.tsx | 65 +++++++++ .../channels/TelegramConnectionPage.tsx | 12 +- .../channels/queries.ts | 14 +- 6 files changed, 184 insertions(+), 61 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 57a94413ea..b764ad420e 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -8,6 +8,9 @@ The three pages follow the latest simplified [Figma design](https://www.figma.co channel details read-only with Remove as the sole resource action. The earlier runtime-configuration editor is outside this iteration of issue #3617. +Channel refresh is explicitly user driven. Do not add background polling or +refresh on focus/reconnection to these pages. + Refresh and Manage use outlined button styling with visible hover/focus states and larger touch targets on mobile. Manage retains navigation-link semantics. Details omit Scope. Bot ID and Agent key ID are blue underlined external links @@ -73,9 +76,11 @@ Registration uses existing `POST /api/channels/registrations` with the explicit selection above. A `202`/`status: accepted` response must contain the registration ID. The page waits for that exact Telegram registration in the owner list before showing success and opening details; inbound activity -continues to use its real status. Observation is bounded to 20 seconds and -Check again performs GET only. Registration failures, including 504 and network -errors, show a shared error toast and restore editable fields and Connect +continues to use its real status. The form does not query registrations before +acceptance or after a failed submission. Accepted registration triggers one +list read; if it is not visible yet, Check again performs one GET. No polling, +window-focus refresh, or reconnect refresh runs. Registration failures, including +504 and network errors, show a shared error toast and restore editable fields and Connect Telegram. Inputs and service selections are preserved for an explicit manual retry; no automatic registration retry runs. An in-flight request still blocks duplicate clicks, and an accepted registration stays locked during observation. @@ -113,7 +118,9 @@ API comment in [issue #3617](https://github.com/aevatarAI/aevatar/issues/3617). `GET /api/channels/registrations/{registrationId}/status` independently. Pending, active, error, and unknown states remain distinct. A status failure does not hide the connection or claim it is active. Visible status queries - refresh every 30 seconds and on window focus. + load on entry and refresh only through the explicit Refresh action. Neither + the list nor status queries poll, refresh on window focus, or refresh on + network reconnection. - Skill uses `default_skill.name` and optional `version` from PR #3620. The deployed name-only `default_skill_name` contract is also supported. An absent name displays Not set; an absent version is omitted. No skill @@ -135,8 +142,9 @@ confirmation available for retry. No real connection is removed during QA. A `status: deleted` response starts list reconciliation. The UI keeps Remove disabled and waits for the registration to disappear from a fresh owner list -before reporting completion and returning to Channels. Automatic observation -is bounded to 20 seconds; Check again repeats only GET and never DELETE. +before reporting completion and returning to Channels. Removal triggers one +list read; if the row is still present, Check again performs one GET and never +repeats DELETE. No background polling runs while waiting. Cleanup warnings produce a safe NyxID follow-up message without displaying raw diagnostic values. diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index cbac9dfc6e..1aefda8d49 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -37,10 +37,9 @@ export default function ChannelDetailsPage({ const [confirmOpen, setConfirmOpen] = React.useState(false); const [removing, setRemoving] = React.useState(false); const [removalAccepted, setRemovalAccepted] = React.useState(false); - const [observeUntil, setObserveUntil] = React.useState(0); const [hasWarnings, setHasWarnings] = React.useState(false); const [removeError, setRemoveError] = React.useState(false); - const registrations = useChannelRegistrations(scopeId, observeUntil); + const registrations = useChannelRegistrations(scopeId); const registration = registrations.data?.find( (row) => row.id === registrationId, ); @@ -86,7 +85,6 @@ export default function ChannelDetailsPage({ const result = await channelsApi.remove(registrationId); setHasWarnings(result.hasWarnings); setRemovalAccepted(true); - setObserveUntil(Date.now() + 20_000); setConfirmOpen(false); await registrations.refetch(); } catch { @@ -269,10 +267,7 @@ export default function ChannelDetailsPage({

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index c257f194b4..6d9f392500 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -1,3 +1,4 @@ +import { focusManager, onlineManager } from '@tanstack/react-query'; import { act, fireEvent, @@ -75,6 +76,40 @@ beforeEach(() => { }); describe('Channel pages', () => { + it('refreshes the list and statuses only when requested after the initial load', async () => { + jest.useFakeTimers(); + try { + const view = renderWithQueryClient( + , + ); + await screen.findByText('Active'); + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ + '/api/channels/registrations', + statusPath, + ]); + await act(async () => { + await jest.advanceTimersByTimeAsync(60_000); + focusManager.setFocused(false); + focusManager.setFocused(true); + onlineManager.setOnline(false); + onlineManager.setOnline(true); + await jest.advanceTimersByTimeAsync(1); + }); + expect(fetchMock).toHaveBeenCalledTimes(2); + fireEvent.click(screen.getByRole('button', { name: 'Refresh channels' })); + await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(4)); + expect(fetchMock.mock.calls.slice(2).map(([input]) => input)).toEqual([ + '/api/channels/registrations', + statusPath, + ]); + view.unmount(); + } finally { + focusManager.setFocused(undefined); + onlineManager.setOnline(true); + jest.useRealTimers(); + } + }); + it('loads the connected table, exact Ornn skill link and first-level navigation without enabling unsupported platforms', async () => { const pendingStatus = deferred(); fetchMock.mockImplementation(async (input) => @@ -254,41 +289,61 @@ describe('Channel pages', () => { }); it('waits for list readback after removal and Check again never repeats DELETE', async () => { - let deletedFromList = false; - fetchMock.mockImplementation(async (input, init) => { - if (input === deletePath && init?.method === 'DELETE') - return response({ status: 'deleted', warnings: [] }); - if (input === statusPath) - return response({ registration_id: registration.id, status: 'active' }); - return response(deletedFromList ? [] : [registration]); - }); - renderWithQueryClient( - , - ); - fireEvent.click(await screen.findByRole('button', { name: 'Remove' })); - fireEvent.click( - within(await screen.findByRole('dialog')).getByRole('button', { - name: 'Remove', - }), - ); - expect(await screen.findByText(/Removal requested/)).toBeInTheDocument(); - expect(mockToast.success).not.toHaveBeenCalled(); - expect(history.replace).not.toHaveBeenCalled(); - expect(screen.getByRole('button', { name: 'Remove' })).toBeDisabled(); - deletedFromList = true; - fireEvent.click(screen.getByRole('button', { name: 'Check again' })); - await waitFor(() => - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels', - ), - ); - expect(mockToast.success).toHaveBeenCalledWith('Channel removed.'); - expect( - fetchMock.mock.calls.filter(([, init]) => init?.method === 'DELETE'), - ).toHaveLength(1); + jest.useFakeTimers(); + try { + let deletedFromList = false; + fetchMock.mockImplementation(async (input, init) => { + if (input === deletePath && init?.method === 'DELETE') + return response({ status: 'deleted', warnings: [] }); + if (input === statusPath) + return response({ + registration_id: registration.id, + status: 'active', + }); + return response(deletedFromList ? [] : [registration]); + }); + renderWithQueryClient( + , + ); + fireEvent.click(await screen.findByRole('button', { name: 'Remove' })); + fireEvent.click( + within(await screen.findByRole('dialog')).getByRole('button', { + name: 'Remove', + }), + ); + expect(await screen.findByText(/Removal requested/)).toBeInTheDocument(); + expect(mockToast.success).not.toHaveBeenCalled(); + expect(history.replace).not.toHaveBeenCalled(); + expect(screen.getByRole('button', { name: 'Remove' })).toBeDisabled(); + const requestsAfterRemoval = fetchMock.mock.calls.length; + await act(async () => { + await jest.advanceTimersByTimeAsync(60_000); + focusManager.setFocused(false); + focusManager.setFocused(true); + onlineManager.setOnline(false); + onlineManager.setOnline(true); + await jest.advanceTimersByTimeAsync(1); + }); + expect(fetchMock).toHaveBeenCalledTimes(requestsAfterRemoval); + deletedFromList = true; + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + await waitFor(() => + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ), + ); + expect(mockToast.success).toHaveBeenCalledWith('Channel removed.'); + expect( + fetchMock.mock.calls.filter(([, init]) => init?.method === 'DELETE'), + ).toHaveLength(1); + } finally { + focusManager.setFocused(undefined); + onlineManager.setOnline(true); + jest.useRealTimers(); + } }); it('keeps failed removal retryable and reports cleanup warnings without exposing their content', async () => { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index 198167f75f..9da52f0b83 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -1,3 +1,4 @@ +import { focusManager, onlineManager } from '@tanstack/react-query'; import { act, fireEvent, @@ -65,6 +66,70 @@ afterEach(() => { global.fetch = originalFetch; }); +it('reads registrations only after acceptance or Check again, never while idle or after failure', async () => { + jest.useFakeTimers(); + try { + let attempts = 0; + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') { + attempts += 1; + return attempts === 1 + ? response({}, 504) + : response( + { status: 'accepted', registration_id: 'registration-new' }, + 202, + ); + } + return input === servicePath ? response({ services: [] }) : response([]); + }); + const listReads = () => + fetchMock.mock.calls.filter( + ([input, init]) => input === listPath && init?.method !== 'POST', + ).length; + const idleAndReturn = async () => { + await act(async () => { + await jest.advanceTimersByTimeAsync(60_000); + focusManager.setFocused(false); + focusManager.setFocused(true); + onlineManager.setOnline(false); + onlineManager.setOnline(true); + await jest.advanceTimersByTimeAsync(1); + }); + }; + const view = renderWithQueryClient( + , + ); + await screen.findByText(/No services are available/); + await idleAndReturn(); + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + await screen.findByText( + 'Could not confirm the connection. Please try again.', + ); + await idleAndReturn(); + expect(listReads()).toBe(0); + expect(attempts).toBe(1); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + await screen.findByRole('button', { name: 'Check again' }); + expect(listReads()).toBe(1); + await idleAndReturn(); + expect(listReads()).toBe(1); + expect(history.replace).not.toHaveBeenCalled(); + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + await waitFor(() => expect(listReads()).toBe(2)); + expect(attempts).toBe(2); + view.unmount(); + } finally { + focusManager.setFocused(undefined); + onlineManager.setOnline(true); + await act(async () => { + await jest.runOnlyPendingTimersAsync(); + }); + jest.useRealTimers(); + } +}); + it('searches services, enforces permissions, submits selected IDs once and waits for the new registration', async () => { let resolvePost!: (value: Response) => void; const post = new Promise((resolve) => { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index 7919194a3a..33b1483f52 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -83,7 +83,6 @@ export default function TelegramConnectionPage({ const [selectedIds, setSelectedIds] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); const [submittedId, setSubmittedId] = React.useState(null); - const [observeUntil, setObserveUntil] = React.useState(0); const [validationAttempted, setValidationAttempted] = React.useState(false); const [leaveTarget, setLeaveTarget] = React.useState(null); const inFlight = React.useRef(false); @@ -92,13 +91,16 @@ export default function TelegramConnectionPage({ const toast = useConsoleToast(); const webhookBaseUrl = getChannelWebhookBaseUrl(); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); - const registrations = useChannelRegistrations(scopeId, observeUntil); + // Read the list only to confirm an accepted submission or on Check again. + const registrations = useChannelRegistrations(scopeId, false); const services = useQuery({ queryKey: ['channels', scopeId, 'service-choices'], queryFn: ({ signal }) => listChannelServices(signal), enabled: Boolean(scopeId), retry: false, staleTime: 0, + refetchOnWindowFocus: false, + refetchOnReconnect: false, }); const invalidSelection = selectedIds.some( (id) => @@ -183,7 +185,6 @@ export default function TelegramConnectionPage({ if (!mounted.current) return; setBotToken(''); setSubmittedId(receipt.registrationId); - setObserveUntil(Date.now() + 20_000); await registrations.refetch(); } catch (failure) { if (!mounted.current) return; @@ -371,10 +372,7 @@ export default function TelegramConnectionPage({

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts index d2edf91f9b..e69447814f 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts @@ -7,15 +7,16 @@ export const channelKeys = { ['channels', scopeId, 'status', registrationId] as const, }; -export function useChannelRegistrations(scopeId: string, observeUntil = 0) { +export function useChannelRegistrations(scopeId: string, enabled = true) { return useQuery({ queryKey: channelKeys.list(scopeId), queryFn: ({ signal }) => channelsApi.list(signal), - enabled: Boolean(scopeId), + enabled: Boolean(scopeId) && enabled, retry: false, staleTime: 0, - refetchOnWindowFocus: true, - refetchInterval: () => (Date.now() < observeUntil ? 2000 : false), + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchInterval: false, }); } @@ -26,7 +27,8 @@ export function useChannelStatus(scopeId: string, registrationId: string) { enabled: Boolean(scopeId && registrationId), retry: false, staleTime: 15_000, - refetchOnWindowFocus: true, - refetchInterval: 30_000, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchInterval: false, }); } From 30818c74e74415eb77d12ad33dc78b24566475ff Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:20:20 +0800 Subject: [PATCH 133/167] Resolve console home from the authenticated scope --- apps/aevatar-console-web/config/routes.ts | 2 +- .../features/2026-09-14-console-home-scope.md | 19 ++++ apps/aevatar-console-web/src/locales/en-US.ts | 2 + apps/aevatar-console-web/src/locales/zh-CN.ts | 2 + .../src/pages/scopes/index.test.tsx | 93 +++++++++++++++++++ .../src/pages/scopes/index.tsx | 49 ++++++++++ .../src/routesConfig.test.ts | 5 +- .../src/shared/navigation/consoleHome.test.ts | 5 +- .../src/shared/navigation/consoleHome.ts | 3 +- 9 files changed, 172 insertions(+), 8 deletions(-) create mode 100644 apps/aevatar-console-web/docs/features/2026-09-14-console-home-scope.md create mode 100644 apps/aevatar-console-web/src/pages/scopes/index.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/scopes/index.tsx diff --git a/apps/aevatar-console-web/config/routes.ts b/apps/aevatar-console-web/config/routes.ts index 15d5b3cab8..dcd5ced701 100644 --- a/apps/aevatar-console-web/config/routes.ts +++ b/apps/aevatar-console-web/config/routes.ts @@ -37,7 +37,7 @@ export default [ }, { path: '/scopes', - redirect: CONSOLE_HOME_ROUTE, + component: './scopes', hideInMenu: true, }, { diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-console-home-scope.md b/apps/aevatar-console-web/docs/features/2026-09-14-console-home-scope.md new file mode 100644 index 0000000000..3744f14bbe --- /dev/null +++ b/apps/aevatar-console-web/docs/features/2026-09-14-console-home-scope.md @@ -0,0 +1,19 @@ +# Console home scope resolution + +The default home, login fallback, overview redirect and 404 return action use +`/scopes`. This is a technical entry that reads the existing authenticated +`GET /api/auth/me` contract and opens +`/scopes/{scopeId}/workflow-activity-vnext/workflows` using the returned scope. + +Never embed a personal scope in the default route or infer it from user +`subject`, browser storage, or a previously cached account. A fresh successful +session response with `authenticated: true` and a nonempty `scopeId` is required +before navigation. Loading does not open a workspace; failed or incomplete +authentication offers manual retry without a fallback scope. No background +polling, focus refresh, or reconnect refresh is introduced. + +Existing explicit deep links and server authorization remain authoritative for +resource access. This change fixes the default destination; a URL or scope ID +is not an authorization grant. Tests cover different accounts, cached previous +identity, missing/expired authentication and manual recovery. Full frontend +validation is delegated to GitHub CI. diff --git a/apps/aevatar-console-web/src/locales/en-US.ts b/apps/aevatar-console-web/src/locales/en-US.ts index f0151f4f64..2d31a2fc84 100644 --- a/apps/aevatar-console-web/src/locales/en-US.ts +++ b/apps/aevatar-console-web/src/locales/en-US.ts @@ -7,6 +7,8 @@ const enUSMessages = { ...channelMessages, ...workflowActivityVNextMessages, 'common.appName': 'Aevatar', + 'console.home.unavailable': 'Could not open your workspace', + 'console.home.retry': 'Try again', 'common.language.english': 'English', 'common.language.label': 'Language', 'common.language.switch': 'Switch language', diff --git a/apps/aevatar-console-web/src/locales/zh-CN.ts b/apps/aevatar-console-web/src/locales/zh-CN.ts index 0a38c32c72..db6618c521 100644 --- a/apps/aevatar-console-web/src/locales/zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/zh-CN.ts @@ -7,6 +7,8 @@ const zhCNMessages = { ...channelMessages, ...workflowActivityVNextMessages, 'common.appName': 'Aevatar', + 'console.home.unavailable': '暂时无法打开你的工作区', + 'console.home.retry': '重试', 'common.language.english': 'English', 'common.language.label': '语言', 'common.language.switch': '切换语言', diff --git a/apps/aevatar-console-web/src/pages/scopes/index.test.tsx b/apps/aevatar-console-web/src/pages/scopes/index.test.tsx new file mode 100644 index 0000000000..3ee54f20ce --- /dev/null +++ b/apps/aevatar-console-web/src/pages/scopes/index.test.tsx @@ -0,0 +1,93 @@ +import { act, fireEvent, screen, waitFor } from '@testing-library/react'; +import React from 'react'; +import { history } from '@/shared/navigation/history'; +import { studioApi } from '@/shared/studio/api'; +import { + createTestQueryClient, + renderWithQueryClient, +} from '../../../tests/reactQueryTestUtils'; +import ConsoleScopeEntry from './index'; + +jest.mock('@/shared/studio/api', () => ({ + studioApi: { getAuthSession: jest.fn() }, +})); +const getSession = jest.mocked(studioApi.getAuthSession); + +beforeEach(() => { + getSession.mockReset(); + jest.spyOn(history, 'replace').mockImplementation(() => {}); +}); + +it.each([ + 'scope-alice', + 'scope-bob', +])('opens the server-confirmed workspace %s', async (scopeId) => { + getSession.mockResolvedValue({ + enabled: true, + authenticated: true, + subject: 'user-identity-is-not-scope', + scopeId, + }); + renderWithQueryClient(React.createElement(ConsoleScopeEntry)); + await waitFor(() => + expect(history.replace).toHaveBeenCalledWith( + `/scopes/${scopeId}/workflow-activity-vnext/workflows`, + ), + ); +}); + +it('waits for current authentication instead of routing from an earlier account cache', async () => { + const client = createTestQueryClient(); + client.setQueryData(['scopes', 'auth-session'], { + authenticated: true, + scopeId: 'scope-previous-account', + }); + const currentSession = { + enabled: true, + authenticated: true, + subject: 'user-current', + scopeId: 'scope-current', + }; + let resolve!: (session: typeof currentSession) => void; + getSession.mockReturnValue( + new Promise((done) => { + resolve = done; + }), + ); + renderWithQueryClient(React.createElement(ConsoleScopeEntry), client); + expect(history.replace).not.toHaveBeenCalled(); + await act(async () => resolve(currentSession)); + await waitFor(() => expect(history.replace).toHaveBeenCalledTimes(1)); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-current/workflow-activity-vnext/workflows', + ); +}); + +it.each([ + { authenticated: true, subject: 'user-without-scope', scopeId: ' ' }, + { authenticated: false, subject: 'user-expired', scopeId: 'scope-expired' }, +])('does not invent a destination for $subject', async (session) => { + getSession.mockResolvedValue({ enabled: true, ...session }); + renderWithQueryClient(React.createElement(ConsoleScopeEntry)); + await screen.findByText('Could not open your workspace'); + expect(history.replace).not.toHaveBeenCalled(); +}); + +it('fails closed on session lookup failure and resolves the current scope on manual retry', async () => { + getSession.mockRejectedValueOnce(new Error('TEST_ONLY_PRIVATE_DIAGNOSTIC')); + renderWithQueryClient(React.createElement(ConsoleScopeEntry)); + await screen.findByText('Could not open your workspace'); + expect(history.replace).not.toHaveBeenCalled(); + expect(document.body).not.toHaveTextContent('TEST_ONLY_PRIVATE_DIAGNOSTIC'); + getSession.mockResolvedValue({ + enabled: true, + authenticated: true, + scopeId: 'scope-retried', + }); + fireEvent.click(screen.getByRole('button', { name: 'Try again' })); + await waitFor(() => + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-retried/workflow-activity-vnext/workflows', + ), + ); +}); diff --git a/apps/aevatar-console-web/src/pages/scopes/index.tsx b/apps/aevatar-console-web/src/pages/scopes/index.tsx new file mode 100644 index 0000000000..f5ba9b3ccb --- /dev/null +++ b/apps/aevatar-console-web/src/pages/scopes/index.tsx @@ -0,0 +1,49 @@ +import { useQuery } from '@tanstack/react-query'; +import { Button, Result } from 'antd'; +import React from 'react'; +import { t } from '@/shared/i18n/messages'; +import { history } from '@/shared/navigation/history'; +import { resolveStudioScopeContext } from '@/shared/scope/context'; +import { studioApi } from '@/shared/studio/api'; +import { AevatarPageLoading } from '@/shared/ui/AevatarLoading'; +import { buildWorkflowActivitySectionHref } from '../workflow-activity-vnext/navigation'; + +export default function ConsoleScopeEntry() { + const session = useQuery({ + queryKey: ['scopes', 'auth-session'], + queryFn: () => studioApi.getAuthSession(), + staleTime: 0, + refetchOnMount: 'always', + refetchOnWindowFocus: false, + refetchOnReconnect: false, + retry: false, + }); + // A cached previous account must not determine the destination. Wait for + // this entry's server confirmation; subject/local storage are not scope IDs. + const scopeId = + session.isSuccess && !session.isFetching && session.data.authenticated + ? resolveStudioScopeContext(session.data)?.scopeId + : undefined; + + React.useEffect(() => { + if (scopeId) { + history.replace(buildWorkflowActivitySectionHref(scopeId, 'workflows')); + } + }, [scopeId]); + + if (session.isPending || session.isFetching || scopeId) { + return ; + } + + return ( + void session.refetch()}> + {t('console.home.retry', 'Try again')} + + } + /> + ); +} diff --git a/apps/aevatar-console-web/src/routesConfig.test.ts b/apps/aevatar-console-web/src/routesConfig.test.ts index 62831c1c7d..2d4f77a385 100644 --- a/apps/aevatar-console-web/src/routesConfig.test.ts +++ b/apps/aevatar-console-web/src/routesConfig.test.ts @@ -58,8 +58,9 @@ describe('console routes', () => { expect(findRoute(routes, '/runtime/runs').menuGroupKey).toBe('platform'); expect(findRoute(routes, '/scopes/overview').hideInMenu).toBe(true); expect(findRoute(routes, '/scopes').name).toBeUndefined(); - expect(findRoute(routes, '/scopes').component).toBeUndefined(); - for (const path of ['/', '/overview', '/scopes']) { + expect(findRoute(routes, '/scopes').component).toBe('./scopes'); + expect(findRoute(routes, '/scopes').redirect).toBeUndefined(); + for (const path of ['/', '/overview']) { expect(findRoute(routes, path).redirect).toBe(CONSOLE_HOME_ROUTE); } expect(findRouteIndex(routes, '/chat')).toBeLessThan( diff --git a/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts b/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts index c28de6801b..00278397cd 100644 --- a/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts +++ b/apps/aevatar-console-web/src/shared/navigation/consoleHome.test.ts @@ -11,10 +11,9 @@ describe('consoleHome', () => { jest.resetModules(); }); - it('uses the fixed-scope Workflow Activity catalogue as console home', () => { + it('uses the session scope resolver as console home', () => { const module = loadModule(); - const expectedRoute = - '/scopes/ccb108c4-dcb3-473a-a0f7-e9859bb2f2a0/workflow-activity-vnext/workflows'; + const expectedRoute = '/scopes'; expect(module.WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE).toBe(expectedRoute); expect(module.getConsoleHomeRoute()).toBe(expectedRoute); diff --git a/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts b/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts index 06c5247065..f62437992b 100644 --- a/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts +++ b/apps/aevatar-console-web/src/shared/navigation/consoleHome.ts @@ -1,5 +1,4 @@ -export const WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE = - '/scopes/ccb108c4-dcb3-473a-a0f7-e9859bb2f2a0/workflow-activity-vnext/workflows'; +export const WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE = '/scopes'; export function getConsoleHomeRoute(): string { return WORKFLOW_ACTIVITY_VNEXT_HOME_ROUTE; From e263334482a10567dc1b919be768201afee89e04 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:50:26 +0800 Subject: [PATCH 134/167] fix: Filter channel services by current NyxID grants --- .../docs/features/2026-09-14-channel-pages.md | 45 ++++-- .../src/locales/channelMessages.en-US.ts | 4 +- .../src/locales/channelMessages.zh-CN.ts | 5 +- .../channels/ChannelServicePicker.tsx | 4 +- .../channels/TelegramConnectionPage.test.tsx | 150 +++++++++++++++--- .../channels/TelegramConnectionPage.tsx | 11 ++ .../shared/api/channelConnectionApi.test.ts | 85 ---------- .../src/shared/api/channelServicesApi.test.ts | 144 +++++++++++++++++ .../src/shared/api/channelServicesApi.ts | 57 +++++-- 9 files changed, 368 insertions(+), 137 deletions(-) create mode 100644 apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index b764ad420e..ccc9c87a09 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -50,13 +50,35 @@ and the authenticated registration POST. It stays in component memory for a manual retry after failure and clears after successful admission. No registration mutation cache stores it. -Services come from the current NyxID session's -`GET /api/v1/user-services`, resolved against the existing NyxID authority. -Its `{services: [...]}` response differs from the Aevatar runtime-config -inventory: it uses `is_active` and `credential_source.type` (`personal` or -`org`, with an `allowed` flag on the organization variant). Unknown variants, -inactive services, and disallowed organization services cannot be selected. -Only safe display fields enter the query cache. +Services use two reads against the existing NyxID authority through the current +session's authenticated fetch: + +- `GET /api/v1/user-services` supplies account-owned/inherited records and safe + display fields. It does **not** filter the current OAuth bearer's grants. + Personal ownership alone therefore does not make a service selectable. +- `GET /api/v1/mcp/config` (`contract_version: "1.0"`) supplies the current + caller's authorized, executable services. Its documented contract applies + service/node grants and credential/route availability before publication. + Only `is_user_service: true` rows carry exact `UserService.id` values; + platform catalog IDs must never enter a bot's service allowlist. + +The picker contains only the exact-ID intersection of the two responses, +further excluding inactive inventory rows, disallowed organization rows and +unknown credential-source variants. Services outside the current authorization +are hidden, including from search and Select all. The catalog can omit a +service whose credential or node route is unavailable even when the account +owns it. Neither endpoint failing, an unsupported catalog version nor malformed +or ambiguous identities can fall back to showing the account inventory. Only +safe display fields from the filtered result enter the query cache; raw +catalog operation descriptors and credential data are discarded. Both reads +omit browser cookies and bypass HTTP caching so NyxID evaluates the existing +bearer session. They run on entry and explicit recovery, without polling, +window-focus or reconnect refresh. + +This contract is documented in NyxID `docs/API.md` (MCP Configuration) and +`docs/ARCHITECTURE.md`, with implementation in `handlers/mcp.rs` and +`services/mcp_service.rs`. Backend registration authority remains enforced by +NyxID/Aevatar; this picker does not mint credentials or broaden OAuth grants. Options use label, then catalog service name, then slug. Search matches label or slug and preserves selections outside the current search. A Select all @@ -68,8 +90,11 @@ Each selection uses the exact `services[].id`; no default selection is made. The registration POST always supplies `authorization_mode: explicit_service_allowlist` and `service_ids`, including `[]` for no service access. It never substitutes a slug, endpoint ID, API key ID, or omitted field. If a selected service becomes -unavailable, submission is blocked until it is deselected. Load failure has -inline retry, while a genuine empty inventory can connect without service access. +unavailable after an explicit recovery read, it is removed from the selection. +Submission waits while access is being rechecked, preventing stale hidden IDs +from entering a retry. Load failure has inline retry; an empty authorized list +can connect with an explicit empty service allowlist even if the account owns +other services. Registration uses existing `POST /api/channels/registrations` with `platform`, `bot_token`, `label`, `default_skill_name`, `webhook_base_url`, and @@ -81,7 +106,7 @@ acceptance or after a failed submission. Accepted registration triggers one list read; if it is not visible yet, Check again performs one GET. No polling, window-focus refresh, or reconnect refresh runs. Registration failures, including 504 and network errors, show a shared error toast and restore editable fields and Connect -Telegram. Inputs and service selections are preserved for an explicit manual +Telegram. Inputs and still-authorized selections are preserved for an explicit manual retry; no automatic registration retry runs. An in-flight request still blocks duplicate clicks, and an accepted registration stays locked during observation. diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 82059442ee..3ee8979b9b 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -91,12 +91,12 @@ export default { 'channels.connect.selectAll': 'Select all', 'channels.connect.selectAllResults': 'Select all results', 'channels.connect.servicesHelp': - 'Select the services this bot can use from your NyxID account.', + 'Only services available through your current NyxID authorization are shown.', 'channels.connect.servicesLoading': 'Loading services', 'channels.connect.servicesError': 'Could not load your services. Retry before connecting.', 'channels.connect.servicesEmpty': - 'No services are available in your NyxID account. You can connect this bot without service access.', + 'No services are available with your current NyxID authorization. You can connect this bot without service access.', 'channels.connect.searchServices': 'Search services by name or slug', 'channels.connect.serviceUnavailable': 'Unavailable', 'channels.connect.personal': 'Personal', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 31cd3c9d17..b44b48311d 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -84,12 +84,11 @@ export default { 'channels.connect.selected': '已选 {count} 项', 'channels.connect.selectAll': '全选', 'channels.connect.selectAllResults': '全选搜索结果', - 'channels.connect.servicesHelp': - '从你的 NyxID 账户中选择此机器人可以使用的服务。', + 'channels.connect.servicesHelp': '仅显示当前 NyxID 授权范围内可用的服务。', 'channels.connect.servicesLoading': '正在加载服务', 'channels.connect.servicesError': '无法加载服务,请重试后再连接。', 'channels.connect.servicesEmpty': - '你的 NyxID 账户暂无可用服务。可以连接机器人,但不授予服务访问权限。', + '当前 NyxID 授权范围内暂无可用服务。可以连接机器人,但不授予服务访问权限。', 'channels.connect.searchServices': '按名称或标识搜索服务', 'channels.connect.serviceUnavailable': '不可用', 'channels.connect.personal': '个人', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx index 97f91846cb..6536d8580b 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx @@ -55,7 +55,7 @@ export default function ChannelServicePicker({

{t( 'channels.connect.servicesHelp', - 'Select the services this bot can use from your NyxID account.', + 'Only services available through your current NyxID authorization are shown.', )}

{loading ? ( @@ -80,7 +80,7 @@ export default function ChannelServicePicker({
{t( 'channels.connect.servicesEmpty', - 'No services are available in your NyxID account. You can connect this bot without service access.', + 'No services are available with your current NyxID authorization. You can connect this bot without service access.', )}
) : ( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index 9da52f0b83..c8f0265904 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -40,6 +40,15 @@ const service = { credential_source: { type: 'personal' }, }; const servicePath = 'https://nyx.example.test/api/v1/user-services'; +const catalogPath = 'https://nyx.example.test/api/v1/mcp/config'; +const catalog = { + contract_version: '1.0', + services: [ + { service_id: 'user-service-github', is_user_service: true }, + { service_id: 'user-service-personal', is_user_service: true }, + { service_id: 'user-service-slack', is_user_service: true }, + ], +}; const listPath = '/api/channels/registrations'; function enterNames() { fireEvent.change(screen.getByLabelText(/^Bot token/), { @@ -59,7 +68,11 @@ beforeEach(() => { jest.spyOn(history, 'push').mockImplementation(() => {}); jest.spyOn(history, 'replace').mockImplementation(() => {}); fetchMock.mockImplementation(async (input) => - input === servicePath ? response({ services: [service] }) : response([]), + input === catalogPath + ? response(catalog) + : input === servicePath + ? response({ services: [service] }) + : response([]), ); }); afterEach(() => { @@ -71,6 +84,7 @@ it('reads registrations only after acceptance or Check again, never while idle o try { let attempts = 0; fetchMock.mockImplementation(async (input, init) => { + if (input === catalogPath) return response(catalog); if (init?.method === 'POST') { attempts += 1; return attempts === 1 @@ -101,7 +115,10 @@ it('reads registrations only after acceptance or Check again, never while idle o ); await screen.findByText(/No services are available/); await idleAndReturn(); - expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ + servicePath, + catalogPath, + ]); enterNames(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( @@ -137,11 +154,17 @@ it('searches services, enforces permissions, submits selected IDs once and waits }); let observed = false; fetchMock.mockImplementation(async (input, init) => { + if (input === catalogPath) return response(catalog); if (init?.method === 'POST') return post; if (input === servicePath) return response({ services: [ service, + { + ...service, + id: 'user-service-ungranted', + label: 'GitHub not authorized', + }, { ...service, id: 'user-service-org', @@ -180,8 +203,15 @@ it('searches services, enforces permissions, submits selected IDs once and waits expect( await screen.findByRole('checkbox', { name: /GitHub work/ }), ).not.toBeChecked(); - expect(screen.getByRole('checkbox', { name: /Team Slack/ })).toBeDisabled(); - expect(screen.getByRole('checkbox', { name: /Drive/ })).toBeDisabled(); + expect( + screen.queryByRole('checkbox', { name: /Team Slack/ }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('checkbox', { name: /Drive/ }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('checkbox', { name: /GitHub not authorized/ }), + ).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('checkbox', { name: 'Select all' })); fireEvent.change( screen.getByRole('textbox', { name: 'Search services by name or slug' }), @@ -257,21 +287,32 @@ it('searches services, enforces permissions, submits selected IDs once and waits }); it('selects and clears available results while preserving selections outside the search', async () => { - fetchMock.mockResolvedValue( - response({ - services: [ - service, - { ...service, id: 'user-service-personal', label: 'GitHub personal' }, - { ...service, id: 'user-service-slack', label: 'Slack', slug: 'slack' }, - { ...service, id: 'inactive', label: 'Inactive', is_active: false }, - { - ...service, - id: 'denied', - label: 'Denied', - credential_source: { type: 'org', allowed: false }, - }, - ], - }), + fetchMock.mockImplementation(async (input) => + input === catalogPath + ? response(catalog) + : response({ + services: [ + service, + { + ...service, + id: 'user-service-personal', + label: 'GitHub personal', + }, + { + ...service, + id: 'user-service-slack', + label: 'Slack', + slug: 'slack', + }, + { ...service, id: 'inactive', label: 'Inactive', is_active: false }, + { + ...service, + id: 'denied', + label: 'Denied', + credential_source: { type: 'org', allowed: false }, + }, + ], + }), ); renderWithQueryClient(); const all = await screen.findByRole('checkbox', { name: 'Select all' }); @@ -282,8 +323,7 @@ it('selects and clears available results while preserving selections outside the expect(all).toBeChecked(); expect(screen.getByText('3 selected')).toBeInTheDocument(); for (const name of [/Inactive/, /Denied/]) { - expect(screen.getByRole('checkbox', { name })).toBeDisabled(); - expect(screen.getByRole('checkbox', { name })).not.toBeChecked(); + expect(screen.queryByRole('checkbox', { name })).not.toBeInTheDocument(); } fireEvent.click(all); expect(screen.getByText('0 selected')).toBeInTheDocument(); @@ -309,8 +349,8 @@ it('selects and clears available results while preserving selections outside the ).toBePartiallyChecked(); }); -it('recovers service loading failure, requires only a token and defaults optional names with no service access', async () => { - let serviceReads = 0; +it('recovers caller-catalog failure without exposing account services and defaults optional names with no access', async () => { + let catalogReads = 0; telegramFetch.mockResolvedValue( response({ ok: true, @@ -322,10 +362,11 @@ it('recovers service loading failure, requires only a token and defaults optiona }), ); fetchMock.mockImplementation(async (input, init) => { - if (input === servicePath) - return ++serviceReads === 1 + if (input === catalogPath) + return ++catalogReads === 1 ? response({}, 503) - : response({ services: [] }); + : response({ contract_version: '1.0', services: [] }); + if (input === servicePath) return response({ services: [service] }); if (init?.method === 'POST') return response( { error: 'missing_bot_token', note: 'TEST_ONLY_SECRET' }, @@ -389,6 +430,7 @@ it('shows a name-lookup toast and retries with the current token and custom fiel new Error(`Failed https://api.telegram.org/bot${telegramToken}/getMe`), ); fetchMock.mockImplementation(async (input, init) => { + if (input === catalogPath) return response(catalog); if (init?.method === 'POST') return response( { status: 'accepted', registration_id: 'registration-lookup' }, @@ -442,6 +484,7 @@ it.each([ ])('shows a toast for %s, preserves inputs and allows a successful manual retry', async (failure) => { let attempts = 0; fetchMock.mockImplementation(async (input, init) => { + if (input === catalogPath) return response(catalog); if (init?.method === 'POST') { attempts += 1; if (attempts === 1) { @@ -502,6 +545,61 @@ it.each([ ).toBeInTheDocument(); }); +it('removes revoked selections after a rejected submission and allows a retry with the remaining grants', async () => { + let attempted = false; + let resolveCatalog!: (value: Response) => void; + const updatedCatalog = new Promise((resolve) => { + resolveCatalog = resolve; + }); + fetchMock.mockImplementation(async (input, init) => { + if (input === servicePath) return response({ services: [service] }); + if (input === catalogPath) + return attempted ? updatedCatalog : response(catalog); + if (init?.method === 'POST') { + attempted = true; + return response({ error: 'invalid_service_ids' }, 400); + } + return response([]); + }); + renderWithQueryClient(); + fireEvent.click(await screen.findByRole('checkbox', { name: /GitHub work/ })); + enterNames(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + await screen.findByText( + 'The selected services are no longer available. Review your selection and try again.', + ); + expect( + screen.getByRole('button', { name: 'Connect Telegram' }), + ).toBeDisabled(); + // An old selection cannot be submitted while its replacement is pending. + fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); + expect( + fetchMock.mock.calls.filter(([, init]) => init?.method === 'POST'), + ).toHaveLength(1); + await act(async () => + resolveCatalog(response({ contract_version: '1.0', services: [] })), + ); + await screen.findByText(/No services are available/); + expect( + screen.queryByRole('checkbox', { name: /GitHub work/ }), + ).not.toBeInTheDocument(); + expect(screen.getByText('0 selected')).toBeInTheDocument(); + expect(screen.getByLabelText(/^Bot token/)).toHaveValue('TEST_ONLY_TOKEN'); + expect( + screen.getByRole('button', { name: 'Connect Telegram' }), + ).toBeEnabled(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + await waitFor(() => + expect( + fetchMock.mock.calls.filter(([, init]) => init?.method === 'POST'), + ).toHaveLength(2), + ); + const posts = fetchMock.mock.calls.filter( + ([, init]) => init?.method === 'POST', + ); + expect(JSON.parse(String(posts[1][1]?.body)).service_ids).toEqual([]); +}); + it('confirms discarding an edited setup without issuing a registration request', async () => { renderWithQueryClient(); await screen.findByRole('checkbox', { name: /GitHub work/ }); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index 33b1483f52..c67e81f7cb 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -111,6 +111,15 @@ export default function TelegramConnectionPage({ const locked = submitting || submittedId !== null; const dirty = Boolean(botToken || label || skillName || selectedIds.length); + React.useEffect(() => { + if (!services.isSuccess || services.isFetching) return; + const availableIds = new Set(services.data.map((service) => service.id)); + setSelectedIds((current) => { + const available = current.filter((id) => availableIds.has(id)); + return available.length === current.length ? current : available; + }); + }, [services.data, services.isSuccess, services.isFetching]); + React.useEffect(() => { mounted.current = true; return () => { @@ -169,6 +178,7 @@ export default function TelegramConnectionPage({ !botToken.trim() || !webhookBaseUrl || !services.isSuccess || + services.isFetching || invalidSelection ) return; @@ -392,6 +402,7 @@ export default function TelegramConnectionPage({ locked || !webhookBaseUrl || !services.isSuccess || + services.isFetching || invalidSelection } > diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts index f747a8807a..5f3c17262b 100644 --- a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -1,5 +1,4 @@ import { authFetch } from '@/shared/auth/fetch'; -import { listChannelServices } from './channelServicesApi'; import { ChannelRegistrationError, channelsApi } from './channelsApi'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); @@ -25,90 +24,6 @@ afterEach(() => { global.fetch = originalFetch; }); -it('maps the documented NyxID credential variants and keeps exact service IDs without retaining credential data', async () => { - fetchMock.mockResolvedValue( - response({ - services: [ - { - id: 'user-service-a', - slug: 'api-github', - label: 'GitHub work', - is_active: true, - credential_source: { type: 'personal' }, - default_request_headers: [{ value: 'TEST_ONLY_SECRET' }], - }, - { - id: 'user-service-b', - slug: 'drive', - catalog_service_name: 'Google Drive', - is_active: true, - credential_source: { - type: 'org', - org_name: 'Team', - role: 'viewer', - allowed: false, - }, - }, - { - id: 'user-service-c', - slug: 'calendar', - is_active: false, - credential_source: { type: 'org', org_name: 'Team', allowed: true }, - }, - { - id: 'user-service-d', - slug: 'future', - is_active: true, - credential_source: { type: 'new_source', allowed: true }, - }, - ], - }), - ); - const result = await listChannelServices(); - expect(fetchMock.mock.calls[0][0]).toBe( - 'https://nyx.example.test/api/v1/user-services', - ); - expect( - result.map(({ id, label, allowed, active, source }) => ({ - id, - label, - allowed, - active, - source, - })), - ).toEqual([ - { - id: 'user-service-a', - label: 'GitHub work', - allowed: true, - active: true, - source: 'personal', - }, - { - id: 'user-service-b', - label: 'Google Drive', - allowed: false, - active: true, - source: 'organization', - }, - { - id: 'user-service-c', - label: 'calendar', - allowed: true, - active: false, - source: 'organization', - }, - { - id: 'user-service-d', - label: 'future', - allowed: false, - active: true, - source: 'unknown', - }, - ]); - expect(JSON.stringify(result)).not.toContain('TEST_ONLY_SECRET'); -}); - it('preserves an explicit empty allowlist and retains only the accepted registration identity', async () => { fetchMock.mockResolvedValue( response( diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts new file mode 100644 index 0000000000..b3bf6c9e32 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts @@ -0,0 +1,144 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { listChannelServices } from './channelServicesApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), +})); +const fetchMock = jest.mocked(authFetch); +const inventoryPath = 'https://nyx.example.test/api/v1/user-services'; +const catalogPath = 'https://nyx.example.test/api/v1/mcp/config'; +const personal = { + id: 'us-work', + slug: 'api-github', + label: 'GitHub work', + is_active: true, + credential_source: { type: 'personal' }, + default_request_headers: [{ value: 'TEST_ONLY_SECRET' }], +}; +const response = (value: unknown, status = 200) => + ({ ok: status === 200, status, json: async () => value }) as Response; + +afterEach(() => fetchMock.mockReset()); + +it('returns only active account services authorized by the caller catalog, matching exact UserService IDs', async () => { + fetchMock.mockImplementation(async (input) => + input === inventoryPath + ? response({ + services: [ + personal, + { ...personal, id: 'us-other-account-key' }, + { ...personal, id: 'catalog-github', slug: 'platform-copy' }, + { ...personal, id: 'us-inactive', is_active: false }, + { + ...personal, + id: 'us-org', + slug: 'drive', + label: '', + catalog_service_name: 'Google Drive', + credential_source: { + type: 'org', + org_name: 'Team', + allowed: true, + }, + }, + { + ...personal, + id: 'us-viewer', + credential_source: { type: 'org', allowed: false }, + }, + { + ...personal, + id: 'us-unknown', + credential_source: { type: 'future', allowed: true }, + }, + ], + }) + : response({ + contract_version: '1.0', + services: [ + { service_id: 'us-work', is_user_service: true }, + { service_id: 'us-org', is_user_service: true }, + { service_id: 'us-inactive', is_user_service: true }, + { service_id: 'us-viewer', is_user_service: true }, + { service_id: 'us-unknown', is_user_service: true }, + { service_id: 'catalog-github', is_user_service: false }, + { service_id: 'us-not-in-inventory', is_user_service: true }, + ], + }), + ); + const signal = new AbortController().signal; + const result = await listChannelServices(signal); + expect(result).toEqual([ + { + id: 'us-work', + slug: 'api-github', + label: 'GitHub work', + active: true, + allowed: true, + source: 'personal', + organizationName: null, + }, + { + id: 'us-org', + slug: 'drive', + label: 'Google Drive', + active: true, + allowed: true, + source: 'organization', + organizationName: 'Team', + }, + ]); + expect(JSON.stringify(result)).not.toContain('TEST_ONLY_SECRET'); + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ + inventoryPath, + catalogPath, + ]); + for (const path of [inventoryPath, catalogPath]) { + expect(fetchMock).toHaveBeenCalledWith(path, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { Accept: 'application/json' }, + }); + } +}); + +it('keeps an empty caller catalog empty even when the account owns services', async () => { + fetchMock.mockImplementation(async (input) => + response( + input === inventoryPath + ? { services: [personal] } + : { contract_version: '1.0', services: [] }, + ), + ); + await expect(listChannelServices()).resolves.toEqual([]); +}); + +it.each([ + ['denied access', {}, 403], + ['unsupported contract', { contract_version: 'future', services: [] }, 200], + [ + 'missing identity kind', + { contract_version: '1.0', services: [{ service_id: 'us-work' }] }, + 200, + ], + [ + 'ambiguous identity', + { + contract_version: '1.0', + services: [ + { service_id: 'us-work', is_user_service: true }, + { service_id: 'us-work', is_user_service: false }, + ], + }, + 200, + ], +] as const)('never falls back to the account inventory after %s', async (_reason, catalog, status) => { + fetchMock.mockImplementation(async (input) => + input === inventoryPath + ? response({ services: [personal] }) + : response(catalog, status), + ); + await expect(listChannelServices()).rejects.toThrow(); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts index 6aba840982..ff18fdd122 100644 --- a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts @@ -27,8 +27,8 @@ function decodeService(value: unknown): ChannelServiceChoice { if (!id.trim() || !slug.trim()) throw new Error('Missing service identity.'); const personal = source.type === 'personal'; const organization = source.type === 'org'; - // Only the documented personal variant has implicit permission. Unknown - // variants and organization rows without allowed=true cannot be selected. + // This is account-level availability only. Current bearer access is checked + // separately against NyxID's caller-scoped catalog before returning choices. return { id, slug, @@ -51,14 +51,53 @@ export async function listChannelServices( const config = getNyxIDRuntimeConfig(); if (config.configurationError || !config.baseUrl) throw new Error('NyxID is unavailable.'); - const response = await authFetch(`${config.baseUrl}/api/v1/user-services`, { - signal, - headers: { Accept: 'application/json' }, - }); - if (!response.ok) throw new ChannelApiError(response.status); - const body = expectRecord(await response.json(), 'User services'); + const [body, catalog] = await Promise.all( + ['/api/v1/user-services', '/api/v1/mcp/config'].map(async (path) => { + const response = await authFetch(`${config.baseUrl}${path}`, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { Accept: 'application/json' }, + }); + if (!response.ok) throw new ChannelApiError(response.status); + return expectRecord(await response.json(), 'NyxID service inventory'); + }), + ); + // The account inventory does not enforce OAuth service grants. The v1 MCP + // catalog does, and only is_user_service rows carry exact UserService IDs. + if (catalog.contract_version !== '1.0') + throw new Error('Unsupported NyxID service catalog.'); + const catalogServices = expectArray( + catalog.services, + 'Service catalog', + (value) => { + const row = expectRecord(value, 'Catalog service'); + const id = readString(row, 'service_id', 'Catalog service ID'); + if (!id.trim()) throw new Error('Missing catalog service identity.'); + return { + id, + userService: expectBoolean( + row.is_user_service, + 'User service identity', + ), + }; + }, + ); + if ( + new Set(catalogServices.map((service) => service.id)).size !== + catalogServices.length + ) + throw new Error('Ambiguous catalog service identity.'); + const authorizedIds = new Set( + catalogServices + .filter((service) => service.userService) + .map((service) => service.id), + ); const services = expectArray(body.services, 'User services', decodeService); if (new Set(services.map((service) => service.id)).size !== services.length) throw new Error('Ambiguous user service identity.'); - return services; + return services.filter( + (service) => + service.active && service.allowed && authorizedIds.has(service.id), + ); } From 2490f26f4b6ff584abc91a6f01781e9442021c1e Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 10:19:36 +0800 Subject: [PATCH 135/167] fix: Show Telegram and WhatsApp in channel directory --- .../docs/features/2026-09-14-channel-pages.md | 5 ++++- .../src/locales/channelMessages.en-US.ts | 5 +---- .../src/locales/channelMessages.zh-CN.ts | 5 +---- .../channels/ChannelsPage.test.tsx | 14 +++++++++----- .../channels/ChannelsPage.tsx | 7 ++----- .../channels/presentation.tsx | 5 +++++ 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index ccc9c87a09..1be78203f5 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -30,7 +30,10 @@ missing IDs remain unlinked placeholders. connection details. Registration IDs are opaque and encoded as one segment. - `/scopes/:scopeId/workflow-activity-vnext/channels/connect/telegram`: the single-page Telegram connection form. Connect Telegram navigates here. - Lark, Feishu, Discord, and Slack are marked Soon in the current design. + The channel directory shows Telegram and WhatsApp only. WhatsApp replaces + the Lark/Feishu placeholders and is marked Soon without a connection action; + Discord and Slack placeholders are omitted. Existing connected records still + render the platform returned by the API. The older backend `/channels` onboarding page remains independently available. `AEVATAR_CHANNEL_WEBHOOK_BASE_URL` optionally sets the public backend callback diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 3ee8979b9b..46434117c2 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -11,12 +11,9 @@ export default { 'channels.connect': 'Connect', 'channels.connectPlatform': 'Connect {platform}', 'channels.platform.feishu': 'Feishu', - 'channels.platform.lark.description': 'Connect your bot to chats and groups.', - 'channels.platform.feishu.description': 'Feishu, using the same local flow.', 'channels.platform.telegram.description': 'Connect with a BotFather bot token. Webhook setup is handled for you.', - 'channels.platform.discord.description': 'Server and direct-message support.', - 'channels.platform.slack.description': 'Workspace bot support.', + 'channels.platform.whatsapp.description': 'Chat with your bot on WhatsApp.', 'channels.connected': 'Connected', 'channels.connectedDescription': 'Channels connected to your account.', 'channels.bot': '{platform} bot', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index b44b48311d..b12a07a485 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -9,12 +9,9 @@ export default { 'channels.connect': '连接', 'channels.connectPlatform': '连接 {platform}', 'channels.platform.feishu': '飞书', - 'channels.platform.lark.description': '将机器人接入聊天和群组。', - 'channels.platform.feishu.description': '面向飞书的渠道接入。', 'channels.platform.telegram.description': '使用 BotFather 机器人令牌接入,Webhook 自动配置。', - 'channels.platform.discord.description': '支持服务器频道与私信。', - 'channels.platform.slack.description': '工作区机器人支持。', + 'channels.platform.whatsapp.description': '在 WhatsApp 中与你的机器人对话。', 'channels.connected': '已连接', 'channels.connectedDescription': '连接到你账户的渠道。', 'channels.bot': '{platform} 机器人', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index 6d9f392500..7c8a787fcd 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -128,12 +128,16 @@ describe('Channel pages', () => { 'href', '/scopes/scope-alpha/workflow-activity-vnext/channels/connect/telegram', ); + const availableChannels = screen.getByRole('region', { + name: 'Available channels', + }); expect( - screen.queryByRole('link', { name: 'Connect Lark' }), - ).not.toBeInTheDocument(); - expect( - screen.queryByRole('link', { name: 'Connect Slack' }), - ).not.toBeInTheDocument(); + within(availableChannels) + .getAllByRole('heading', { level: 3 }) + .map((heading) => heading.textContent), + ).toEqual(['Telegram', 'WhatsApp']); + expect(within(availableChannels).getAllByRole('link')).toHaveLength(1); + expect(within(availableChannels).getAllByText('Soon')).toHaveLength(2); expect(screen.getByRole('link', { name: 'Channels' })).toHaveAttribute( 'aria-current', 'page', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index f7fab24ca6..44305e7315 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -25,7 +25,7 @@ import { import { useChannelRegistrations, useChannelStatus } from './queries'; import { channelsCss } from './styles'; -const platforms = ['lark', 'feishu', 'telegram', 'discord', 'slack'] as const; +const platforms = ['telegram', 'whatsapp'] as const; function ConnectedRow({ registration, @@ -142,12 +142,9 @@ export default function ChannelsPage({ {t( `channels.platform.${platform}.description`, { - lark: 'Connect your bot to chats and groups.', - feishu: 'Feishu, using the same local flow.', telegram: 'Connect with a BotFather bot token. Webhook setup is handled for you.', - discord: 'Server and direct-message support.', - slack: 'Workspace bot support.', + whatsapp: 'Chat with your bot on WhatsApp.', }[platform], )}

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx index 9846986b7a..aca6fff58a 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx @@ -3,6 +3,7 @@ import { LinkOutlined, MessageOutlined, SendOutlined, + WhatsAppOutlined, } from '@ant-design/icons'; import { Button } from 'antd'; import * as React from 'react'; @@ -51,6 +52,8 @@ export function platformName(platform: string): string { return t('channels.platform.feishu', 'Feishu'); case 'telegram': return 'Telegram'; + case 'whatsapp': + return 'WhatsApp'; case 'discord': return 'Discord'; case 'slack': @@ -68,6 +71,8 @@ export function ChannelIcon({ platform }: { readonly platform: string }) { > {platform === 'telegram' ? ( + ) : platform === 'whatsapp' ? ( + ) : ['lark', 'feishu', 'discord', 'slack'].includes(platform) ? ( ) : ( From 2fc56ed93a511bccce359fe58ee93d8a53c31c75 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 10:52:47 +0800 Subject: [PATCH 136/167] fix: Include authorized model services in channel grants --- .../docs/features/2026-09-14-channel-pages.md | 64 +++-- .../channels/TelegramConnectionPage.test.tsx | 124 ++++----- .../src/shared/api/channelServicesApi.test.ts | 255 ++++++++++-------- .../src/shared/api/channelServicesApi.ts | 65 ++--- .../src/shared/auth/serviceGrants.ts | 59 ++++ .../tests/fixtures/nyxidServiceSession.ts | 32 +++ 6 files changed, 351 insertions(+), 248 deletions(-) create mode 100644 apps/aevatar-console-web/src/shared/auth/serviceGrants.ts create mode 100644 apps/aevatar-console-web/tests/fixtures/nyxidServiceSession.ts diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index ccc9c87a09..1357ea475d 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -50,35 +50,41 @@ and the authenticated registration POST. It stays in component memory for a manual retry after failure and clears after successful admission. No registration mutation cache stores it. -Services use two reads against the existing NyxID authority through the current -session's authenticated fetch: - -- `GET /api/v1/user-services` supplies account-owned/inherited records and safe - display fields. It does **not** filter the current OAuth bearer's grants. - Personal ownership alone therefore does not make a service selectable. -- `GET /api/v1/mcp/config` (`contract_version: "1.0"`) supplies the current - caller's authorized, executable services. Its documented contract applies - service/node grants and credential/route availability before publication. - Only `is_user_service: true` rows carry exact `UserService.id` values; - platform catalog IDs must never enter a bot's service allowlist. - -The picker contains only the exact-ID intersection of the two responses, -further excluding inactive inventory rows, disallowed organization rows and -unknown credential-source variants. Services outside the current authorization -are hidden, including from search and Select all. The catalog can omit a -service whose credential or node route is unavailable even when the account -owns it. Neither endpoint failing, an unsupported catalog version nor malformed -or ambiguous identities can fall back to showing the account inventory. Only -safe display fields from the filtered result enter the query cache; raw -catalog operation descriptors and credential data are discarded. Both reads -omit browser cookies and bypass HTTP caching so NyxID evaluates the existing -bearer session. They run on entry and explicit recovery, without polling, -window-focus or reconnect refresh. - -This contract is documented in NyxID `docs/API.md` (MCP Configuration) and -`docs/ARCHITECTURE.md`, with implementation in `handlers/mcp.rs` and -`services/mcp_service.rs`. Backend registration authority remains enforced by -NyxID/Aevatar; this picker does not mint credentials or broaden OAuth grants. +Services use the current NyxID session's complete `GET /api/v1/user-services` +inventory and its exact bearer service grants. The account inventory includes +HTTP tools and LLM services; personal ownership alone does not establish that +the current Aevatar session can grant a service to a bot. + +The existing session helper refreshes an expired session before selection. +The auth adapter reads `allow_all_services` and `allowed_service_ids` from that +access token. Explicit `true` permits all active, account-accessible UserServices; +explicit `false` permits only exact IDs in the list, including an empty grant. +Inactive services, disallowed organization memberships and unknown credential +sources remain excluded. Names, slugs, catalog IDs and credential IDs never +substitute for `UserService.id`. No service type is excluded: an authorized LLM +service such as Chrono Public remains selectable even without MCP operations. + +These claims are used only for display filtering, not as client-side signature +verification or proof that registration will succeed. The inventory request is +pinned to the exact bearer used for filtering; NyxID authenticates it and the +registration backend enforces delegation limits. Unsupported/malformed claims, +a mismatched account subject, an expired JWT or a failed inventory read leave +the picker in a retryable error state. There is no allow-all fallback when +claims are absent. Token/parser errors are sanitized, and token/claim payloads +never enter the query cache. Only the filtered safe display fields are retained. + +Do not substitute `/api/v1/mcp/config` for this authorization list: its documented +contract describes executable tool operations and omits LLM services and some +services with unavailable runtime routes. `/api/v1/llm/status` is also not an +exact current-bearer grant inventory, and account consent records may differ +from the current token's resource restrictions. The NyxID access-token producer +in `backend/src/crypto/jwt.rs` emits explicit service grants for OAuth clients; +`backend/src/mw/auth.rs` consumes the same fields for service authorization. + +The inventory read omits browser cookies and bypasses HTTP caching. It runs on +entry and explicit recovery only; no polling, window-focus or reconnect refresh +is added. A bearer with only some account services authorized sees only that +subset, across HTTP and LLM service types. Options use label, then catalog service name, then slug. Search matches label or slug and preserves selections outside the current search. A Select all diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index c8f0265904..cee597f38d 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -8,14 +8,19 @@ import { } from '@testing-library/react'; import * as React from 'react'; import { authFetch } from '@/shared/auth/fetch'; +import { persistAuthSession } from '@/shared/auth/session'; import { history } from '@/shared/navigation/history'; +import { createNyxIDServiceSession } from '../../../../tests/fixtures/nyxidServiceSession'; import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; import WorkflowActivityVNextPage from '../index'; import TelegramConnectionPage from './TelegramConnectionPage'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); jest.mock('@/shared/auth/config', () => ({ - getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), + getNyxIDRuntimeConfig: () => ({ + baseUrl: 'https://nyx.example.test', + enabled: true, + }), })); jest.mock('@/shared/studio/api', () => ({ studioApi: { @@ -40,15 +45,6 @@ const service = { credential_source: { type: 'personal' }, }; const servicePath = 'https://nyx.example.test/api/v1/user-services'; -const catalogPath = 'https://nyx.example.test/api/v1/mcp/config'; -const catalog = { - contract_version: '1.0', - services: [ - { service_id: 'user-service-github', is_user_service: true }, - { service_id: 'user-service-personal', is_user_service: true }, - { service_id: 'user-service-slack', is_user_service: true }, - ], -}; const listPath = '/api/channels/registrations'; function enterNames() { fireEvent.change(screen.getByLabelText(/^Bot token/), { @@ -63,16 +59,13 @@ function enterNames() { } beforeEach(() => { fetchMock.mockReset(); + persistAuthSession(createNyxIDServiceSession()); telegramFetch.mockReset(); global.fetch = telegramFetch; jest.spyOn(history, 'push').mockImplementation(() => {}); jest.spyOn(history, 'replace').mockImplementation(() => {}); fetchMock.mockImplementation(async (input) => - input === catalogPath - ? response(catalog) - : input === servicePath - ? response({ services: [service] }) - : response([]), + input === servicePath ? response({ services: [service] }) : response([]), ); }); afterEach(() => { @@ -84,7 +77,6 @@ it('reads registrations only after acceptance or Check again, never while idle o try { let attempts = 0; fetchMock.mockImplementation(async (input, init) => { - if (input === catalogPath) return response(catalog); if (init?.method === 'POST') { attempts += 1; return attempts === 1 @@ -115,10 +107,7 @@ it('reads registrations only after acceptance or Check again, never while idle o ); await screen.findByText(/No services are available/); await idleAndReturn(); - expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ - servicePath, - catalogPath, - ]); + expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); enterNames(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( @@ -154,12 +143,18 @@ it('searches services, enforces permissions, submits selected IDs once and waits }); let observed = false; fetchMock.mockImplementation(async (input, init) => { - if (input === catalogPath) return response(catalog); if (init?.method === 'POST') return post; if (input === servicePath) return response({ services: [ service, + { + ...service, + id: 'user-service-model', + slug: 'chrono-llm-public', + label: 'Chrono Public', + service_type: 'llm', + }, { ...service, id: 'user-service-ungranted', @@ -212,6 +207,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits expect( screen.queryByRole('checkbox', { name: /GitHub not authorized/ }), ).not.toBeInTheDocument(); + expect(screen.getByRole('checkbox', { name: /Chrono Public/ })).toBeEnabled(); fireEvent.click(screen.getByRole('checkbox', { name: 'Select all' })); fireEvent.change( screen.getByRole('textbox', { name: 'Search services by name or slug' }), @@ -220,7 +216,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits expect( screen.queryByRole('checkbox', { name: /GitHub work/ }), ).not.toBeInTheDocument(); - expect(screen.getByText('1 selected')).toBeInTheDocument(); + expect(screen.getByText('2 selected')).toBeInTheDocument(); enterNames(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); @@ -238,7 +234,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits label: 'My team bot', default_skill_name: 'review.skill', authorization_mode: 'explicit_service_allowlist', - service_ids: ['user-service-github'], + service_ids: ['user-service-github', 'user-service-model'], webhook_base_url: 'https://aevatar-console-backend-api.aevatar.ai', }); await act(async () => @@ -287,32 +283,30 @@ it('searches services, enforces permissions, submits selected IDs once and waits }); it('selects and clears available results while preserving selections outside the search', async () => { - fetchMock.mockImplementation(async (input) => - input === catalogPath - ? response(catalog) - : response({ - services: [ - service, - { - ...service, - id: 'user-service-personal', - label: 'GitHub personal', - }, - { - ...service, - id: 'user-service-slack', - label: 'Slack', - slug: 'slack', - }, - { ...service, id: 'inactive', label: 'Inactive', is_active: false }, - { - ...service, - id: 'denied', - label: 'Denied', - credential_source: { type: 'org', allowed: false }, - }, - ], - }), + fetchMock.mockImplementation(async () => + response({ + services: [ + service, + { + ...service, + id: 'user-service-personal', + label: 'GitHub personal', + }, + { + ...service, + id: 'user-service-slack', + label: 'Slack', + slug: 'slack', + }, + { ...service, id: 'inactive', label: 'Inactive', is_active: false }, + { + ...service, + id: 'denied', + label: 'Denied', + credential_source: { type: 'org', allowed: false }, + }, + ], + }), ); renderWithQueryClient(); const all = await screen.findByRole('checkbox', { name: 'Select all' }); @@ -349,8 +343,9 @@ it('selects and clears available results while preserving selections outside the ).toBePartiallyChecked(); }); -it('recovers caller-catalog failure without exposing account services and defaults optional names with no access', async () => { - let catalogReads = 0; +it('recovers inventory failure and defaults optional names with an explicitly empty service grant', async () => { + persistAuthSession(createNyxIDServiceSession({ allowed_service_ids: [] })); + let serviceReads = 0; telegramFetch.mockResolvedValue( response({ ok: true, @@ -362,11 +357,10 @@ it('recovers caller-catalog failure without exposing account services and defaul }), ); fetchMock.mockImplementation(async (input, init) => { - if (input === catalogPath) - return ++catalogReads === 1 + if (input === servicePath) + return ++serviceReads === 1 ? response({}, 503) - : response({ contract_version: '1.0', services: [] }); - if (input === servicePath) return response({ services: [service] }); + : response({ services: [service] }); if (init?.method === 'POST') return response( { error: 'missing_bot_token', note: 'TEST_ONLY_SECRET' }, @@ -430,7 +424,6 @@ it('shows a name-lookup toast and retries with the current token and custom fiel new Error(`Failed https://api.telegram.org/bot${telegramToken}/getMe`), ); fetchMock.mockImplementation(async (input, init) => { - if (input === catalogPath) return response(catalog); if (init?.method === 'POST') return response( { status: 'accepted', registration_id: 'registration-lookup' }, @@ -484,7 +477,6 @@ it.each([ ])('shows a toast for %s, preserves inputs and allows a successful manual retry', async (failure) => { let attempts = 0; fetchMock.mockImplementation(async (input, init) => { - if (input === catalogPath) return response(catalog); if (init?.method === 'POST') { attempts += 1; if (attempts === 1) { @@ -547,16 +539,18 @@ it.each([ it('removes revoked selections after a rejected submission and allows a retry with the remaining grants', async () => { let attempted = false; - let resolveCatalog!: (value: Response) => void; - const updatedCatalog = new Promise((resolve) => { - resolveCatalog = resolve; + let resolveInventory!: (value: Response) => void; + const updatedInventory = new Promise((resolve) => { + resolveInventory = resolve; }); fetchMock.mockImplementation(async (input, init) => { - if (input === servicePath) return response({ services: [service] }); - if (input === catalogPath) - return attempted ? updatedCatalog : response(catalog); + if (input === servicePath) + return attempted ? updatedInventory : response({ services: [service] }); if (init?.method === 'POST') { attempted = true; + persistAuthSession( + createNyxIDServiceSession({ allowed_service_ids: [] }), + ); return response({ error: 'invalid_service_ids' }, 400); } return response([]); @@ -576,9 +570,7 @@ it('removes revoked selections after a rejected submission and allows a retry wi expect( fetchMock.mock.calls.filter(([, init]) => init?.method === 'POST'), ).toHaveLength(1); - await act(async () => - resolveCatalog(response({ contract_version: '1.0', services: [] })), - ); + await act(async () => resolveInventory(response({ services: [service] }))); await screen.findByText(/No services are available/); expect( screen.queryByRole('checkbox', { name: /GitHub work/ }), diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts index b3bf6c9e32..4f00dfa632 100644 --- a/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts @@ -1,13 +1,19 @@ import { authFetch } from '@/shared/auth/fetch'; +import { persistAuthSession } from '@/shared/auth/session'; +import { createNyxIDServiceSession } from '../../../tests/fixtures/nyxidServiceSession'; import { listChannelServices } from './channelServicesApi'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); jest.mock('@/shared/auth/config', () => ({ - getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), + getNyxIDRuntimeConfig: () => ({ + baseUrl: 'https://nyx.example.test', + clientId: 'test-client', + enabled: true, + }), })); const fetchMock = jest.mocked(authFetch); +const originalFetch = global.fetch; const inventoryPath = 'https://nyx.example.test/api/v1/user-services'; -const catalogPath = 'https://nyx.example.test/api/v1/mcp/config'; const personal = { id: 'us-work', slug: 'api-github', @@ -19,126 +25,153 @@ const personal = { const response = (value: unknown, status = 200) => ({ ok: status === 200, status, json: async () => value }) as Response; -afterEach(() => fetchMock.mockReset()); +afterEach(() => { + fetchMock.mockReset(); + global.fetch = originalFetch; +}); -it('returns only active account services authorized by the caller catalog, matching exact UserService IDs', async () => { - fetchMock.mockImplementation(async (input) => - input === inventoryPath - ? response({ - services: [ - personal, - { ...personal, id: 'us-other-account-key' }, - { ...personal, id: 'catalog-github', slug: 'platform-copy' }, - { ...personal, id: 'us-inactive', is_active: false }, - { - ...personal, - id: 'us-org', - slug: 'drive', - label: '', - catalog_service_name: 'Google Drive', - credential_source: { - type: 'org', - org_name: 'Team', - allowed: true, - }, - }, - { - ...personal, - id: 'us-viewer', - credential_source: { type: 'org', allowed: false }, - }, - { - ...personal, - id: 'us-unknown', - credential_source: { type: 'future', allowed: true }, - }, - ], - }) - : response({ - contract_version: '1.0', - services: [ - { service_id: 'us-work', is_user_service: true }, - { service_id: 'us-org', is_user_service: true }, - { service_id: 'us-inactive', is_user_service: true }, - { service_id: 'us-viewer', is_user_service: true }, - { service_id: 'us-unknown', is_user_service: true }, - { service_id: 'catalog-github', is_user_service: false }, - { service_id: 'us-not-in-inventory', is_user_service: true }, - ], - }), +it('includes authorized LLM services and matches exact UserService grants without requiring an MCP tool entry', async () => { + const session = createNyxIDServiceSession({ + allowed_service_ids: [ + 'us-work', + 'us-model', + 'us-inactive', + 'us-org', + 'us-viewer', + 'us-unknown', + 'us-not-in-inventory', + 'catalog-github', + ], + }); + persistAuthSession(session); + fetchMock.mockResolvedValue( + response({ + services: [ + personal, + { ...personal, id: 'us-other-account-key' }, + { + ...personal, + id: 'us-catalog-reference', + catalog_service_id: 'catalog-github', + }, + { ...personal, id: 'us-inactive', is_active: false }, + { + ...personal, + id: 'us-model', + slug: 'chrono-llm-public', + label: 'Chrono Public', + service_type: 'llm', + }, + { + ...personal, + id: 'us-org', + slug: 'drive', + label: '', + catalog_service_name: 'Google Drive', + credential_source: { type: 'org', org_name: 'Team', allowed: true }, + }, + { + ...personal, + id: 'us-viewer', + credential_source: { type: 'org', allowed: false }, + }, + { + ...personal, + id: 'us-unknown', + credential_source: { type: 'future', allowed: true }, + }, + ], + }), ); const signal = new AbortController().signal; const result = await listChannelServices(signal); - expect(result).toEqual([ - { - id: 'us-work', - slug: 'api-github', - label: 'GitHub work', - active: true, - allowed: true, - source: 'personal', - organizationName: null, - }, - { - id: 'us-org', - slug: 'drive', - label: 'Google Drive', - active: true, - allowed: true, - source: 'organization', - organizationName: 'Team', - }, + expect(result.map(({ id, label }) => ({ id, label }))).toEqual([ + { id: 'us-work', label: 'GitHub work' }, + { id: 'us-model', label: 'Chrono Public' }, + { id: 'us-org', label: 'Google Drive' }, ]); - expect(JSON.stringify(result)).not.toContain('TEST_ONLY_SECRET'); - expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ - inventoryPath, - catalogPath, - ]); - for (const path of [inventoryPath, catalogPath]) { - expect(fetchMock).toHaveBeenCalledWith(path, { - signal, - credentials: 'omit', - cache: 'no-store', - headers: { Accept: 'application/json' }, - }); - } + expect(JSON.stringify(result)).not.toContain('TEST_ONLY'); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith(inventoryPath, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { + Accept: 'application/json', + Authorization: `Bearer ${session.tokens.accessToken}`, + }, + }); }); -it('keeps an empty caller catalog empty even when the account owns services', async () => { - fetchMock.mockImplementation(async (input) => - response( - input === inventoryPath - ? { services: [personal] } - : { contract_version: '1.0', services: [] }, - ), +it.each([ + true, + false, +])('respects explicit allow_all_services=%s with an empty ID list', async (allowAll) => { + persistAuthSession( + createNyxIDServiceSession({ + allow_all_services: allowAll, + allowed_service_ids: [], + }), ); - await expect(listChannelServices()).resolves.toEqual([]); + fetchMock.mockResolvedValue(response({ services: [personal] })); + const result = await listChannelServices(); + expect(result.map(({ id }) => id)).toEqual(allowAll ? ['us-work'] : []); }); it.each([ - ['denied access', {}, 403], - ['unsupported contract', { contract_version: 'future', services: [] }, 200], - [ - 'missing identity kind', - { contract_version: '1.0', services: [{ service_id: 'us-work' }] }, - 200, - ], - [ - 'ambiguous identity', - { - contract_version: '1.0', - services: [ - { service_id: 'us-work', is_user_service: true }, - { service_id: 'us-work', is_user_service: false }, - ], + ['missing grant mode', { allow_all_services: undefined }], + ['malformed ID list', { allowed_service_ids: ['us-work', null] }], + ['wrong account subject', { sub: 'another-user' }], + ['expired JWT', { exp: 1 }], +] as const)('fails closed for %s without exposing token payloads or reading the inventory', async (_name, claims) => { + persistAuthSession( + createNyxIDServiceSession({ ...claims, note: 'TEST_ONLY_SECRET' }), + ); + await expect(listChannelServices()).rejects.toThrow( + 'Could not read the current NyxID service authorization.', + ); + expect(fetchMock).not.toHaveBeenCalled(); +}); + +it('does not treat locally decoded claims as successful server authorization', async () => { + persistAuthSession(createNyxIDServiceSession({ allow_all_services: true })); + fetchMock.mockResolvedValue(response({ services: [personal] }, 401)); + await expect(listChannelServices()).rejects.toMatchObject({ status: 401 }); +}); + +it('refreshes an expired session before filtering and pins the inventory request to the refreshed bearer', async () => { + const old = createNyxIDServiceSession({ allowed_service_ids: ['us-old'] }); + const fresh = createNyxIDServiceSession({ allowed_service_ids: ['us-work'] }); + persistAuthSession({ + ...old, + tokens: { + ...old.tokens, + expiresAt: Date.now() - 1, + refreshToken: 'TEST_ONLY_REFRESH', }, - 200, - ], -] as const)('never falls back to the account inventory after %s', async (_reason, catalog, status) => { - fetchMock.mockImplementation(async (input) => - input === inventoryPath - ? response({ services: [personal] }) - : response(catalog, status), + }); + const tokenFetch = jest.fn().mockResolvedValue( + response({ + access_token: fresh.tokens.accessToken, + token_type: 'Bearer', + expires_in: 3600, + }), + ); + global.fetch = tokenFetch; + fetchMock.mockResolvedValue( + response({ services: [personal, { ...personal, id: 'us-old' }] }), + ); + expect((await listChannelServices()).map(({ id }) => id)).toEqual([ + 'us-work', + ]); + expect(tokenFetch).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith( + inventoryPath, + expect.objectContaining({ + headers: { + Accept: 'application/json', + Authorization: `Bearer ${fresh.tokens.accessToken}`, + }, + }), ); - await expect(listChannelServices()).rejects.toThrow(); }); diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts index ff18fdd122..5d888244b8 100644 --- a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts @@ -1,5 +1,7 @@ +import { ensureActiveAuthSession } from '@/shared/auth/client'; import { getNyxIDRuntimeConfig } from '@/shared/auth/config'; import { authFetch } from '@/shared/auth/fetch'; +import { readAccessTokenServiceGrants } from '@/shared/auth/serviceGrants'; import { ChannelApiError } from './channelsApi'; import { expectArray, @@ -28,7 +30,7 @@ function decodeService(value: unknown): ChannelServiceChoice { const personal = source.type === 'personal'; const organization = source.type === 'org'; // This is account-level availability only. Current bearer access is checked - // separately against NyxID's caller-scoped catalog before returning choices. + // separately against the current access token before returning choices. return { id, slug, @@ -51,53 +53,32 @@ export async function listChannelServices( const config = getNyxIDRuntimeConfig(); if (config.configurationError || !config.baseUrl) throw new Error('NyxID is unavailable.'); - const [body, catalog] = await Promise.all( - ['/api/v1/user-services', '/api/v1/mcp/config'].map(async (path) => { - const response = await authFetch(`${config.baseUrl}${path}`, { - signal, - credentials: 'omit', - cache: 'no-store', - headers: { Accept: 'application/json' }, - }); - if (!response.ok) throw new ChannelApiError(response.status); - return expectRecord(await response.json(), 'NyxID service inventory'); - }), + const session = await ensureActiveAuthSession(); + if (!session) throw new ChannelApiError(401); + const grants = readAccessTokenServiceGrants( + session.tokens.accessToken, + session.user.sub, ); - // The account inventory does not enforce OAuth service grants. The v1 MCP - // catalog does, and only is_user_service rows carry exact UserService IDs. - if (catalog.contract_version !== '1.0') - throw new Error('Unsupported NyxID service catalog.'); - const catalogServices = expectArray( - catalog.services, - 'Service catalog', - (value) => { - const row = expectRecord(value, 'Catalog service'); - const id = readString(row, 'service_id', 'Catalog service ID'); - if (!id.trim()) throw new Error('Missing catalog service identity.'); - return { - id, - userService: expectBoolean( - row.is_user_service, - 'User service identity', - ), - }; + const response = await authFetch(`${config.baseUrl}/api/v1/user-services`, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { + Accept: 'application/json', + // Pin the inventory to the exact bearer whose grants were read above. + Authorization: `Bearer ${session.tokens.accessToken}`, }, - ); - if ( - new Set(catalogServices.map((service) => service.id)).size !== - catalogServices.length - ) - throw new Error('Ambiguous catalog service identity.'); - const authorizedIds = new Set( - catalogServices - .filter((service) => service.userService) - .map((service) => service.id), - ); + }); + if (!response.ok) throw new ChannelApiError(response.status); + const body = expectRecord(await response.json(), 'User services'); + const authorizedIds = new Set(grants.allowedServiceIds); const services = expectArray(body.services, 'User services', decodeService); if (new Set(services.map((service) => service.id)).size !== services.length) throw new Error('Ambiguous user service identity.'); return services.filter( (service) => - service.active && service.allowed && authorizedIds.has(service.id), + service.active && + service.allowed && + (grants.allowAllServices || authorizedIds.has(service.id)), ); } diff --git a/apps/aevatar-console-web/src/shared/auth/serviceGrants.ts b/apps/aevatar-console-web/src/shared/auth/serviceGrants.ts new file mode 100644 index 0000000000..28d7a9d499 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/auth/serviceGrants.ts @@ -0,0 +1,59 @@ +import { + expectArray, + expectBoolean, + expectRecord, + expectString, +} from '@/shared/api/http/decoders'; + +export interface NyxIDServiceGrants { + readonly allowAllServices: boolean; + readonly allowedServiceIds: readonly string[]; +} + +// Display filtering only: this does not verify a JWT signature or authorize an +// operation. NyxID validates the same bearer on the inventory request, and the +// registration endpoint remains responsible for enforcing delegation limits. +export function readAccessTokenServiceGrants( + accessToken: string, + expectedSubject: string, +): NyxIDServiceGrants { + try { + const parts = accessToken.split('.'); + if (parts.length !== 3 || parts.some((part) => !part)) throw new Error(); + const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/'); + const bytes = Uint8Array.from(atob(payload), (character) => + character.charCodeAt(0), + ); + const claims = expectRecord( + JSON.parse(new TextDecoder().decode(bytes)), + 'Access claims', + ); + if ( + claims.token_type !== 'access' || + claims.relay === true || + !expectedSubject || + claims.sub !== expectedSubject || + typeof claims.exp !== 'number' || + !Number.isFinite(claims.exp) || + claims.exp * 1000 <= Date.now() + ) + throw new Error(); + const allowAllServices = expectBoolean( + claims.allow_all_services, + 'Service grant mode', + ); + const allowedServiceIds = expectArray( + claims.allowed_service_ids, + 'Service grants', + (value) => { + const id = expectString(value, 'User service ID'); + if (!id.trim()) throw new Error(); + return id; + }, + ); + return { allowAllServices, allowedServiceIds }; + } catch { + // JSON/parser errors must never expose any part of the bearer payload. + throw new Error('Could not read the current NyxID service authorization.'); + } +} diff --git a/apps/aevatar-console-web/tests/fixtures/nyxidServiceSession.ts b/apps/aevatar-console-web/tests/fixtures/nyxidServiceSession.ts new file mode 100644 index 0000000000..3c171b9ada --- /dev/null +++ b/apps/aevatar-console-web/tests/fixtures/nyxidServiceSession.ts @@ -0,0 +1,32 @@ +import type { NyxIDAuthSession } from '@/shared/auth/session'; + +// Deliberately unsigned test credential. It can only be used with mocked HTTP. +export function createNyxIDServiceSession( + claims: Record = {}, +): NyxIDAuthSession { + const expiresAt = Date.now() + 3_600_000; + const payload = Buffer.from( + JSON.stringify({ + sub: 'user-test', + token_type: 'access', + exp: Math.floor(expiresAt / 1000), + allow_all_services: false, + allowed_service_ids: [ + 'user-service-github', + 'user-service-personal', + 'user-service-slack', + 'user-service-model', + ], + ...claims, + }), + ).toString('base64url'); + return { + tokens: { + accessToken: `eyJhbGciOiJSUzI1NiJ9.${payload}.TEST_ONLY_SIGNATURE`, + tokenType: 'Bearer', + expiresIn: 3600, + expiresAt, + }, + user: { sub: 'user-test' }, + }; +} From 0f1a196a683d45e79e71337853078201b179aeb3 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:34:32 +0800 Subject: [PATCH 137/167] fix: Center channel pages within the content area --- .../docs/features/2026-09-14-channel-pages.md | 6 ++++++ .../src/pages/workflow-activity-vnext/channels/styles.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index d9f3ea43a6..5114ff4b7b 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -11,6 +11,12 @@ runtime-configuration editor is outside this iteration of issue #3617. Channel refresh is explicitly user driven. Do not add background polling or refresh on focus/reconnection to these pages. +The page header and content are horizontally centered within the area beside +the navigation rail, using the same maximum width and responsive side padding. +The connection form and details share this alignment; the form keeps its +narrower maximum width. On small screens the containers fill the available +width while retaining the shell's side padding. + Refresh and Manage use outlined button styling with visible hover/focus states and larger touch targets on mobile. Manage retains navigation-link semantics. Details omit Scope. Bot ID and Agent key ID are blue underlined external links diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index d483fa59d8..e346260659 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -6,9 +6,10 @@ export const channelsCss = ` --channels-action-shadow: 0 1px 2px rgb(16 24 40 / 5%); background: var(--wa-subtle); } +.channels__main .wa-vnext__header, .channels__content { margin-inline: auto; max-width: 1160px; width: 100%; } .channels__main .wa-vnext__header { padding-top: 40px; } .channels__main .wa-vnext__header h1 { font-size: 24px; line-height: 32px; } -.channels__content { max-width: 1160px; padding-top: 0; } +.channels__content { padding-top: 0; } .channels__intro { color: var(--wa-muted); line-height: 20px; margin: 4px 0 28px; } .channels__section-label { color: var(--wa-muted); font-size: 12px; font-weight: 500; margin: 0 0 12px; } .channels__platforms { display: grid; gap: 16px; grid-template-columns: repeat(4, minmax(0, 1fr)); } From 37fa7f54a195104a9c564a434a042696741e0598 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:54:13 +0800 Subject: [PATCH 138/167] fix: Use channel labels across connection and management pages --- .../docs/features/2026-09-14-channel-pages.md | 44 +++++-- .../src/locales/channelMessages.en-US.ts | 13 +- .../src/locales/channelMessages.zh-CN.ts | 12 +- .../channels/ChannelDetailsPage.tsx | 7 ++ .../channels/ChannelsPage.test.tsx | 118 +++++++++++++++--- .../channels/ChannelsPage.tsx | 61 ++++++++- .../channels/TelegramConnectionPage.test.tsx | 38 +++--- .../channels/TelegramConnectionPage.tsx | 65 ++++------ .../channels/connectionStyles.ts | 2 - .../channels/presentation.tsx | 11 +- .../channels/queries.ts | 15 +++ .../channels/styles.ts | 14 ++- .../src/shared/api/channelBotsApi.test.ts | 58 +++++++++ .../src/shared/api/channelBotsApi.ts | 43 +++++++ .../shared/api/channelConnectionApi.test.ts | 36 +----- .../src/shared/api/channelsApi.ts | 9 +- 16 files changed, 397 insertions(+), 149 deletions(-) create mode 100644 apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts create mode 100644 apps/aevatar-console-web/src/shared/api/channelBotsApi.ts diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 5114ff4b7b..3c73c9c754 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -51,8 +51,9 @@ separate Console frontend origin or shown as an editable form field. ## Telegram connection form -The form requires a masked bot token. Label and Skill name are independently -optional, and the service selection may be empty. The token reveal control is +The form requires a masked bot token and shows one optional Channel name field +(「Channel 名字」 in Chinese). It has no separate Skill name input. The service +selection may be empty. The token reveal control is keyboard accessible. Unsaved form navigation asks whether to discard; form data is never persisted. The token is used only for Telegram's official name lookup and the authenticated registration POST. It stays in component memory for a @@ -125,15 +126,15 @@ Telegram. Inputs and still-authorized selections are preserved for an explicit m retry; no automatic registration retry runs. An in-flight request still blocks duplicate clicks, and an accepted registration stays locked during observation. -### Optional names and Telegram bot-name defaults +### Channel name and Telegram bot-name defaults -Label and Skill name show an optional marker and a bot-name placeholder. When -either trimmed value is blank, the registration adapter first calls Telegram's -official `getMe` endpoint and reads the bot's `first_name`, not its `username`. -Each field resolves independently as its trimmed custom value or that bot name; -a custom label never supplies an empty skill name. Both custom names bypass -lookup. The UI locks while resolving/submitting so the result cannot overwrite -an edit to another token; retries resolve the current token again. +The optional Channel name is the only naming decision. The registration adapter +trims it and sends the same resolved value as both `label` and +`default_skill_name`. A nonblank custom name bypasses Telegram name lookup. +When blank or omitted, the adapter calls Telegram's official `getMe` endpoint +once and uses the bot's `first_name`, never its `username`, for both fields. +The form locks during lookup/submission and preserves its input after failure; +a manual retry resolves the current token/name again. This uses the same official API flow as NyxID's existing Telegram onboarding, because the reviewed Aevatar/NyxID backend has no separate pre-registration @@ -145,6 +146,29 @@ authenticated fetch helper. Raw fetch errors and response bodies are discarded. A rejected token, unavailable lookup or absent bot name prevents registration, shows a safe toast and leaves the form available for retry. +## Connected names, channels and skills + +The connected table separates Channel name from Channel (platform). The current +Aevatar registration summary does not return the submitted label: provisioning +stores it on the NyxID bot. For a nonempty list with bot references, the page +therefore reads `GET /api/v1/channel-bots` once through existing NyxID auth/config, +without an organization selector. This endpoint returns the caller's active +personal bots as `{ bots, total }`. Only `id`, `platform` and `label` enter the +query cache. Registration `nyx_channel_bot_id` must exactly match `bots[].id`, +and platforms must agree; registration IDs, platform bot IDs, usernames and +skill names never substitute for that join or for the Channel name. + +Names load independently of registrations and statuses. Missing/deactivated +bots, empty labels and failed reads show Name unavailable beside the existing +identifier; loading is shown explicitly. A name-read failure preserves the +connected rows and actions, shows a safe error toast and can be retried with +Refresh. Refresh reads the names once with the list/status reads. No per-row +name request, automatic retry, polling, focus or reconnect refresh is added. + +Channel details include Skill using the same name, optional version and Ornn +link as the connected list. Missing skills show Not set. Reading names/skills +does not request full runtime configuration or alter bot resources. + ## API and ownership Contracts were checked against `origin/feature/integrate` and the backend diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 46434117c2..27e822ebfa 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -16,7 +16,9 @@ export default { 'channels.platform.whatsapp.description': 'Chat with your bot on WhatsApp.', 'channels.connected': 'Connected', 'channels.connectedDescription': 'Channels connected to your account.', - 'channels.bot': '{platform} bot', + 'channels.name.loading': 'Loading name…', + 'channels.name.unavailable': 'Name unavailable', + 'channels.column.name': 'Channel name', 'channels.skill.notSet': 'Not set', 'channels.skill.version': 'Version {version}', 'channels.skill.open': 'Open {name} in Ornn', @@ -45,6 +47,8 @@ export default { 'channels.retry': 'Try again', 'channels.error.load': 'Could not load channels. Please try again.', 'channels.error.refresh': 'Could not refresh channels. Try again.', + 'channels.error.names': + 'Could not load channel names. Use Refresh to try again.', 'channels.error.unavailable': 'This channel is unavailable or you do not have access.', 'channels.details': 'Channel details', @@ -78,8 +82,7 @@ export default { 'channels.connect.showToken': 'Show bot token', 'channels.connect.hideToken': 'Hide bot token', 'channels.connect.botFather': 'Get token from BotFather', - 'channels.connect.label': 'Label', - 'channels.connect.skillName': 'Skill name', + 'channels.connect.name': 'Channel name', 'channels.connect.optional': '(optional)', 'channels.connect.botNameDefault': 'Defaults to the Telegram bot name', 'channels.connect.tokenRequired': 'Enter the bot token from BotFather.', @@ -114,7 +117,7 @@ export default { 'channels.connect.error.services': 'The selected services are no longer available. Review your selection and try again.', 'channels.connect.error.skill': - 'The Skill name could not be used. Check the name and your access in Ornn.', + 'Could not configure the bot skill. Check the Channel name and your access in Ornn.', 'channels.connect.error.authorization': 'Your session or service access needs attention. Sign in again and review your NyxID access.', 'channels.connect.error.conflict': @@ -122,7 +125,7 @@ export default { 'channels.connect.error.configuration': 'Channel setup is unavailable. Ask your administrator to check the deployment configuration.', 'channels.connect.error.rejected': - 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', + 'Could not connect Telegram. Check the bot token, Channel name, and selected services, then try again.', 'channels.connect.error.uncertain': 'Could not confirm the connection. Please try again.', 'channels.connect.discardTitle': 'Discard this connection setup?', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index b12a07a485..02d5e7637e 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -14,7 +14,9 @@ export default { 'channels.platform.whatsapp.description': '在 WhatsApp 中与你的机器人对话。', 'channels.connected': '已连接', 'channels.connectedDescription': '连接到你账户的渠道。', - 'channels.bot': '{platform} 机器人', + 'channels.name.loading': '正在加载名称…', + 'channels.name.unavailable': '名称暂不可用', + 'channels.column.name': 'Channel 名字', 'channels.skill.notSet': '未设置', 'channels.skill.version': '版本 {version}', 'channels.skill.open': '在 Ornn 中打开 {name}', @@ -42,6 +44,7 @@ export default { 'channels.retry': '重试', 'channels.error.load': '无法加载渠道,请重试。', 'channels.error.refresh': '无法刷新渠道,请重试。', + 'channels.error.names': '无法加载 Channel 名称,请点击刷新重试。', 'channels.error.unavailable': '此渠道不可用或你没有访问权限。', 'channels.details': '渠道详情', 'channels.details.loading': '正在加载渠道详情', @@ -72,8 +75,7 @@ export default { 'channels.connect.showToken': '显示机器人令牌', 'channels.connect.hideToken': '隐藏机器人令牌', 'channels.connect.botFather': '从 BotFather 获取令牌', - 'channels.connect.label': '名称', - 'channels.connect.skillName': '技能名称', + 'channels.connect.name': 'Channel 名字', 'channels.connect.optional': '(可选)', 'channels.connect.botNameDefault': '默认使用 Telegram 机器人名称', 'channels.connect.tokenRequired': '请输入从 BotFather 获取的机器人令牌。', @@ -101,7 +103,7 @@ export default { '暂时无法获取 Telegram 机器人名称,请重试。', 'channels.connect.error.services': '所选服务已不可用,请重新检查选择后重试。', 'channels.connect.error.skill': - '无法使用此技能名称,请在 Ornn 中检查名称和访问权限。', + '无法配置机器人 Skill,请检查 Channel 名字和你在 Ornn 中的访问权限。', 'channels.connect.error.authorization': '登录状态或服务权限需要处理,请重新登录并检查 NyxID 访问权限。', 'channels.connect.error.conflict': @@ -109,7 +111,7 @@ export default { 'channels.connect.error.configuration': '渠道接入暂不可用,请联系管理员检查部署配置。', 'channels.connect.error.rejected': - '无法连接 Telegram,请检查机器人令牌、技能名称和所选服务后重试。', + '无法连接 Telegram,请检查机器人令牌、Channel 名字和所选服务后重试。', 'channels.connect.error.uncertain': '暂时无法确认连接结果,请重试。', 'channels.connect.discardTitle': '放弃此次连接设置?', 'channels.connect.discard': '放弃', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index 1aefda8d49..d977d25a05 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -16,6 +16,7 @@ import { ChannelIcon, ChannelLink, ChannelLoadError, + ChannelSkill, compactChannelIdentifier, DeliveryStatus, InboundStatus, @@ -183,6 +184,12 @@ export default function ChannelDetailsPage({
{t('channels.column.channel', 'Channel')}
{platformName(registration.platform)}
+
+
{t('channels.column.skill', 'Skill')}
+
+ +
+
{t('channels.inboundMessages', 'Inbound messages')}
diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index 7c8a787fcd..d7d939157a 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -14,6 +14,9 @@ import ChannelDetailsPage from './ChannelDetailsPage'; import ChannelsPage from './ChannelsPage'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), +})); jest.mock('@/shared/studio/api', () => ({ studioApi: { getAuthSession: jest.fn().mockResolvedValue({ authenticated: false }), @@ -51,6 +54,8 @@ const response = (value: unknown, status = 200) => const statusPath = '/api/channels/registrations/registration%3Aalpha%2Fone/status'; const deletePath = '/api/channels/registrations/registration%3Aalpha%2Fone'; +const botsPath = 'https://nyx.example.test/api/v1/channel-bots'; +const bot = { id: 'bot-alpha', platform: 'telegram', label: 'Team channel' }; function deferred() { let resolve!: (value: T) => void; const promise = new Promise((done) => { @@ -67,6 +72,7 @@ beforeEach(() => { jest.spyOn(history, 'push').mockImplementation(() => {}); jest.spyOn(history, 'replace').mockImplementation(() => {}); fetchMock.mockImplementation(async (input) => { + if (input === botsPath) return response({ bots: [bot] }); if (input === '/api/channels/registrations') return response([registration]); if (input === statusPath) @@ -83,10 +89,10 @@ describe('Channel pages', () => { , ); await screen.findByText('Active'); - expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([ - '/api/channels/registrations', - statusPath, - ]); + await screen.findByText('Team channel'); + expect(fetchMock.mock.calls.map(([input]) => input).sort()).toEqual( + ['/api/channels/registrations', statusPath, botsPath].sort(), + ); await act(async () => { await jest.advanceTimersByTimeAsync(60_000); focusManager.setFocused(false); @@ -95,13 +101,15 @@ describe('Channel pages', () => { onlineManager.setOnline(true); await jest.advanceTimersByTimeAsync(1); }); - expect(fetchMock).toHaveBeenCalledTimes(2); + expect(fetchMock).toHaveBeenCalledTimes(3); fireEvent.click(screen.getByRole('button', { name: 'Refresh channels' })); - await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(4)); - expect(fetchMock.mock.calls.slice(2).map(([input]) => input)).toEqual([ - '/api/channels/registrations', - statusPath, - ]); + await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(6)); + expect( + fetchMock.mock.calls + .slice(3) + .map(([input]) => input) + .sort(), + ).toEqual(['/api/channels/registrations', statusPath, botsPath].sort()); view.unmount(); } finally { focusManager.setFocused(undefined); @@ -112,12 +120,46 @@ describe('Channel pages', () => { it('loads the connected table, exact Ornn skill link and first-level navigation without enabling unsupported platforms', async () => { const pendingStatus = deferred(); + const pendingNames = deferred(); fetchMock.mockImplementation(async (input) => - input === statusPath ? pendingStatus.promise : response([registration]), + input === botsPath + ? pendingNames.promise + : input === statusPath + ? pendingStatus.promise + : response([registration]), ); renderWithQueryClient(); const table = await screen.findByRole('table', { name: /^Connected/ }); expect(within(table).getByText('Checking…')).toBeInTheDocument(); + expect( + within(table).getByRole('columnheader', { name: 'Channel name' }), + ).toBeInTheDocument(); + expect( + within(table).getByRole('columnheader', { name: 'Channel', exact: true }), + ).toBeInTheDocument(); + expect(within(table).getByText('Loading name…')).toBeInTheDocument(); + await act(async () => + pendingNames.resolve( + response({ + bots: [ + { + ...bot, + id: registration.id, + label: 'Wrong registration ID match', + }, + { ...bot, id: 'unrelated-bot', label: 'Unrelated bot' }, + bot, + ], + }), + ), + ); + expect(await within(table).findByText('Team channel')).toBeInTheDocument(); + expect( + within(table).getByRole('cell', { name: 'Telegram', exact: true }), + ).toBeInTheDocument(); + expect( + within(table).queryByText(/Wrong registration|Unrelated bot/), + ).not.toBeInTheDocument(); expect( screen.getByRole('link', { name: 'Open review.skill in Ornn' }), ).toHaveAttribute('href', 'https://ornn.chrono-ai.fun/skills/review.skill'); @@ -160,9 +202,11 @@ describe('Channel pages', () => { it('keeps status failure distinct from an active bot and shows missing skills without a fabricated version', async () => { fetchMock.mockImplementation(async (input) => - input === statusPath - ? response({}, 502) - : response([{ ...registration, default_skill: null }]), + input === botsPath + ? response({ bots: [bot] }) + : input === statusPath + ? response({}, 502) + : response([{ ...registration, default_skill: null }]), ); renderWithQueryClient(); const table = await screen.findByRole('table'); @@ -172,6 +216,46 @@ describe('Channel pages', () => { expect(within(table).getByRole('link', { name: 'Manage' })).toBeEnabled(); }); + it('preserves the connected row when names fail, shows one toast and recovers on manual refresh', async () => { + let namesAvailable = false; + fetchMock.mockImplementation(async (input) => { + if (input === botsPath) + return namesAvailable ? response({ bots: [bot] }) : response({}, 503); + if (input === statusPath) + return response({ registration_id: registration.id, status: 'active' }); + return response([registration]); + }); + renderWithQueryClient(); + await screen.findByText('Name unavailable'); + await waitFor(() => expect(mockToast.error).toHaveBeenCalledTimes(1)); + expect(screen.getByText('bot-alpha')).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'Manage' })).toBeEnabled(); + namesAvailable = true; + fireEvent.click(screen.getByRole('button', { name: 'Refresh channels' })); + expect(await screen.findByText('Team channel')).toBeInTheDocument(); + expect(mockToast.error).toHaveBeenCalledTimes(1); + }); + + it('does not use a mismatched platform or a registration ID as a bot label match', async () => { + fetchMock.mockImplementation(async (input) => { + if (input === botsPath) + return response({ + bots: [ + { ...bot, platform: 'lark', label: 'Wrong platform' }, + { ...bot, id: registration.id, label: 'Wrong identity' }, + ], + }); + if (input === statusPath) + return response({ registration_id: registration.id, status: 'active' }); + return response([registration]); + }); + renderWithQueryClient(); + expect(await screen.findByText('Name unavailable')).toBeInTheDocument(); + expect( + screen.queryByText(/Wrong platform|Wrong identity/), + ).not.toBeInTheDocument(); + }); + it('recovers a failed collection query into a genuine empty state', async () => { fetchMock .mockResolvedValueOnce(response({}, 503)) @@ -251,6 +335,10 @@ describe('Channel pages', () => { expect(screen.queryByText('Scope')).not.toBeInTheDocument(); expect(screen.queryByText('scope-alpha')).not.toBeInTheDocument(); expect(screen.getByText('telegram-provider')).toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Open review.skill in Ornn' }), + ).toHaveAttribute('href', 'https://ornn.chrono-ai.fun/skills/review.skill'); + expect(screen.getByText('Version 2.4')).toBeInTheDocument(); expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); expect(document.body).not.toHaveTextContent('example.invalid'); expect( @@ -276,6 +364,7 @@ describe('Channel pages', () => { ...registration, nyx_channel_bot_id: null, nyx_agent_api_key_id: null, + default_skill: null, }, ]), ); @@ -289,6 +378,7 @@ describe('Channel pages', () => { name: 'Channel details', }); expect(within(details).getAllByText('—')).toHaveLength(2); + expect(within(details).getByText('Not set')).toBeInTheDocument(); expect(within(details).queryByRole('link')).not.toBeInTheDocument(); }); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 44305e7315..5eefc13571 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -22,7 +22,12 @@ import { InboundStatus, platformName, } from './presentation'; -import { useChannelRegistrations, useChannelStatus } from './queries'; +import { + channelKeys, + useChannelBotIdentities, + useChannelRegistrations, + useChannelStatus, +} from './queries'; import { channelsCss } from './styles'; const platforms = ['telegram', 'whatsapp'] as const; @@ -30,15 +35,26 @@ const platforms = ['telegram', 'whatsapp'] as const; function ConnectedRow({ registration, scopeId, + label, + namePending, }: { readonly registration: ChannelRegistration; readonly scopeId: string; + readonly label: string | null; + readonly namePending: boolean; }) { const status = useChannelStatus(scopeId, registration.id); return ( + + + - + {platformName(registration.platform)} @@ -77,9 +93,36 @@ export default function ChannelsPage({ readonly scopeId: string; }) { const registrations = useChannelRegistrations(scopeId); + const hasBotIds = Boolean(registrations.data?.some((row) => row.botId)); + const bots = useChannelBotIdentities(scopeId, hasBotIds); const toast = useConsoleToast(); const queryClient = useQueryClient(); const [refreshing, setRefreshing] = React.useState(false); + const reportedNamesError = React.useRef(0); + React.useEffect(() => { + if ( + refreshing || + !bots.isError || + bots.isFetching || + bots.errorUpdatedAt === reportedNamesError.current + ) + return; + reportedNamesError.current = bots.errorUpdatedAt; + if (!registrations.isError) + toast.error( + t( + 'channels.error.names', + 'Could not load channel names. Use Refresh to try again.', + ), + ); + }, [ + bots.errorUpdatedAt, + bots.isError, + bots.isFetching, + refreshing, + registrations.isError, + toast, + ]); async function refresh() { setRefreshing(true); try { @@ -89,6 +132,10 @@ export default function ChannelsPage({ queryKey: ['channels', scopeId, 'status'], type: 'active', }), + queryClient.refetchQueries({ + queryKey: channelKeys.bots(scopeId), + type: 'active', + }), ]); if (result.isError) toast.error( @@ -222,6 +269,7 @@ export default function ChannelsPage({ {[ + ['name', 'Channel name'], ['channel', 'Channel'], ['skill', 'Skill'], ['inbound', 'Inbound'], @@ -240,6 +288,15 @@ export default function ChannelsPage({ key={registration.id} registration={registration} scopeId={scopeId} + label={ + bots.data?.find( + (bot) => + bot.id === registration.botId && + bot.platform.toLowerCase() === + registration.platform.toLowerCase(), + )?.label ?? null + } + namePending={Boolean(registration.botId) && bots.isPending} /> ))} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index cee597f38d..2811d341eb 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -46,16 +46,13 @@ const service = { }; const servicePath = 'https://nyx.example.test/api/v1/user-services'; const listPath = '/api/channels/registrations'; -function enterNames() { +function enterChannelName() { fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: 'TEST_ONLY_TOKEN' }, }); - fireEvent.change(screen.getByLabelText(/^Label/), { + fireEvent.change(screen.getByLabelText(/^Channel name/), { target: { value: 'My team bot' }, }); - fireEvent.change(screen.getByLabelText(/^Skill name/), { - target: { value: 'review.skill' }, - }); } beforeEach(() => { fetchMock.mockReset(); @@ -108,7 +105,7 @@ it('reads registrations only after acceptance or Check again, never while idle o await screen.findByText(/No services are available/); await idleAndReturn(); expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); - enterNames(); + enterChannelName(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( 'Could not confirm the connection. Please try again.', @@ -217,7 +214,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits screen.queryByRole('checkbox', { name: /GitHub work/ }), ).not.toBeInTheDocument(); expect(screen.getByText('2 selected')).toBeInTheDocument(); - enterNames(); + enterChannelName(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); expect( @@ -232,7 +229,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', label: 'My team bot', - default_skill_name: 'review.skill', + default_skill_name: 'My team bot', authorization_mode: 'explicit_service_allowlist', service_ids: ['user-service-github', 'user-service-model'], webhook_base_url: 'https://aevatar-console-backend-api.aevatar.ai', @@ -386,12 +383,12 @@ it('recovers inventory failure and defaults optional names with an explicitly em expect(fetchMock.mock.calls.some(([, init]) => init?.method === 'POST')).toBe( false, ); - expect(screen.getByLabelText('Label (optional)')).not.toBeRequired(); - expect(screen.getByLabelText('Skill name (optional)')).not.toBeRequired(); + expect(screen.getByLabelText('Channel name (optional)')).not.toBeRequired(); + expect(screen.queryByLabelText(/^Skill name/)).not.toBeInTheDocument(); fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: telegramToken }, }); - fireEvent.change(screen.getByLabelText('Label (optional)'), { + fireEvent.change(screen.getByLabelText('Channel name (optional)'), { target: { value: ' ' }, }); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); @@ -451,23 +448,19 @@ it('shows a name-lookup toast and retries with the current token and custom fiel fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: updatedToken }, }); - fireEvent.change(screen.getByLabelText(/^Label/), { + fireEvent.change(screen.getByLabelText(/^Channel name/), { target: { value: 'Custom label' }, }); - telegramFetch.mockResolvedValueOnce( - response({ ok: true, result: { is_bot: true, first_name: 'Current Bot' } }), - ); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText(/Telegram setup was submitted/); expect(telegramFetch.mock.calls.map(([url]) => url)).toEqual([ `https://api.telegram.org/bot${telegramToken}/getMe`, - `https://api.telegram.org/bot${updatedToken}/getMe`, ]); const post = fetchMock.mock.calls.find(([, init]) => init?.method === 'POST'); expect(JSON.parse(String(post?.[1]?.body))).toMatchObject({ bot_token: updatedToken, label: 'Custom label', - default_skill_name: 'Current Bot', + default_skill_name: 'Custom label', }); }); @@ -504,7 +497,7 @@ it.each([ }); renderWithQueryClient(); fireEvent.click(await screen.findByRole('checkbox', { name: 'Select all' })); - enterNames(); + enterChannelName(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); expect(await screen.findByRole('alert')).toHaveTextContent( 'Could not confirm the connection. Please try again.', @@ -512,8 +505,7 @@ it.each([ const form = screen.getByRole('form', { name: 'Connect Telegram' }); expect(within(form).queryByRole('alert')).not.toBeInTheDocument(); expect(screen.getByLabelText(/^Bot token/)).toHaveValue('TEST_ONLY_TOKEN'); - expect(screen.getByLabelText(/^Label/)).toHaveValue('My team bot'); - expect(screen.getByLabelText(/^Skill name/)).toHaveValue('review.skill'); + expect(screen.getByLabelText(/^Channel name/)).toHaveValue('My team bot'); expect(screen.getByRole('checkbox', { name: 'Select all' })).toBeChecked(); expect(screen.getByRole('checkbox', { name: 'Select all' })).toBeEnabled(); expect( @@ -557,7 +549,7 @@ it('removes revoked selections after a rejected submission and allows a retry wi }); renderWithQueryClient(); fireEvent.click(await screen.findByRole('checkbox', { name: /GitHub work/ })); - enterNames(); + enterChannelName(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( 'The selected services are no longer available. Review your selection and try again.', @@ -595,14 +587,14 @@ it('removes revoked selections after a rejected submission and allows a retry wi it('confirms discarding an edited setup without issuing a registration request', async () => { renderWithQueryClient(); await screen.findByRole('checkbox', { name: /GitHub work/ }); - enterNames(); + enterChannelName(); fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); const dialog = await screen.findByRole('dialog', { name: 'Discard this connection setup?', }); expect(history.push).not.toHaveBeenCalled(); fireEvent.click(within(dialog).getByRole('button', { name: 'Stay' })); - expect(screen.getByLabelText(/^Label/)).toHaveValue('My team bot'); + expect(screen.getByLabelText(/^Channel name/)).toHaveValue('My team bot'); fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); fireEvent.click( within(await screen.findByRole('dialog')).getByRole('button', { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index c67e81f7cb..94a38e3ac2 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -43,7 +43,7 @@ function registrationErrorMessage(reason: ChannelRegistrationFailure) { ], skill: [ 'channels.connect.error.skill', - 'The Skill name could not be used. Check the name and your access in Ornn.', + 'Could not configure the bot skill. Check the Channel name and your access in Ornn.', ], authorization: [ 'channels.connect.error.authorization', @@ -59,7 +59,7 @@ function registrationErrorMessage(reason: ChannelRegistrationFailure) { ], rejected: [ 'channels.connect.error.rejected', - 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', + 'Could not connect Telegram. Check the bot token, Channel name, and selected services, then try again.', ], uncertain: [ 'channels.connect.error.uncertain', @@ -78,8 +78,7 @@ export default function TelegramConnectionPage({ // Secret input stays in this component only, never in Query mutation state, // a persisted draft, navigation URL, or navigation state. const [botToken, setBotToken] = React.useState(''); - const [label, setLabel] = React.useState(''); - const [skillName, setSkillName] = React.useState(''); + const [channelName, setChannelName] = React.useState(''); const [selectedIds, setSelectedIds] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); const [submittedId, setSubmittedId] = React.useState(null); @@ -109,7 +108,7 @@ export default function TelegramConnectionPage({ ), ); const locked = submitting || submittedId !== null; - const dirty = Boolean(botToken || label || skillName || selectedIds.length); + const dirty = Boolean(botToken || channelName || selectedIds.length); React.useEffect(() => { if (!services.isSuccess || services.isFetching) return; @@ -187,8 +186,7 @@ export default function TelegramConnectionPage({ try { const receipt = await channelsApi.registerTelegram({ botToken, - label, - skillName, + label: channelName, serviceIds: selectedIds, webhookBaseUrl, }); @@ -312,42 +310,23 @@ export default function TelegramConnectionPage({

) : null}
-
- {[ - { - id: 'telegram-label', - key: 'label', - title: 'Label', - value: label, - change: setLabel, - }, - { - id: 'telegram-skill', - key: 'skillName', - title: 'Skill name', - value: skillName, - change: setSkillName, - }, - ].map((field) => ( -
-
- -
- field.change(event.target.value)} - /> -
- ))} +
+
+ +
+ setChannelName(event.target.value)} + />
span { color: var(--wa-blue); font-size: 11px; } .channels__service-picker { border: 1px solid var(--channels-border); border-radius: 5px; margin-top: 12px; padding: 10px 10px 0; } @@ -49,7 +48,6 @@ export const channelConnectionCss = ` .channels__connect-heading h1 { font-size: 24px; line-height: 32px; } .channels__connect-heading { margin-bottom: 20px; } .channels__connection-form { padding: 20px; } - .channels__name-fields { gap: 0; grid-template-columns: minmax(0, 1fr); } .channels__service-option { align-items: flex-start; gap: 8px; padding-inline: 2px; } .channels__service-bulk { padding-inline: 2px; } .channels__service-source { flex-basis: 80px; padding-top: 3px; } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx index aca6fff58a..9bd0be3b7f 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx @@ -88,8 +88,12 @@ export function compactChannelIdentifier(value: string): string { export function ChannelIdentity({ registration, + label, + pending, }: { readonly registration: ChannelRegistration; + readonly label: string | null; + readonly pending: boolean; }) { const identifier = registration.botId ?? registration.id; return ( @@ -97,9 +101,10 @@ export function ChannelIdentity({
- {t('channels.bot', '{platform} bot', { - platform: platformName(registration.platform), - })} + {label ?? + (pending + ? t('channels.name.loading', 'Loading name…') + : t('channels.name.unavailable', 'Name unavailable'))} {compactChannelIdentifier(identifier)} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts index e69447814f..8245fbb8d9 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts @@ -1,12 +1,27 @@ import { useQuery } from '@tanstack/react-query'; +import { listChannelBotIdentities } from '@/shared/api/channelBotsApi'; import { channelsApi } from '@/shared/api/channelsApi'; export const channelKeys = { list: (scopeId: string) => ['channels', scopeId, 'registrations'] as const, + bots: (scopeId: string) => ['channels', scopeId, 'bot-identities'] as const, status: (scopeId: string, registrationId: string) => ['channels', scopeId, 'status', registrationId] as const, }; +export function useChannelBotIdentities(scopeId: string, enabled: boolean) { + return useQuery({ + queryKey: channelKeys.bots(scopeId), + queryFn: ({ signal }) => listChannelBotIdentities(signal), + enabled: Boolean(scopeId) && enabled, + retry: false, + staleTime: 0, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchInterval: false, + }); +} + export function useChannelRegistrations(scopeId: string, enabled = true) { return useQuery({ queryKey: channelKeys.list(scopeId), diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index e346260659..e78cc19021 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -31,9 +31,10 @@ export const channelsCss = ` .channels__table { border-collapse: collapse; table-layout: fixed; width: 100%; } .channels__table th { background: var(--wa-subtle); border-bottom: 1px solid var(--channels-border); color: var(--wa-muted); font-size: 10px; font-weight: 600; line-height: 16px; padding: 12px 16px; text-align: left; text-transform: uppercase; } .channels__table th:nth-child(1) { width: auto; } -.channels__table th:nth-child(2) { width: 25%; } -.channels__table th:nth-child(3) { width: 16%; } -.channels__table th:nth-child(4) { width: 19%; } +.channels__table th:nth-child(2) { width: 12%; } +.channels__table th:nth-child(3) { width: 22%; } +.channels__table th:nth-child(4) { width: 13%; } +.channels__table th:nth-child(5) { width: 15%; } .channels__table th:last-child { text-align: right; width: 128px; } .channels__table td { border-bottom: 1px solid var(--channels-border); height: 92px; overflow-wrap: anywhere; padding: 16px; vertical-align: middle; } .channels__table tr:last-child td { border-bottom: 0; } @@ -89,9 +90,10 @@ export const channelsCss = ` .channels__platforms { grid-template-columns: repeat(2, minmax(0, 1fr)); } .channels__table th, .channels__table td { padding-left: 12px; padding-right: 12px; } .channels__table th:nth-child(1) { width: auto; } - .channels__table th:nth-child(2) { width: 22%; } - .channels__table th:nth-child(3) { width: 18%; } - .channels__table th:nth-child(4) { width: 20%; } + .channels__table th:nth-child(2) { width: 12%; } + .channels__table th:nth-child(3) { width: 20%; } + .channels__table th:nth-child(4) { width: 14%; } + .channels__table th:nth-child(5) { width: 16%; } .channels__table th:last-child { width: 120px; } } @media (max-width: 767px) { diff --git a/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts new file mode 100644 index 0000000000..bdc65936f0 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts @@ -0,0 +1,58 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { listChannelBotIdentities } from './channelBotsApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), +})); +const fetchMock = jest.mocked(authFetch); +const response = (value: unknown, status = 200) => + ({ ok: status === 200, status, json: async () => value }) as Response; + +it('reads personal bot labels in one request and keeps only safe identity fields', async () => { + fetchMock.mockResolvedValue( + response({ + bots: [ + { + id: 'bot-alpha', + platform: 'telegram', + label: ' Team channel ', + platform_bot_id: 'different-platform-id', + access_token: 'TEST_ONLY_SECRET', + webhook_url: 'https://example.invalid/private', + }, + { id: 'bot-beta', platform: 'lark', label: ' ' }, + ], + total: 2, + }), + ); + const signal = new AbortController().signal; + expect(await listChannelBotIdentities(signal)).toEqual([ + { id: 'bot-alpha', platform: 'telegram', label: 'Team channel' }, + { id: 'bot-beta', platform: 'lark', label: null }, + ]); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith( + 'https://nyx.example.test/api/v1/channel-bots', + { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { Accept: 'application/json' }, + }, + ); +}); + +it('rejects ambiguous identities and HTTP failures without retaining error bodies', async () => { + const bot = { id: 'bot-alpha', platform: 'telegram', label: 'Team channel' }; + fetchMock.mockResolvedValue(response({ bots: [bot, bot] })); + await expect(listChannelBotIdentities()).rejects.toThrow('Ambiguous'); + const json = jest.fn().mockResolvedValue({ token: 'TEST_ONLY_SECRET' }); + fetchMock.mockResolvedValue({ + ok: false, + status: 403, + json, + } as unknown as Response); + await expect(listChannelBotIdentities()).rejects.toThrow('403'); + expect(json).not.toHaveBeenCalled(); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts b/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts new file mode 100644 index 0000000000..5440db001b --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts @@ -0,0 +1,43 @@ +import { getNyxIDRuntimeConfig } from '@/shared/auth/config'; +import { authFetch } from '@/shared/auth/fetch'; +import { ChannelApiError } from './channelsApi'; +import { expectArray, expectRecord, readString } from './http/decoders'; + +export interface ChannelBotIdentity { + readonly id: string; + readonly platform: string; + readonly label: string | null; +} + +export async function listChannelBotIdentities( + signal?: AbortSignal, +): Promise { + const config = getNyxIDRuntimeConfig(); + if (config.configurationError || !config.baseUrl) + throw new Error('NyxID is unavailable.'); + // Omitting org_id reads only the authenticated owner's personal bots. + const response = await authFetch(`${config.baseUrl}/api/v1/channel-bots`, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { Accept: 'application/json' }, + }); + if (!response.ok) throw new ChannelApiError(response.status); + const body = expectRecord(await response.json(), 'Channel bots'); + const bots = expectArray(body.bots, 'Channel bots', (value) => { + const bot = expectRecord(value, 'Channel bot'); + const id = readString(bot, 'id', 'Channel bot ID'); + const platform = readString(bot, 'platform', 'Channel bot platform'); + if (!id.trim() || !platform.trim()) + throw new Error('Missing channel bot identity.'); + // Keep only safe identity fields, never the full upstream object. + return { + id, + platform, + label: readString(bot, 'label', 'Channel bot label').trim() || null, + }; + }); + if (new Set(bots.map((bot) => bot.id)).size !== bots.length) + throw new Error('Ambiguous channel bot identity.'); + return bots; +} diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts index 5f3c17262b..07f2dc931a 100644 --- a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -39,7 +39,6 @@ it('preserves an explicit empty allowlist and retains only the accepted registra await channelsApi.registerTelegram({ botToken: ' TEST_ONLY_TOKEN ', label: ' Label ', - skillName: ' Skill ', serviceIds: [], webhookBaseUrl: 'https://api.example.test', }), @@ -50,7 +49,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', label: 'Label', - default_skill_name: 'Skill', + default_skill_name: 'Label', authorization_mode: 'explicit_service_allowlist', service_ids: [], webhook_base_url: 'https://api.example.test', @@ -65,7 +64,6 @@ it('preserves an explicit empty allowlist and retains only the accepted registra channelsApi.registerTelegram({ botToken: 'TEST_ONLY_TOKEN', label: 'Label', - skillName: 'Skill', serviceIds: [], webhookBaseUrl: 'https://api.example.test', }), @@ -73,30 +71,9 @@ it('preserves an explicit empty allowlist and retains only the accepted registra }); it.each([ - { - label: undefined, - skillName: undefined, - expectedLabel: 'My Bot', - expectedSkill: 'My Bot', - }, - { - label: ' Custom label ', - skillName: ' ', - expectedLabel: 'Custom label', - expectedSkill: 'My Bot', - }, - { - label: ' ', - skillName: ' Custom skill ', - expectedLabel: 'My Bot', - expectedSkill: 'Custom skill', - }, -])('defaults only blank names from Telegram first_name: $expectedLabel / $expectedSkill', async ({ - label, - skillName, - expectedLabel, - expectedSkill, -}) => { + undefined, + ' ', +])('uses Telegram first_name for both backend names when the channel name is blank (%s)', async (label) => { const token = '123456:TEST_ONLY_TOKEN'; telegramFetch.mockResolvedValue( response({ @@ -117,7 +94,6 @@ it.each([ await channelsApi.registerTelegram({ botToken: ` ${token} `, label, - skillName, serviceIds: ['user-service-a'], webhookBaseUrl: 'https://api.example.test', }); @@ -134,8 +110,8 @@ it.each([ }, ); expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toMatchObject({ - label: expectedLabel, - default_skill_name: expectedSkill, + label: 'My Bot', + default_skill_name: 'My Bot', bot_token: token, service_ids: ['user-service-a'], }); diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 2151244e0c..7a5ab25de2 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -58,7 +58,6 @@ export class ChannelRegistrationError extends Error { export interface TelegramRegistrationInput { readonly botToken: string; readonly label?: string; - readonly skillName?: string; readonly serviceIds: readonly string[]; readonly webhookBaseUrl: string; } @@ -199,9 +198,7 @@ export const channelsApi = { ): Promise<{ readonly registrationId: string }> { const botToken = input.botToken.trim(); const label = input.label?.trim() ?? ''; - const skillName = input.skillName?.trim() ?? ''; - const botName = - !label || !skillName ? await readTelegramBotName(botToken) : ''; + const channelName = label || (await readTelegramBotName(botToken)); // This request carries the token only in the authenticated POST body. // Do not use a mutation cache, navigation state, or persistent draft. let response: Response; @@ -215,8 +212,8 @@ export const channelsApi = { body: JSON.stringify({ platform: 'telegram', bot_token: botToken, - label: label || botName, - default_skill_name: skillName || botName, + label: channelName, + default_skill_name: channelName, webhook_base_url: input.webhookBaseUrl, authorization_mode: 'explicit_service_allowlist', service_ids: [...input.serviceIds], From f9ffe12ea1a5f687205798f66caf31a143ab80e9 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 14:48:39 +0800 Subject: [PATCH 139/167] fix: Use supported role query options in channel tests --- .../workflow-activity-vnext/channels/ChannelsPage.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index d7d939157a..ba79f62981 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -135,7 +135,7 @@ describe('Channel pages', () => { within(table).getByRole('columnheader', { name: 'Channel name' }), ).toBeInTheDocument(); expect( - within(table).getByRole('columnheader', { name: 'Channel', exact: true }), + within(table).getByRole('columnheader', { name: 'Channel' }), ).toBeInTheDocument(); expect(within(table).getByText('Loading name…')).toBeInTheDocument(); await act(async () => @@ -155,7 +155,7 @@ describe('Channel pages', () => { ); expect(await within(table).findByText('Team channel')).toBeInTheDocument(); expect( - within(table).getByRole('cell', { name: 'Telegram', exact: true }), + within(table).getByRole('cell', { name: 'Telegram' }), ).toBeInTheDocument(); expect( within(table).queryByText(/Wrong registration|Unrelated bot/), From a8729430afd96af116ef5e04cf62cd4eccba8210 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:15:57 +0800 Subject: [PATCH 140/167] fix: Reveal complete channel identifiers in tooltips --- .../docs/features/2026-09-14-channel-pages.md | 6 ++++ .../src/locales/channelMessages.en-US.ts | 1 + .../src/locales/channelMessages.zh-CN.ts | 1 + .../channels/ChannelIdentity.test.tsx | 36 +++++++++++++++++++ .../channels/presentation.tsx | 13 +++++-- .../channels/styles.ts | 3 ++ 6 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 1357ea475d..44550d12dc 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -22,6 +22,12 @@ The Figma `/keys/xxx` placeholder refers to NyxID service details, so it is not used for Agent keys. Each complete source ID is encoded as one URL segment; missing IDs remain unlinked placeholders. +The compact identifier in each connected-channel row opens the shared Tooltip +with the exact full bot ID (or registration ID when no bot ID exists). Mouse +hover, keyboard focus and click/tap reveal the full value, which wraps within +the Tooltip instead of being shortened again. The control has a visible focus +ring. + ## Routes and existing setup - `/scopes/:scopeId/workflow-activity-vnext/channels`: platform directory and diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 3ee8979b9b..2f202b6140 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -23,6 +23,7 @@ export default { 'channels.skill.notSet': 'Not set', 'channels.skill.version': 'Version {version}', 'channels.skill.open': 'Open {name} in Ornn', + 'channels.identifier.showFull': 'Show full ID', 'channels.column.channel': 'Channel', 'channels.column.skill': 'Skill', 'channels.column.inbound': 'Inbound', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index b44b48311d..cd8ecc7dd4 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -21,6 +21,7 @@ export default { 'channels.skill.notSet': '未设置', 'channels.skill.version': '版本 {version}', 'channels.skill.open': '在 Ornn 中打开 {name}', + 'channels.identifier.showFull': '查看完整 ID', 'channels.column.channel': '渠道', 'channels.column.skill': '技能', 'channels.column.inbound': '消息入站', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx new file mode 100644 index 0000000000..60f862291a --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx @@ -0,0 +1,36 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import * as React from 'react'; +import type { ChannelRegistration } from '@/shared/api/channelsApi'; +import { ChannelIdentity } from './presentation'; + +describe('Channel identifier disclosure', () => { + it.each([ + 'bot', + 'registration', + ] as const)('reveals the complete %s ID from the compact identifier', async (identityKind) => { + const fullId = + identityKind === 'bot' + ? 'bot-alpha-1234567890-1234567890-complete' + : 'registration-alpha-1234567890-1234567890-complete'; + const registration: ChannelRegistration = { + id: identityKind === 'registration' ? fullId : 'registration-alpha', + botId: identityKind === 'bot' ? fullId : null, + platform: 'telegram', + scopeId: 'scope-alpha', + providerSlug: null, + agentKeyId: null, + skill: null, + workflowDeliveryStatus: null, + owned: true, + }; + render(); + const identifier = screen.getByRole('button', { name: 'Show full ID' }); + expect(identifier).not.toHaveTextContent(fullId); + expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); + + if (identityKind === 'bot') fireEvent.mouseEnter(identifier); + else fireEvent.focus(identifier); + + expect(await screen.findByRole('tooltip')).toHaveTextContent(fullId); + }); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx index 9846986b7a..2c0477ce82 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx @@ -13,6 +13,7 @@ import { import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { getOrnnRuntimeConfig } from '@/shared/studio/ornnConfig'; +import AevatarTooltip from '@/shared/ui/AevatarTooltip'; export function ChannelLink({ href, @@ -96,9 +97,15 @@ export function ChannelIdentity({ platform: platformName(registration.platform), })} - - {compactChannelIdentifier(identifier)} - + + +
); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index d483fa59d8..0fc3cc224c 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -43,6 +43,9 @@ export const channelsCss = ` .channels__identity-copy strong { font-size: 13px; font-weight: 600; } .channels__identifier { color: var(--wa-muted); font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace; font-size: 11px; overflow-wrap: anywhere; } .channels__identity .channels__identifier { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.channels__identifier-trigger { align-self: flex-start; background: transparent; border: 0; border-radius: 3px; cursor: help; max-width: 100%; padding: 0; text-align: left; } +.channels__identifier-trigger:hover { color: var(--wa-ink); } +.channels__identifier-trigger:focus-visible { outline: 2px solid var(--wa-blue); outline-offset: 3px; } .channels__skill { display: flex; flex-direction: column; gap: 6px; overflow-wrap: anywhere; } .channels__skill a { color: var(--wa-blue); font-weight: 500; } .channels__skill .anticon { font-size: 10px; } From 9ba0709959a86be5826453f6085e7f3d846d4bd7 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:03:51 +0800 Subject: [PATCH 141/167] test: Keep channel tooltip fixture compatible with naming changes --- .../channels/ChannelIdentity.test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx index 60f862291a..9d0e9b022a 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx @@ -23,7 +23,12 @@ describe('Channel identifier disclosure', () => { workflowDeliveryStatus: null, owned: true, }; - render(); + const identityProps = { + registration, + label: 'Channel for ID test', + pending: false, + }; + render(); const identifier = screen.getByRole('button', { name: 'Show full ID' }); expect(identifier).not.toHaveTextContent(fullId); expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); From 6e3b47e4d9f34a6e7b065c1438da0c679c18f857 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:28:12 +0800 Subject: [PATCH 142/167] fix: Add six random digits to default channel names --- .../docs/features/2026-09-14-channel-pages.md | 5 ++++- .../src/locales/channelMessages.en-US.ts | 3 ++- .../src/locales/channelMessages.zh-CN.ts | 2 +- .../channels/TelegramConnectionPage.test.tsx | 17 ++++++++--------- .../channels/TelegramConnectionPage.tsx | 2 +- .../shared/api/channelConnectionApi.test.ts | 19 ++++++++++++++----- .../src/shared/api/channelsApi.ts | 4 +++- 7 files changed, 33 insertions(+), 19 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index 3c73c9c754..8919a59ffa 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -132,7 +132,10 @@ The optional Channel name is the only naming decision. The registration adapter trims it and sends the same resolved value as both `label` and `default_skill_name`. A nonblank custom name bypasses Telegram name lookup. When blank or omitted, the adapter calls Telegram's official `getMe` endpoint -once and uses the bot's `first_name`, never its `username`, for both fields. +once and appends a hyphen plus six random digits to the bot's trimmed +`first_name`, never its `username` (for example, `My Bot-482731`). The suffix is +generated in the frontend once per submission, in the range 100000–999999, and +the same generated name is sent in both fields. Custom names receive no suffix. The form locks during lookup/submission and preserves its input after failure; a manual retry resolves the current token/name again. diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 27e822ebfa..a12e3f75d9 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -84,7 +84,8 @@ export default { 'channels.connect.botFather': 'Get token from BotFather', 'channels.connect.name': 'Channel name', 'channels.connect.optional': '(optional)', - 'channels.connect.botNameDefault': 'Defaults to the Telegram bot name', + 'channels.connect.botNameDefault': + 'Defaults to the bot name + 6 random digits', 'channels.connect.tokenRequired': 'Enter the bot token from BotFather.', 'channels.connect.services': 'Services', 'channels.connect.selected': '{count} selected', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 02d5e7637e..32e316006c 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -77,7 +77,7 @@ export default { 'channels.connect.botFather': '从 BotFather 获取令牌', 'channels.connect.name': 'Channel 名字', 'channels.connect.optional': '(可选)', - 'channels.connect.botNameDefault': '默认使用 Telegram 机器人名称', + 'channels.connect.botNameDefault': '默认使用机器人名字 + 6 位随机数字', 'channels.connect.tokenRequired': '请输入从 BotFather 获取的机器人令牌。', 'channels.connect.services': '服务', 'channels.connect.selected': '已选 {count} 项', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index 2811d341eb..78111e5770 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -397,16 +397,15 @@ it('recovers inventory failure and defaults optional names with an explicitly em 'Check the bot token from BotFather and try again.', ), ).toBeInTheDocument(); - expect( - JSON.parse( - String( - fetchMock.mock.calls.find(([, init]) => init?.method === 'POST')?.[1] - ?.body, - ), + const payload = JSON.parse( + String( + fetchMock.mock.calls.find(([, init]) => init?.method === 'POST')?.[1] + ?.body, ), - ).toMatchObject({ - label: 'My Telegram Bot', - default_skill_name: 'My Telegram Bot', + ); + expect(payload).toMatchObject({ + label: expect.stringMatching(/^My Telegram Bot-[1-9]\d{5}$/), + default_skill_name: payload.label, service_ids: [], }); expect(screen.getByLabelText(/^Bot token/)).toHaveValue(telegramToken); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index 94a38e3ac2..efdea8dc64 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -322,7 +322,7 @@ export default function TelegramConnectionPage({ value={channelName} placeholder={t( 'channels.connect.botNameDefault', - 'Defaults to the Telegram bot name', + 'Defaults to the bot name + 6 random digits', )} disabled={locked} onChange={(event) => setChannelName(event.target.value)} diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts index 07f2dc931a..f556f83200 100644 --- a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -22,9 +22,11 @@ beforeEach(() => { afterEach(() => { fetchMock.mockReset(); global.fetch = originalFetch; + jest.restoreAllMocks(); }); it('preserves an explicit empty allowlist and retains only the accepted registration identity', async () => { + const random = jest.spyOn(Math, 'random'); fetchMock.mockResolvedValue( response( { @@ -45,6 +47,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra ).toEqual({ registrationId: 'registration-new' }); expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); expect(telegramFetch).not.toHaveBeenCalled(); + expect(random).not.toHaveBeenCalled(); expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toEqual({ platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', @@ -71,9 +74,14 @@ it('preserves an explicit empty allowlist and retains only the accepted registra }); it.each([ - undefined, - ' ', -])('uses Telegram first_name for both backend names when the channel name is blank (%s)', async (label) => { + { label: undefined, randomValue: 0, expectedName: 'My Bot-100000' }, + { label: ' ', randomValue: 0.99999999, expectedName: 'My Bot-999999' }, +])('uses the bot name plus six random digits for both backend names when the label is $label', async ({ + label, + randomValue, + expectedName, +}) => { + const random = jest.spyOn(Math, 'random').mockReturnValue(randomValue); const token = '123456:TEST_ONLY_TOKEN'; telegramFetch.mockResolvedValue( response({ @@ -110,11 +118,12 @@ it.each([ }, ); expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toMatchObject({ - label: 'My Bot', - default_skill_name: 'My Bot', + label: expectedName, + default_skill_name: expectedName, bot_token: token, service_ids: ['user-service-a'], }); + expect(random).toHaveBeenCalledTimes(1); }); it('does not register using a username when Telegram omits a usable bot name', async () => { diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 7a5ab25de2..67d8fcc531 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -198,7 +198,9 @@ export const channelsApi = { ): Promise<{ readonly registrationId: string }> { const botToken = input.botToken.trim(); const label = input.label?.trim() ?? ''; - const channelName = label || (await readTelegramBotName(botToken)); + const channelName = + label || + `${await readTelegramBotName(botToken)}-${Math.floor(100_000 + Math.random() * 900_000)}`; // This request carries the token only in the authenticated POST body. // Do not use a mutation cache, navigation state, or persistent draft. let response: Response; From 6ac14004c851c3c65579a1b5171fd803b64371f4 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:35:23 +0800 Subject: [PATCH 143/167] Clarify workflow generation and mutation feedback --- .../2026-09-15-workflow-operation-feedback.md | 57 ++++ .../workflowActivityVNextMessages.en-US.ts | 33 +- .../workflowActivityVNextMessages.zh-CN.ts | 25 +- .../workflow-activity-vnext/index.test.tsx | 59 +++- .../pages/workflow-activity-vnext/styles.ts | 5 +- .../workflows/NewWorkflowPage.test.tsx | 180 ++++++++++- .../workflows/NewWorkflowPage.tsx | 193 ++++++++++- .../workflows/WorkflowMutationDialog.test.tsx | 162 ++++++++++ .../workflows/WorkflowMutationDialog.tsx | 297 +++++++++++++++++ .../workflows/WorkflowsPage.tsx | 304 +++--------------- .../workflows/workflowArchival.ts | 17 +- .../workflows/workflowObservation.ts | 29 ++ .../workflows/workflowRemoval.ts | 17 +- .../shared/studio/api.authorWorkflow.test.ts | 61 ++++ .../src/shared/studio/api.ts | 58 ++-- 15 files changed, 1154 insertions(+), 343 deletions(-) create mode 100644 apps/aevatar-console-web/docs/2026-09-15-workflow-operation-feedback.md create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowObservation.ts create mode 100644 apps/aevatar-console-web/src/shared/studio/api.authorWorkflow.test.ts diff --git a/apps/aevatar-console-web/docs/2026-09-15-workflow-operation-feedback.md b/apps/aevatar-console-web/docs/2026-09-15-workflow-operation-feedback.md new file mode 100644 index 0000000000..6d2795e835 --- /dev/null +++ b/apps/aevatar-console-web/docs/2026-09-15-workflow-operation-feedback.md @@ -0,0 +1,57 @@ +# Workflow generation and change feedback + +The Workflow Activity vNext creation and catalogue surfaces distinguish a +request being accepted from its completed result. + +## Describe + +- Generate and open shows generation, validation, and saving as separate + stages. Real server progress and generated text are available in a bounded, + expandable Generation details section. +- Generation and validation have a two-minute deadline. Cancel generation or + Change method stops waiting and prevents that attempt from creating a draft. + The entered name and description remain available. Leaving the page or scope + invalidates the pending attempt, including a late successful response. +- Once saving starts, cancellation is no longer offered. Draft creation and + accepted-save observation retain their existing completion/recovery contract. +- A stream ending without a nonempty completed workflow is an error, including + an HTTP 200 response containing only a partial YAML document. Only the final + result is parsed and submitted for draft creation. +- Full generation progress still depends on the backend sending events while + it works. Backend issue #3631 tracks buffering in the reference backend. + +## Archive and delete draft + +- The confirmation dialog identifies the selected workflow by name. It blocks + duplicate submission while the command is pending. +- After acceptance, it automatically checks the exact scoped workflow for up + to 30 seconds, including time spent waiting for a read request. Archive uses + the archived catalogue and requires Deactivated; deletion requires the exact + workflow to be absent from the all-workflows catalogue. +- A delayed result remains unconfirmed. A failed check has its own persistent + status and technical details. Neither is labelled as a completed deletion. +- Check again only reads the accepted operation's result. Closing stops active + checks without undoing the submitted request. Reopening the same selected + operation while the page retains it preserves acceptance and offers another + check. Selecting a different operation or leaving the page ends that local + recovery context; a later page visit reloads authoritative catalogue state. +- Only observed completion produces a success toast and refreshes the list. + Old reads cannot close a newer dialog or report stale success. + +## Verification scope + +Nine added regression cases cover real progress and cancellation followed by +retry, timeout during validation, generation unmount, archive completion beyond +the former short window, failed deletion-check recovery, a hung read with +close/reopen and stale completion, final validated SSE output, and truncated SSE +rejection. The ninth case verifies that confirmed deletion remains distinct +from a failed subsequent list refresh, with a read-only recovery action. Existing catalogue, creation, observation, and Studio adapter tests +also remain part of the focused validation set. + +The design continues the Operational Automation Ledger direction and the +17-frame Excalidraw baseline with SHA-256 +`30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de`. +The contract and user-path specifications remain +`docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md` and +`docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md`. +Remote state comes from real APIs and API-acknowledged actions only. diff --git a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts index 3d223c5ae9..e412abc755 100644 --- a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.en-US.ts @@ -1,4 +1,30 @@ const workflowActivityVNextMessages = { + 'workflowActivityVNext.workflows.refreshFailed': + "The workflow list couldn't refresh. The last loaded list is still shown.", + 'workflowActivityVNext.new.cancelGeneration': 'Cancel generation', + 'workflowActivityVNext.new.generating': 'Generating workflow…', + 'workflowActivityVNext.new.validating': 'Checking generated workflow…', + 'workflowActivityVNext.new.savingGenerated': 'Saving workflow…', + 'workflowActivityVNext.new.generationTimedOut': + 'Generation took longer than two minutes. No draft was saved. You can try again.', + 'workflowActivityVNext.new.generationCancelled': + 'Generation cancelled. Your name and description are kept; no draft was saved.', + 'workflowActivityVNext.new.generationReceiving': + 'Generation updates received. Waiting for the completed workflow.', + 'workflowActivityVNext.new.generationWaiting': + 'Waiting for generation updates. You can cancel while you wait.', + 'workflowActivityVNext.new.generationDetails': 'Generation details', + 'workflowActivityVNext.workflows.deleteSuccess': 'Draft deleted', + 'workflowActivityVNext.workflows.checkFailed': + "Couldn't check whether the change has finished", + 'workflowActivityVNext.workflows.changeObserving': + 'Request accepted. Checking for completion…', + 'workflowActivityVNext.workflows.changeDelayed': + 'This is taking longer than expected. Completion is still unconfirmed.', + 'workflowActivityVNext.workflows.checkingDescription': + 'We will keep checking for up to 30 seconds. Closing this dialog does not undo the request.', + 'workflowActivityVNext.workflows.checkAgainDescription': + 'Check again to see the result. This only checks the status and does not submit another request.', 'workflowActivityVNext.arguments.addItem': 'Add item', 'workflowActivityVNext.arguments.addItemAria': 'Add item to {path}', 'workflowActivityVNext.arguments.addProperty': 'Add property', @@ -925,8 +951,6 @@ const workflowActivityVNextMessages = { 'workflowActivityVNext.workflows.archive': 'Archive', 'workflowActivityVNext.workflows.archiveCheckAgain': 'Check again', 'workflowActivityVNext.workflows.archiveConfirm': 'Archive workflow', - 'workflowActivityVNext.workflows.archiveDelayed': - "Archive was accepted, but it hasn't been confirmed yet", 'workflowActivityVNext.workflows.archiveDescription': 'This stops new runs for the published workflow. Its editable draft, published revisions, and Activity history remain available. Publishing it again restores it.', 'workflowActivityVNext.workflows.archiveFailed': @@ -957,11 +981,6 @@ const workflowActivityVNextMessages = { 'This deletes only the editable draft. Published versions and run history remain available.', 'workflowActivityVNext.workflows.deleteDraft': 'Delete draft', 'workflowActivityVNext.workflows.deleteFailed': "Draft couldn't be deleted", - 'workflowActivityVNext.workflows.deleteRefreshFailed': - 'Draft was deleted, but the workflow list could not refresh. Please try again.', - 'workflowActivityVNext.workflows.deleteObservationDelayed': - 'Draft was deleted, but the workflow catalogue has not confirmed its removal yet', - 'workflowActivityVNext.workflows.deleteRetry': 'Try again', 'workflowActivityVNext.workflows.deleteTitle': 'Delete editable draft?', 'workflowActivityVNext.workflows.empty': 'No workflows yet', 'workflowActivityVNext.workflows.emptyDescription': diff --git a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts index ab29257f06..1865eb5962 100644 --- a/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/workflowActivityVNextMessages.zh-CN.ts @@ -3,6 +3,24 @@ import enUSMessages from './workflowActivityVNextMessages.en-US'; const workflowActivityVNextMessages: Record = { ...enUSMessages, + 'workflowActivityVNext.workflows.refreshFailed': '工作流列表刷新失败,当前仍显示上次加载的列表。', + 'workflowActivityVNext.new.cancelGeneration': '取消生成', + 'workflowActivityVNext.new.generating': '正在生成工作流…', + 'workflowActivityVNext.new.validating': '正在校验生成的工作流…', + 'workflowActivityVNext.new.savingGenerated': '正在保存工作流…', + 'workflowActivityVNext.new.generationTimedOut': '生成已超过两分钟,未保存草稿。你可以重试。', + 'workflowActivityVNext.new.generationCancelled': '已取消生成,名称和描述已保留,未保存草稿。', + 'workflowActivityVNext.new.generationReceiving': '已收到生成进度,正在等待完整工作流。', + 'workflowActivityVNext.new.generationWaiting': '正在等待生成进度,你可以随时取消。', + 'workflowActivityVNext.new.generationDetails': '生成详情', + 'workflowActivityVNext.workflows.deleteSuccess': '草稿已删除', + 'workflowActivityVNext.workflows.checkFailed': '暂时无法确认操作是否完成', + 'workflowActivityVNext.workflows.changeObserving': '请求已接受,正在确认是否完成…', + 'workflowActivityVNext.workflows.changeDelayed': '处理时间比预期更长,暂时还无法确认是否完成。', + 'workflowActivityVNext.workflows.checkingDescription': + '将持续检查最多 30 秒。关闭弹窗不会撤销已提交的请求。', + 'workflowActivityVNext.workflows.checkAgainDescription': + '点击“再次检查”查看结果,只检查状态,不会重复提交请求。', 'workflowActivityVNext.arguments.addItem': '添加一项', 'workflowActivityVNext.arguments.addItemAria': '向 {path} 添加一项', 'workflowActivityVNext.arguments.addProperty': '添加属性', @@ -870,8 +888,6 @@ const workflowActivityVNextMessages: Record = 'workflowActivityVNext.workflows.archive': '归档', 'workflowActivityVNext.workflows.archiveCheckAgain': '再次检查', 'workflowActivityVNext.workflows.archiveConfirm': '归档工作流', - 'workflowActivityVNext.workflows.archiveDelayed': - '归档请求已接受,但尚未确认完成', 'workflowActivityVNext.workflows.archiveDescription': '这会停止已发布工作流的新运行。可编辑草稿、已发布修订和活动历史仍会保留。再次发布即可恢复。', 'workflowActivityVNext.workflows.archiveFailed': '无法归档工作流', @@ -900,11 +916,6 @@ const workflowActivityVNextMessages: Record = '此操作只删除可编辑草稿,已发布版本和运行历史会继续保留。', 'workflowActivityVNext.workflows.deleteDraft': '删除草稿', 'workflowActivityVNext.workflows.deleteFailed': '无法删除草稿', - 'workflowActivityVNext.workflows.deleteRefreshFailed': - '草稿已删除,但工作流列表无法刷新,请重试。', - 'workflowActivityVNext.workflows.deleteObservationDelayed': - '草稿已删除,但工作流目录尚未确认其移除', - 'workflowActivityVNext.workflows.deleteRetry': '重试', 'workflowActivityVNext.workflows.deleteTitle': '删除可编辑草稿?', 'workflowActivityVNext.workflows.empty': '暂无工作流', 'workflowActivityVNext.workflows.emptyDescription': diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx index 9495a4a39b..f0acb2fe95 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx @@ -755,7 +755,7 @@ describe('Workflow Activity vNext catalogue', () => { ); }); - it('reports only the specific delete refresh failure after the draft was removed', async () => { + it('reports a failed completion check without claiming the draft was deleted', async () => { mockScopesApi.queryWorkflowCatalogue .mockResolvedValueOnce( createCatalogueResponse([ @@ -783,7 +783,7 @@ describe('Workflow Activity vNext catalogue', () => { await waitFor(() => expect(mockConsoleToast.error).toHaveBeenCalledWith( - 'Draft was deleted, but the workflow list could not refresh. Please try again.', + "Couldn't check whether the change has finished", ), ); expect(mockConsoleToast.error).toHaveBeenCalledTimes(1); @@ -1459,6 +1459,43 @@ describe('Workflow Activity vNext catalogue', () => { expect(mockScopesApi.queryWorkflowCatalogue).toHaveBeenCalledTimes(3); }); + it('keeps confirmed deletion distinct from a failed list refresh and retries the list', async () => { + mockScopesApi.queryWorkflowCatalogue + .mockResolvedValueOnce( + createCatalogueResponse([ + createCatalogueRow({ + workflowId: 'wf-support', + name: 'Support triage', + }), + ]), + ) + .mockResolvedValueOnce(createCatalogueResponse([])) + .mockRejectedValueOnce(new Error('list refresh unavailable')) + .mockResolvedValue(createCatalogueResponse([])); + mockStudioApi.deleteWorkflowDraft.mockResolvedValue(undefined); + renderWithQueryClient(); + const row = (await screen.findByText('Support triage')).closest('tr'); + fireEvent.click( + within(row as HTMLElement).getByRole('button', { + name: 'More actions for Support triage in Workspace', + }), + ); + fireEvent.click( + await screen.findByRole('menuitem', { name: 'Delete draft' }), + ); + fireEvent.click(screen.getByRole('button', { name: 'Delete draft' })); + expect( + await screen.findByText( + "The workflow list couldn't refresh. The last loaded list is still shown.", + ), + ).toBeInTheDocument(); + expect(mockConsoleToast.success).toHaveBeenCalledWith('Draft deleted'); + expect(screen.getByText('Support triage')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Retry' })); + expect(await screen.findByText('No workflows yet')).toBeInTheDocument(); + expect(mockStudioApi.deleteWorkflowDraft).toHaveBeenCalledTimes(1); + }); + it('archives by workflow identity and observes the exact row across catalogue pages', async () => { const activeCommitted = { serviceKey: 'svc-alpha', @@ -1532,7 +1569,7 @@ describe('Workflow Activity vNext catalogue', () => { query: 'wf-alpha', cursor: undefined, take: 100, - }), + }, expect.any(AbortSignal)), ); expect(mockScopesApi.queryWorkflowCatalogue).toHaveBeenCalledWith({ scopeId: 'scope-alpha', @@ -1540,7 +1577,7 @@ describe('Workflow Activity vNext catalogue', () => { query: 'wf-alpha', cursor: 'archive-page-2', take: 100, - }); + }, expect.any(AbortSignal)); expect(mockScopesApi.listWorkflows).not.toHaveBeenCalled(); expect(mockScopesApi.archiveWorkflow).toHaveBeenCalledWith( 'scope-alpha', @@ -6343,18 +6380,16 @@ describe('Workflow Activity vNext creation', () => { "Workflow couldn't be created", ), ); + expect(screen.getByRole('alert')).toHaveTextContent( + "Workflow couldn't be created", + ); expect( - screen.queryByText("Workflow couldn't be created"), - ).not.toBeInTheDocument(); - expect( - screen.queryByText('LLM service rejected the request'), - ).not.toBeInTheDocument(); + screen.getByText('LLM service rejected the request'), + ).not.toBeVisible(); fireEvent.click(screen.getByRole('button', { name: 'Change method' })); fireEvent.click(screen.getByRole('button', { name: 'Describe' })); - expect( - screen.queryByText("Workflow couldn't be created"), - ).not.toBeInTheDocument(); + expect(screen.queryByRole('alert')).not.toBeInTheDocument(); expect(screen.getByLabelText('Workflow name')).toHaveValue('Weekly review'); }); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts index d07172b4b1..2f14018670 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/styles.ts @@ -201,7 +201,10 @@ export const workflowActivityVNextCss = ` .wa-vnext__state--compact { border-style: solid; justify-items: start; min-height: 0; padding: 20px; place-items: initial; text-align: left; } .wa-vnext__state--compact h3 { font-size: 16px; margin: 0 0 7px; text-wrap: balance; } .wa-vnext__state--compact p { max-width: 560px; text-wrap: pretty; } +.wa-vnext__list-feedback { color: var(--wa-amber); margin-bottom: 12px; } .wa-vnext__notice { background: var(--wa-amber-bg); border: 1px solid #fdb022; border-radius: var(--wa-radius); color: #7a2e0e; margin-bottom: 12px; padding: 10px 12px; } +.wa-vnext__notice--progress { background: var(--wa-blue-bg); border-color: var(--wa-line); color: var(--wa-ink); } +.wa-vnext__generation-details pre { max-height: 240px; overflow: auto; overflow-wrap: anywhere; white-space: pre-wrap; } .wa-vnext__notice--error { background: var(--wa-red-bg); border-color: #fda29b; color: var(--wa-red); } .wa-vnext__panel { border: 1px solid var(--wa-line); border-radius: var(--wa-radius); padding: 20px; } .wa-vnext__form { display: grid; gap: 16px; } @@ -226,7 +229,7 @@ export const workflowActivityVNextCss = ` .wa-vnext__creation-heading { align-items: center; border-bottom: 1px solid var(--wa-line); display: flex; gap: 10px; padding-bottom: 14px; } .wa-vnext__creation-heading .ant-btn { margin-left: -12px; } .wa-vnext__creation-field > span:first-child { color: var(--wa-ink); display: block; font-size: 12px; font-weight: 650; line-height: 18px; } -.wa-vnext__creation-actions { display: flex; justify-content: flex-end; padding-top: 2px; } +.wa-vnext__creation-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; padding-top: 2px; } .wa-vnext__creation-actions .ant-btn { min-width: 156px; } .wa-vnext__creation-template-preview { border-left: 2px solid var(--wa-blue); padding: 2px 0 2px 14px; } .wa-vnext__template-browser { display: grid; gap: 18px; min-width: 0; } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx index bdc5018358..1cdf1ecf71 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.test.tsx @@ -1,11 +1,19 @@ -import { fireEvent, screen, waitFor, within } from '@testing-library/react'; +import { + act, + fireEvent, + screen, + waitFor, + within, +} from '@testing-library/react'; import * as React from 'react'; import { history } from '@/shared/navigation/history'; import { cleanupTestQueryClients, renderWithQueryClient, } from '../../../../tests/reactQueryTestUtils'; -import NewWorkflowPage from './NewWorkflowPage'; +import NewWorkflowPage, { + WORKFLOW_GENERATION_TIMEOUT_MS, +} from './NewWorkflowPage'; import WorkflowTemplatesPage from './WorkflowTemplatesPage'; jest.mock('@umijs/max', () => ({ @@ -385,14 +393,14 @@ describe('New workflow save-target recovery', () => { expect( await screen.findByText("Workflow couldn't be created"), ).toBeVisible(); - expect( - screen.queryByText('Generation unavailable'), - ).not.toBeInTheDocument(); + expect(screen.getByText('Generation unavailable')).not.toBeVisible(); expect(workflowName).toHaveValue('Weekly review'); expect(description).toHaveValue('Summarize this week'); expect(mockStudioApi.createWorkflowDraft).not.toHaveBeenCalled(); - fireEvent.click(screen.getByRole('button', { name: 'Generate and open' })); + fireEvent.click( + await screen.findByRole('button', { name: 'Generate and open' }), + ); await waitFor(() => expect(mockStudioApi.createWorkflowDraft).toHaveBeenCalledTimes(1), ); @@ -1148,3 +1156,163 @@ describe('New workflow save-target recovery', () => { ); }); }); + +function deferred() { + let resolve!: (value: T) => void; + const promise = new Promise((done) => { + resolve = done; + }); + return { promise, resolve }; +} + +async function describeWorkflow() { + fireEvent.click(await screen.findByRole('button', { name: 'Describe' })); + fireEvent.change(screen.getByLabelText('Workflow name'), { + target: { value: 'Incident review' }, + }); + fireEvent.change(screen.getByLabelText('What should this workflow do?'), { + target: { value: 'Review an incident' }, + }); + await waitFor(() => + expect( + screen.getByRole('button', { name: 'Generate and open' }), + ).toBeEnabled(), + ); + fireEvent.click(screen.getByRole('button', { name: 'Generate and open' })); +} + +describe('Workflow generation feedback and cancellation', () => { + beforeEach(() => { + jest.clearAllMocks(); + for (const mock of Object.values(mockStudioApi)) mock.mockReset(); + mockScopesApi.listWorkflows.mockReset(); + mockStudioApi.getWorkspaceSettings.mockResolvedValue(readyWorkspace); + mockStudioApi.listWorkflowDrafts.mockResolvedValue([]); + mockScopesApi.listWorkflows.mockResolvedValue([]); + mockStudioApi.parseYaml.mockResolvedValue({ + document: { name: 'incident_review', roles: [], steps: [] }, + findings: [], + }); + mockStudioApi.createWorkflowDraft.mockResolvedValue(materializedWorkflow); + mockUseDraftMaterialization.mockReturnValue({ + phase: 'idle', + receipt: null, + observe: jest.fn(), + retry: jest.fn(), + }); + }); + + it('shows real generation updates, preserves input on cancel and saves only the next attempt', async () => { + const oldAttempt = deferred(); + mockStudioApi.authorWorkflow + .mockReturnValueOnce(oldAttempt.promise) + .mockResolvedValueOnce('name: fresh_workflow'); + renderWithQueryClient(); + await describeWorkflow(); + expect(screen.getByText('Generating workflow…')).toBeInTheDocument(); + const options = mockStudioApi.authorWorkflow.mock.calls[0][1]; + act(() => options.onReasoning('Validating workflow attempt 1')); + expect( + screen.getByText( + 'Generation updates received. Waiting for the completed workflow.', + ), + ).toBeInTheDocument(); + fireEvent.click(screen.getByText('Generation details')); + expect(screen.getByText('Validating workflow attempt 1')).toBeVisible(); + fireEvent.click(screen.getByRole('button', { name: 'Cancel generation' })); + expect(options.signal.aborted).toBe(true); + expect(screen.getByLabelText('What should this workflow do?')).toHaveValue( + 'Review an incident', + ); + expect(screen.getByLabelText('Workflow name')).toHaveValue( + 'Incident review', + ); + fireEvent.click(screen.getByRole('button', { name: 'Generate and open' })); + await waitFor(() => + expect(history.push).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/workflows/wf-created-alpha', + ), + ); + await act(async () => { + oldAttempt.resolve('name: stale_workflow'); + }); + expect(mockStudioApi.createWorkflowDraft).toHaveBeenCalledTimes(1); + expect(mockStudioApi.createWorkflowDraft).toHaveBeenCalledWith( + expect.objectContaining({ yaml: 'name: fresh_workflow' }), + ); + }); + + it('times out during validation and prevents a late valid document from being saved', async () => { + const validation = deferred<{ + document: { name: string }; + findings: never[]; + }>(); + mockStudioApi.authorWorkflow.mockResolvedValue('name: incident_review'); + mockStudioApi.parseYaml.mockReturnValue(validation.promise); + renderWithQueryClient(); + // Resolve workspace queries before controlling only the generation deadline. + fireEvent.click(await screen.findByRole('button', { name: 'Describe' })); + fireEvent.change(screen.getByLabelText('Workflow name'), { + target: { value: 'Incident review' }, + }); + fireEvent.change(screen.getByLabelText('What should this workflow do?'), { + target: { value: 'Review an incident' }, + }); + await waitFor(() => + expect( + screen.getByRole('button', { name: 'Generate and open' }), + ).toBeEnabled(), + ); + jest.useFakeTimers(); + try { + await act(async () => { + fireEvent.click( + screen.getByRole('button', { name: 'Generate and open' }), + ); + }); + expect( + screen.getByText('Checking generated workflow…'), + ).toBeInTheDocument(); + await act(async () => { + await jest.advanceTimersByTimeAsync(WORKFLOW_GENERATION_TIMEOUT_MS); + }); + expect( + screen.getByText( + 'Generation took longer than two minutes. No draft was saved. You can try again.', + ), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Generate and open' }), + ).toBeEnabled(); + await act(async () => { + validation.resolve({ + document: { name: 'incident_review' }, + findings: [], + }); + }); + expect(mockStudioApi.createWorkflowDraft).not.toHaveBeenCalled(); + expect(history.push).not.toHaveBeenCalled(); + } finally { + jest.useRealTimers(); + } + }); + + it('aborts generation on unmount and ignores its late completion', async () => { + const oldAttempt = deferred(); + mockStudioApi.authorWorkflow.mockReturnValue(oldAttempt.promise); + const { rerender } = renderWithQueryClient( + , + ); + await describeWorkflow(); + const signal = mockStudioApi.authorWorkflow.mock.calls[0][1].signal; + // Unmount exercises the same cleanup used by scope/method transitions. + rerender(
Different workspace
); + expect(signal.aborted).toBe(true); + await act(async () => { + oldAttempt.resolve('name: stale_workflow'); + }); + expect(mockStudioApi.parseYaml).not.toHaveBeenCalled(); + expect(mockStudioApi.createWorkflowDraft).not.toHaveBeenCalled(); + expect(history.push).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx index 778894c0ab..c9a8c4ed0c 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx @@ -31,6 +31,8 @@ import { type WorkflowCreationMode, } from './workflowCreation'; +export const WORKFLOW_GENERATION_TIMEOUT_MS = 120_000; + function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } @@ -93,6 +95,30 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ >([]); const [submitting, setSubmitting] = React.useState(false); const [failure, setFailure] = React.useState(''); + const [generationPhase, setGenerationPhase] = React.useState< + 'idle' | 'generating' | 'validating' | 'saving' | 'cancelled' | 'timedOut' + >('idle'); + const [generationText, setGenerationText] = React.useState(''); + const [generationReasoning, setGenerationReasoning] = React.useState(''); + const generation = React.useRef(null); + const generationTimer = React.useRef(undefined); + const stopGeneration = React.useCallback(() => { + window.clearTimeout(generationTimer.current); + generation.current?.abort(); + generation.current = null; + }, []); + React.useEffect(() => { + setSubmitting(false); + setGenerationPhase('idle'); + setGenerationText(''); + setGenerationReasoning(''); + return stopGeneration; + }, [scopeId, stopGeneration]); + const cancelGeneration = () => { + stopGeneration(); + setGenerationPhase('cancelled'); + setSubmitting(false); + }; const toast = useConsoleToast(); const materialization = useDraftMaterialization(scopeId); const workspace = useQuery({ @@ -141,18 +167,23 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ ); const finishSave = React.useCallback( - async (result: StudioWorkflowSaveResult) => { + async (result: StudioWorkflowSaveResult, isCurrent = () => true) => { + if (!isCurrent()) return; if (result.kind === 'materialized') { navigateToWorkflow(result.workflow.workflowId); return; } const readable = await materialization.observe(result.receipt); - if (readable) navigateToWorkflow(readable.workflowId); + if (readable && isCurrent()) navigateToWorkflow(readable.workflowId); }, [materialization.observe, navigateToWorkflow], ); - const persistDraft = async (nextYaml: string, workflowName: string) => { + const persistDraft = async ( + nextYaml: string, + workflowName: string, + isCurrent = () => true, + ) => { if (!directoryId) { setFailure( t( @@ -176,6 +207,7 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ yaml: nextYaml, }); } catch (error) { + if (!isCurrent()) return; if (!isWorkflowCreateResultUnknown(error)) throw error; toast.warning( t( @@ -186,12 +218,15 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ void existingWorkflows.refetch(); return; } - await finishSave(result); + await finishSave(result, isCurrent); }; const createBlank = async () => { const workflowName = name.trim(); if (!workflowName || submitting) return; + setGenerationPhase('idle'); + setGenerationText(''); + setGenerationReasoning(''); setSubmitting(true); setFailure(''); setFindings([]); @@ -230,23 +265,56 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ const generateAndOpen = async () => { const workflowName = name.trim(); - if (!workflowName || !prompt.trim() || submitting) return; + if (!workflowName || !prompt.trim() || submitting || generation.current) + return; + const controller = new AbortController(); + generation.current = controller; + const current = () => + generation.current === controller && !controller.signal.aborted; setSubmitting(true); setFailure(''); setFindings([]); + setGenerationPhase('generating'); + setGenerationText(''); + setGenerationReasoning(''); + generationTimer.current = window.setTimeout(() => { + if (!current()) return; + stopGeneration(); + setGenerationPhase('timedOut'); + setSubmitting(false); + }, WORKFLOW_GENERATION_TIMEOUT_MS); try { const generated = await studioApi.authorWorkflow( { prompt }, - { onText: () => undefined }, + { + signal: controller.signal, + onText: (text) => { + if (current()) setGenerationText(text.slice(-12000)); + }, + onReasoning: (text) => { + if (current()) setGenerationReasoning(text.slice(-12000)); + }, + }, ); + if (!current()) return; + setGenerationPhase('validating'); const parsed = await studioApi.parseYaml({ yaml: generated }); + if (!current()) return; setFindings(parsed.findings); if (hasBlockingFindings(parsed.document, parsed.findings)) return; - await persistDraft(generated, workflowName); + // Cancellation is available until saving begins. An accepted save has its + // own observation/recovery flow and must not be labelled cancelled. + window.clearTimeout(generationTimer.current); + setGenerationPhase('saving'); + await persistDraft(generated, workflowName, current); } catch (error) { - setFailure(errorMessage(error)); + if (current()) setFailure(errorMessage(error)); } finally { - setSubmitting(false); + if (current()) { + stopGeneration(); + setGenerationPhase('idle'); + setSubmitting(false); + } } }; @@ -276,6 +344,7 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ const reviewAccess = () => history.push(buildWorkflowActivitySectionHref(scopeId, 'settings')); const selectMode = (nextMode: WorkflowCreationMode) => { + setGenerationPhase('idle'); setFailure(''); setFindings([]); if (nextMode === 'template') { @@ -392,7 +461,17 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({
+ {generationPhase === 'generating' || + generationPhase === 'validating' ? ( + + ) : null}
+ {generationPhase !== 'idle' ? ( +
+ + {generationPhase === 'generating' + ? t( + 'workflowActivityVNext.new.generating', + 'Generating workflow…', + ) + : generationPhase === 'validating' + ? t( + 'workflowActivityVNext.new.validating', + 'Checking generated workflow…', + ) + : generationPhase === 'saving' + ? t( + 'workflowActivityVNext.new.savingGenerated', + 'Saving workflow…', + ) + : generationPhase === 'timedOut' + ? t( + 'workflowActivityVNext.new.generationTimedOut', + 'Generation took longer than two minutes. No draft was saved. You can try again.', + ) + : t( + 'workflowActivityVNext.new.generationCancelled', + 'Generation cancelled. Your name and description are kept; no draft was saved.', + )} + + {generationPhase === 'generating' ? ( +

+ {generationReasoning || generationText + ? t( + 'workflowActivityVNext.new.generationReceiving', + 'Generation updates received. Waiting for the completed workflow.', + ) + : t( + 'workflowActivityVNext.new.generationWaiting', + 'Waiting for generation updates. You can cancel while you wait.', + )} +

+ ) : null} +
+ ) : null} + {generationReasoning || generationText ? ( +
+ + {generationReasoning ? ( +
{generationReasoning}
+ ) : null} + {generationText ?
{generationText}
: null} +
+
+ ) : null} ) : null} @@ -525,6 +680,20 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ ) : null} + {failure ? ( +
+ + {t( + 'workflowActivityVNext.new.createFailed', + "Workflow couldn't be created", + )} + + {failure} +
+ ) : null} {findings.length > 0 ? (
{findings.map((finding) => ( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.test.tsx new file mode 100644 index 0000000000..1077653a8a --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.test.tsx @@ -0,0 +1,162 @@ +import { act, fireEvent, screen } from '@testing-library/react'; +import * as React from 'react'; +import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; +import WorkflowMutationDialog from './WorkflowMutationDialog'; + +jest.mock('@/shared/api/scopesApi', () => ({ + scopesApi: { + archiveWorkflow: jest.fn(), + queryWorkflowCatalogue: jest.fn(), + }, +})); +jest.mock('@/shared/studio/api', () => ({ + isStudioApiStatus: () => false, + studioApi: { deleteWorkflowDraft: jest.fn() }, +})); +const scopes = jest.requireMock('@/shared/api/scopesApi').scopesApi as { + archiveWorkflow: jest.Mock; + queryWorkflowCatalogue: jest.Mock; +}; +const studio = jest.requireMock('@/shared/studio/api').studioApi as { + deleteWorkflowDraft: jest.Mock; +}; +const row = (deploymentStatus = 'Active') => ({ + workflowId: 'wf-alpha', + hasCommittedSource: true, + committed: { + deploymentStatus, + activeRevisionId: 'rev-alpha', + deploymentId: 'dep-alpha', + }, +}); +const catalogue = (items: ReturnType[]) => ({ + items, + nextPageToken: null, +}); +function deferred() { + let resolve!: (value: T) => void; + const promise = new Promise((done) => { + resolve = done; + }); + return { promise, resolve }; +} +const onObserved = jest.fn(); +const onClose = jest.fn(); +function Harness({ kind }: { kind: 'archive' | 'delete' }) { + const [open, setOpen] = React.useState(true); + return ( + <> + + { + setOpen(false); + onClose(); + }} + /> + + ); +} + +beforeEach(() => { + jest.clearAllMocks(); + scopes.archiveWorkflow.mockResolvedValue({ accepted: true }); + studio.deleteWorkflowDraft.mockResolvedValue(undefined); + jest.useFakeTimers(); +}); +afterEach(() => { + jest.useRealTimers(); +}); + +it('automatically confirms archival after the old observation window without another command', async () => { + let archived = false; + scopes.queryWorkflowCatalogue.mockImplementation(async () => + catalogue(archived ? [row('Deactivated')] : []), + ); + renderWithQueryClient(); + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Archive workflow' })); + }); + await act(async () => { + await jest.advanceTimersByTimeAsync(4500); + }); + expect( + screen.getByText('Request accepted. Checking for completion…'), + ).toBeInTheDocument(); + expect(onObserved).not.toHaveBeenCalled(); + archived = true; + await act(async () => { + await jest.advanceTimersByTimeAsync(3000); + }); + expect(onObserved).toHaveBeenCalledTimes(1); + expect(onClose).toHaveBeenCalledTimes(1); + expect(scopes.archiveWorkflow).toHaveBeenCalledTimes(1); +}); + +it('keeps a failed deletion check distinct from failure to delete and retries only the read', async () => { + scopes.queryWorkflowCatalogue + .mockRejectedValueOnce(new Error('network offline')) + .mockResolvedValue(catalogue([])); + renderWithQueryClient(); + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Delete draft' })); + }); + expect(screen.getByRole('dialog')).toHaveTextContent( + "Couldn't check whether the change has finished", + ); + expect(onObserved).not.toHaveBeenCalled(); + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + }); + expect(onObserved).toHaveBeenCalledTimes(1); + expect(studio.deleteWorkflowDraft).toHaveBeenCalledTimes(1); + expect(scopes.queryWorkflowCatalogue).toHaveBeenCalledTimes(2); +}); + +it('bounds a hung check and ignores its late result after closing and reopening the accepted operation', async () => { + const oldRead = deferred>(); + const newRead = deferred>(); + scopes.queryWorkflowCatalogue + .mockReturnValueOnce(oldRead.promise) + .mockReturnValueOnce(newRead.promise); + renderWithQueryClient(); + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Delete draft' })); + }); + const signal = scopes.queryWorkflowCatalogue.mock.calls[0][1]; + await act(async () => { + await jest.advanceTimersByTimeAsync(30_000); + }); + expect(signal.aborted).toBe(true); + expect( + screen.getByText( + 'This is taking longer than expected. Completion is still unconfirmed.', + ), + ).toBeInTheDocument(); + fireEvent.click( + screen.getAllByRole('button', { name: 'Close' }).slice(-1)[0], + ); + fireEvent.click(screen.getByRole('button', { name: 'Reopen' })); + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + }); + await act(async () => { + oldRead.resolve(catalogue([])); + }); + expect(onObserved).not.toHaveBeenCalled(); + expect( + screen.getByText('Request accepted. Checking for completion…'), + ).toBeInTheDocument(); + await act(async () => { + newRead.resolve(catalogue([])); + }); + expect(onObserved).toHaveBeenCalledTimes(1); + expect(studio.deleteWorkflowDraft).toHaveBeenCalledTimes(1); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.tsx new file mode 100644 index 0000000000..3ba40c476f --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowMutationDialog.tsx @@ -0,0 +1,297 @@ +import { Modal } from 'antd'; +import React from 'react'; +import { scopesApi } from '@/shared/api/scopesApi'; +import { t } from '@/shared/i18n/messages'; +import { isStudioApiStatus, studioApi } from '@/shared/studio/api'; +import { useConsoleToast } from '@/shared/ui/ConsoleToast'; +import TechnicalDetails from '../TechnicalDetails'; +import { observeWorkflowArchival } from './workflowArchival'; +import { + ensureWorkflowObservationActive, + WORKFLOW_OBSERVATION_DELAYS_MS, + WORKFLOW_OBSERVATION_TIMEOUT_MS, +} from './workflowObservation'; +import { observeWorkflowRemoval } from './workflowRemoval'; + +type Phase = + | 'idle' + | 'submitting' + | 'observing' + | 'delayed' + | 'readFailed' + | 'failed'; + +async function readWorkflowMatch( + scopeId: string, + workflowId: string, + archive: boolean, + signal: AbortSignal, +) { + let cursor: string | undefined; + const visitedCursors = new Set(); + do { + ensureWorkflowObservationActive(signal); + const response = await scopesApi.queryWorkflowCatalogue( + { + scopeId, + view: archive ? 'archived' : 'all', + query: workflowId, + cursor, + take: 100, + }, + signal, + ); + ensureWorkflowObservationActive(signal); + const target = response.items.find( + (item) => item.workflowId === workflowId, + ); + if (target) + return [ + { + workflowId: target.workflowId, + activeRevisionId: target.committed?.activeRevisionId ?? '', + deploymentId: target.committed?.deploymentId ?? '', + deploymentStatus: target.committed?.deploymentStatus ?? '', + hasCommittedSource: target.hasCommittedSource, + }, + ]; + const nextCursor = response.nextPageToken ?? undefined; + if (!nextCursor) return []; + if (visitedCursors.has(nextCursor)) + throw new Error('Workflow catalogue returned a repeated page cursor.'); + visitedCursors.add(nextCursor); + cursor = nextCursor; + } while (cursor); + return []; +} + +// Key by scope, operation and workflow in the owner. Closing preserves acceptance +// for the selected operation; reopening can only check that same result. +const WorkflowMutationDialog: React.FC<{ + readonly kind: 'archive' | 'delete'; + readonly scopeId: string; + readonly workflowId: string; + readonly workflowName: string; + readonly open: boolean; + readonly onClose: () => void; + readonly onObserved: () => void; +}> = ({ + kind, + scopeId, + workflowId, + workflowName, + open, + onClose, + onObserved, +}) => { + const archive = kind === 'archive'; + const toast = useConsoleToast(); + const [phase, setPhase] = React.useState('idle'); + const [error, setError] = React.useState(null); + const accepted = React.useRef(false); + const active = React.useRef(null); + const timer = React.useRef(undefined); + const stop = React.useCallback(() => { + window.clearTimeout(timer.current); + active.current?.abort(); + active.current = null; + }, []); + React.useEffect(() => { + if (!open && accepted.current) setPhase('delayed'); + return stop; + }, [open, stop]); + + const confirm = async () => { + if (active.current) return; + const controller = new AbortController(); + active.current = controller; + const current = () => + active.current === controller && !controller.signal.aborted; + setError(null); + setPhase(accepted.current ? 'observing' : 'submitting'); + try { + if (!accepted.current) { + if (archive) await scopesApi.archiveWorkflow(scopeId, workflowId); + else { + try { + await studioApi.deleteWorkflowDraft(workflowId, scopeId); + } catch (cause) { + if (!isStudioApiStatus(cause, 404)) throw cause; + } + } + if (!current()) return; + accepted.current = true; + } + setPhase('observing'); + timer.current = window.setTimeout(() => { + if (!current()) return; + stop(); + setPhase('delayed'); + }, WORKFLOW_OBSERVATION_TIMEOUT_MS); + const input = { + workflowId, + signal: controller.signal, + delaysMs: WORKFLOW_OBSERVATION_DELAYS_MS, + readWorkflows: () => + readWorkflowMatch(scopeId, workflowId, archive, controller.signal), + }; + const result = archive + ? await observeWorkflowArchival(input) + : await observeWorkflowRemoval(input); + if (!current()) return; + if (result.kind === 'delayed') { + setPhase('delayed'); + return; + } + onObserved(); + onClose(); + toast.success( + archive + ? t( + 'workflowActivityVNext.workflows.archiveSuccess', + 'Workflow archived', + ) + : t('workflowActivityVNext.workflows.deleteSuccess', 'Draft deleted'), + ); + } catch (cause) { + if (!current()) return; + setError(cause); + setPhase(accepted.current ? 'readFailed' : 'failed'); + toast.error( + accepted.current + ? t( + 'workflowActivityVNext.workflows.checkFailed', + "Couldn't check whether the change has finished", + ) + : archive + ? t( + 'workflowActivityVNext.workflows.archiveFailed', + "Workflow couldn't be archived", + ) + : t( + 'workflowActivityVNext.workflows.deleteFailed', + "Draft couldn't be deleted", + ), + ); + } finally { + if (active.current === controller) stop(); + } + }; + + const busy = phase === 'submitting' || phase === 'observing'; + const pending = + phase === 'observing' || phase === 'delayed' || phase === 'readFailed'; + return ( + void confirm()} + onCancel={() => { + if (phase !== 'submitting') { + stop(); + onClose(); + } + }} + > + {workflowName} +

+ {archive + ? t( + 'workflowActivityVNext.workflows.archiveDescription', + 'This stops new runs for the published workflow. Its editable draft, published revisions, and Activity history remain available. Publishing it again restores it.', + ) + : t( + 'workflowActivityVNext.workflows.deleteDescription', + 'This deletes only the editable draft. Published versions and run history remain available.', + )} +

+ {pending ? ( +
+ + {phase === 'observing' + ? t( + 'workflowActivityVNext.workflows.changeObserving', + 'Request accepted. Checking for completion…', + ) + : phase === 'readFailed' + ? t( + 'workflowActivityVNext.workflows.checkFailed', + "Couldn't check whether the change has finished", + ) + : t( + 'workflowActivityVNext.workflows.changeDelayed', + 'This is taking longer than expected. Completion is still unconfirmed.', + )} + +

+ {phase === 'observing' + ? t( + 'workflowActivityVNext.workflows.checkingDescription', + 'We will keep checking for up to 30 seconds. Closing this dialog does not undo the request.', + ) + : t( + 'workflowActivityVNext.workflows.checkAgainDescription', + 'Check again to see the result. This only checks the status and does not submit another request.', + )} +

+
+ ) : null} + {phase === 'failed' ? ( +

+ {archive + ? t( + 'workflowActivityVNext.workflows.archiveFailed', + "Workflow couldn't be archived", + ) + : t( + 'workflowActivityVNext.workflows.deleteFailed', + "Draft couldn't be deleted", + )} +

+ ) : null} + {error ? ( + + {error instanceof Error ? error.message : String(error)} + + ) : null} +
+ ); +}; + +export default WorkflowMutationDialog; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx index ea5ce92f63..006ab2192c 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/WorkflowsPage.tsx @@ -35,12 +35,8 @@ import { import TableScrollRegion from '../TableScrollRegion'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import WorkflowScheduleSurface from './WorkflowScheduleSurface'; -import { - canArchiveWorkflow, - isWorkflowArchived, - observeWorkflowArchival, -} from './workflowArchival'; -import { observeWorkflowRemoval } from './workflowRemoval'; +import { canArchiveWorkflow, isWorkflowArchived } from './workflowArchival'; +import WorkflowMutationDialog from './WorkflowMutationDialog'; type WorkflowRow = { readonly activeRevisionId: string; @@ -73,36 +69,6 @@ function toWorkflowRow(item: ScopeWorkflowCatalogueRow): WorkflowRow { }; } -async function readWorkflowCatalogueMatch( - scopeId: string, - view: ScopeWorkflowCatalogueView, - workflowId: string, -): Promise { - let cursor: string | undefined; - const visitedCursors = new Set(); - - do { - const response = await scopesApi.queryWorkflowCatalogue({ - scopeId, - view, - query: workflowId, - cursor, - take: 100, - }); - const target = response.items.find( - (item) => item.workflowId === workflowId, - ); - if (target) return [toWorkflowRow(target)]; - - const nextCursor = response.nextPageToken ?? undefined; - if (!nextCursor || visitedCursors.has(nextCursor)) return []; - visitedCursors.add(nextCursor); - cursor = nextCursor; - } while (cursor); - - return []; -} - function readWorkflowView(params: URLSearchParams): ScopeWorkflowCatalogueView { const view = params.get('view'); if (view === 'archived') return 'archived'; @@ -156,25 +122,17 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => { const [view, setView] = React.useState( readWorkflowView(initialParams), ); - const [archiveTarget, setArchiveTarget] = React.useState( - null, - ); - const [archiving, setArchiving] = React.useState(false); - const [archiveSubmitted, setArchiveSubmitted] = React.useState(false); - const [archivePhase, setArchivePhase] = React.useState< - 'delayed' | 'failed' | 'idle' - >('idle'); + const [mutationTarget, setMutationTarget] = React.useState<{ + readonly scopeId: string; + readonly kind: 'archive' | 'delete'; + readonly row: WorkflowRow; + } | null>(null); + const [mutationOpen, setMutationOpen] = React.useState(false); const [renameTarget, setRenameTarget] = React.useState( null, ); const [renameName, setRenameName] = React.useState(''); const [renaming, setRenaming] = React.useState(false); - const [deleteTarget, setDeleteTarget] = React.useState( - null, - ); - const [deleteFailed, setDeleteFailed] = React.useState(false); - const [deleteSucceeded, setDeleteSucceeded] = React.useState(false); - const [deleting, setDeleting] = React.useState(false); const [scheduleTarget, setScheduleTarget] = React.useState(null); const catalogue = useInfiniteQuery({ @@ -368,133 +326,9 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => { } }; - const openArchive = (row: WorkflowRow) => { - setArchiveTarget(row); - setArchiveSubmitted(false); - setArchivePhase('idle'); - }; - - const closeArchive = () => { - if (archiving) return; - setArchiveTarget(null); - setArchiveSubmitted(false); - setArchivePhase('idle'); - }; - - const confirmArchive = async () => { - if (!archiveTarget || archiving) return; - const target = archiveTarget; - let accepted = archiveSubmitted; - setArchiving(true); - setArchivePhase('idle'); - - try { - if (!accepted) { - await scopesApi.archiveWorkflow(scopeId, target.workflowId); - accepted = true; - setArchiveSubmitted(true); - } - - const observation = await observeWorkflowArchival({ - readWorkflows: () => - readWorkflowCatalogueMatch(scopeId, 'archived', target.workflowId), - workflowId: target.workflowId, - }); - if (observation.kind === 'delayed') { - setArchivePhase('delayed'); - return; - } - - await catalogue.refetch(); - setArchiveTarget(null); - setArchiveSubmitted(false); - setArchivePhase('idle'); - toast.success( - t( - 'workflowActivityVNext.workflows.archiveSuccess', - 'Workflow archived', - ), - ); - } catch { - if (accepted) { - setArchivePhase('delayed'); - } else { - setArchivePhase('failed'); - toast.error( - t( - 'workflowActivityVNext.workflows.archiveFailed', - "Workflow couldn't be archived", - ), - ); - } - } finally { - setArchiving(false); - } - }; - - const closeDelete = () => { - if (deleting) return; - setDeleteTarget(null); - setDeleteFailed(false); - setDeleteSucceeded(false); - }; - - const confirmDelete = async () => { - if (!deleteTarget || deleting) return; - setDeleting(true); - setDeleteFailed(false); - let removed = deleteSucceeded; - try { - if (!removed) { - try { - await studioApi.deleteWorkflowDraft(deleteTarget.workflowId, scopeId); - removed = true; - setDeleteSucceeded(true); - } catch (error) { - if (!isStudioApiStatus(error, 404)) throw error; - removed = true; - setDeleteSucceeded(true); - } - } - - const observation = await observeWorkflowRemoval({ - readWorkflows: () => - readWorkflowCatalogueMatch(scopeId, 'all', deleteTarget.workflowId), - workflowId: deleteTarget.workflowId, - }); - if (observation.kind === 'delayed') { - toast.error( - t( - 'workflowActivityVNext.workflows.deleteObservationDelayed', - 'Draft was deleted, but the workflow catalogue has not confirmed its removal yet', - ), - ); - setDeleteFailed(true); - setDeleteSucceeded(true); - return; - } - - const refreshed = await catalogue.refetch(); - if (refreshed.isError) throw refreshed.error; - setDeleteTarget(null); - setDeleteSucceeded(false); - } catch { - toast.error( - removed - ? t( - 'workflowActivityVNext.workflows.deleteRefreshFailed', - 'Draft was deleted, but the workflow list could not refresh. Please try again.', - ) - : t( - 'workflowActivityVNext.workflows.deleteFailed', - "Draft couldn't be deleted", - ), - ); - setDeleteFailed(true); - setDeleteSucceeded(removed); - } finally { - setDeleting(false); - } + const openMutation = (kind: 'archive' | 'delete', row: WorkflowRow) => { + setMutationTarget({ scopeId, kind, row }); + setMutationOpen(true); }; return ( @@ -584,6 +418,19 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => {
+ {!loading && catalogue.isRefetchError ? ( + + + {t( + 'workflowActivityVNext.workflows.refreshFailed', + "The workflow list couldn't refresh. The last loaded list is still shown.", + )} + + + + ) : null} {loading ? ( = ({ scopeId }) => { if (key === 'copy-reference') void copyWorkflowReference(row); if (key === 'delete') { - setDeleteTarget(row); - setDeleteFailed(false); - setDeleteSucceeded(false); + openMutation('delete', row); } - if (key === 'archive') openArchive(row); + if (key === 'archive') + openMutation('archive', row); }, }} placement="bottomRight" @@ -984,61 +830,6 @@ const WorkflowsPage: React.FC<{ readonly scopeId: string }> = ({ scopeId }) => {
) : null} - void confirmArchive()} - open={Boolean(archiveTarget)} - title={t( - 'workflowActivityVNext.workflows.archiveTitle', - 'Archive this workflow?', - )} - > -

- {t( - 'workflowActivityVNext.workflows.archiveDescription', - 'This stops new runs for the published workflow. Its editable draft, published revisions, and Activity history remain available. Publishing it again restores it.', - )} -

- {archivePhase === 'failed' ? ( -

- {t( - 'workflowActivityVNext.workflows.archiveFailed', - "Workflow couldn't be archived", - )} -

- ) : archivePhase === 'delayed' ? ( -

- {t( - 'workflowActivityVNext.workflows.archiveDelayed', - "Archive was accepted, but it hasn't been confirmed yet", - )} -

- ) : null} -
= ({ scopeId }) => {

) : null}
- void confirmDelete()} - open={Boolean(deleteTarget)} - title={t( - 'workflowActivityVNext.workflows.deleteTitle', - 'Delete editable draft?', - )} - > -

- {t( - 'workflowActivityVNext.workflows.deleteDescription', - 'This deletes only the editable draft. Published versions and run history remain available.', - )} -

-
+ {mutationTarget?.scopeId === scopeId ? ( + setMutationOpen(false)} + onObserved={() => { + setMutationTarget(null); + void catalogue.refetch(); + }} + /> + ) : null} { - return new Promise((resolve) => window.setTimeout(resolve, delayMs)); -} - export async function observeWorkflowArchival(input: { readonly delaysMs?: readonly number[]; + readonly signal?: AbortSignal; readonly readWorkflows: () => Promise< readonly WorkflowArchivalObservationItem[] >; @@ -55,11 +57,16 @@ export async function observeWorkflowArchival(input: { readonly workflowId: string; }): Promise { const delays = input.delaysMs ?? WORKFLOW_ARCHIVAL_OBSERVATION_DELAYS_MS; - const wait = input.wait ?? defaultWait; + const wait = + input.wait ?? + ((delayMs: number) => waitForWorkflowObservation(delayMs, input.signal)); for (const delayMs of delays) { + ensureWorkflowObservationActive(input.signal); if (delayMs > 0) await wait(delayMs); + ensureWorkflowObservationActive(input.signal); const workflows = await input.readWorkflows(); + ensureWorkflowObservationActive(input.signal); const target = workflows.find( (workflow) => workflow.workflowId === input.workflowId, ); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowObservation.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowObservation.ts new file mode 100644 index 0000000000..f9c2853643 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowObservation.ts @@ -0,0 +1,29 @@ +// The observation window includes slow reads, not only the backoff delays. +export const WORKFLOW_OBSERVATION_TIMEOUT_MS = 30_000; +export const WORKFLOW_OBSERVATION_DELAYS_MS = [ + 0, 300, 700, 1200, 2000, 3000, 5000, 8000, 10000, +] as const; + +export function ensureWorkflowObservationActive(signal?: AbortSignal): void { + if (signal?.aborted) { + throw signal.reason ?? new DOMException('Request cancelled.', 'AbortError'); + } +} + +export function waitForWorkflowObservation( + delayMs: number, + signal?: AbortSignal, +): Promise { + return new Promise((resolve, reject) => { + ensureWorkflowObservationActive(signal); + const onAbort = () => { + window.clearTimeout(timer); + reject(signal?.reason); + }; + const timer = window.setTimeout(() => { + signal?.removeEventListener('abort', onAbort); + resolve(); + }, delayMs); + signal?.addEventListener('abort', onAbort, { once: true }); + }); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowRemoval.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowRemoval.ts index bf4db5d1b9..e1b9e03f89 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowRemoval.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/workflowRemoval.ts @@ -1,3 +1,8 @@ +import { + ensureWorkflowObservationActive, + waitForWorkflowObservation, +} from './workflowObservation'; + export const WORKFLOW_REMOVAL_OBSERVATION_DELAYS_MS = [ 0, 300, 700, 1200, 2000, ] as const; @@ -6,23 +11,25 @@ export type WorkflowRemovalObservationResult = | { readonly kind: 'observed' } | { readonly kind: 'delayed' }; -function defaultWait(delayMs: number): Promise { - return new Promise((resolve) => window.setTimeout(resolve, delayMs)); -} - export async function observeWorkflowRemoval(input: { readonly delaysMs?: readonly number[]; + readonly signal?: AbortSignal; readonly readWorkflows: () => Promise; readonly wait?: (delayMs: number) => Promise; readonly workflowId: string; }): Promise { const delays = input.delaysMs ?? WORKFLOW_REMOVAL_OBSERVATION_DELAYS_MS; - const wait = input.wait ?? defaultWait; + const wait = + input.wait ?? + ((delayMs: number) => waitForWorkflowObservation(delayMs, input.signal)); const workflowId = input.workflowId.trim(); for (const delayMs of delays) { + ensureWorkflowObservationActive(input.signal); if (delayMs > 0) await wait(delayMs); + ensureWorkflowObservationActive(input.signal); const workflows = await input.readWorkflows(); + ensureWorkflowObservationActive(input.signal); if (!workflows.some((workflow) => workflow.workflowId === workflowId)) { return { kind: 'observed' }; } diff --git a/apps/aevatar-console-web/src/shared/studio/api.authorWorkflow.test.ts b/apps/aevatar-console-web/src/shared/studio/api.authorWorkflow.test.ts new file mode 100644 index 0000000000..fdfaff8991 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/studio/api.authorWorkflow.test.ts @@ -0,0 +1,61 @@ +import { studioApi } from './api'; + +const originalFetch = global.fetch; +afterEach(() => { + global.fetch = originalFetch; +}); + +function serveStream() { + let controller!: ReadableStreamDefaultController; + const cancel = jest.fn(); + const body = new ReadableStream({ + start(value) { + controller = value; + }, + cancel, + }); + global.fetch = jest.fn().mockResolvedValue({ ok: true, body }); + const frame = (value: unknown) => + controller.enqueue( + new TextEncoder().encode(`data: ${JSON.stringify(value)}\r\n\r\n`), + ); + return { controller, frame, cancel }; +} + +it('delivers progress before completion and returns the final validated workflow', async () => { + const stream = serveStream(); + let progress!: () => void; + const progressReceived = new Promise((resolve) => { + progress = resolve; + }); + const onReasoning = jest.fn(() => progress()); + const onText = jest.fn(); + const generated = studioApi.authorWorkflow( + { prompt: 'Make a workflow' }, + { onReasoning, onText }, + ); + let finished = false; + void generated.then(() => { + finished = true; + }); + stream.frame({ + type: 'TEXT_MESSAGE_REASONING', + delta: 'Validating workflow', + }); + await progressReceived; + expect(onReasoning).toHaveBeenCalledWith('Validating workflow'); + expect(finished).toBe(false); + stream.frame({ type: 'TEXT_MESSAGE_CONTENT', delta: 'name: candidate' }); + stream.frame({ type: 'TEXT_MESSAGE_END', message: 'name: validated' }); + stream.controller.close(); + await expect(generated).resolves.toBe('name: validated'); + expect(onText).toHaveBeenLastCalledWith('name: validated'); +}); + +it('rejects a truncated successful HTTP stream instead of returning a partial workflow', async () => { + const stream = serveStream(); + const generated = studioApi.authorWorkflow({ prompt: 'Make a workflow' }); + stream.frame({ type: 'TEXT_MESSAGE_CONTENT', delta: 'name: unfinished' }); + stream.controller.close(); + await expect(generated).rejects.toThrow('without a completed workflow'); +}); diff --git a/apps/aevatar-console-web/src/shared/studio/api.ts b/apps/aevatar-console-web/src/shared/studio/api.ts index d7831d9aca..1d0020b265 100644 --- a/apps/aevatar-console-web/src/shared/studio/api.ts +++ b/apps/aevatar-console-web/src/shared/studio/api.ts @@ -1081,38 +1081,39 @@ async function streamSse( } if (!response.body) { - return; + throw new Error('Workflow generation returned no event stream.'); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; - - while (true) { - const { done, value } = await reader.read(); - buffer += decoder.decode(value || new Uint8Array(), { stream: !done }); - - let boundary = buffer.indexOf('\n\n'); - while (boundary >= 0) { - const block = buffer.slice(0, boundary); - buffer = buffer.slice(boundary + 2); - - const data = block - .split('\n') - .filter((line) => line.startsWith('data:')) - .map((line) => line.slice(5).trim()) - .join('\n'); - - if (data && data !== '[DONE]') { - onFrame(JSON.parse(data) as unknown); + let ended = false; + try { + while (true) { + if (signal?.aborted) throw signal.reason ?? new DOMException('Request cancelled.', 'AbortError'); + const { done, value } = await reader.read(); + if (signal?.aborted) throw signal.reason ?? new DOMException('Request cancelled.', 'AbortError'); + buffer += decoder.decode(value || new Uint8Array(), { stream: !done }); + let boundary = /\r?\n\r?\n/.exec(buffer); + while (boundary) { + const block = buffer.slice(0, boundary.index); + buffer = buffer.slice(boundary.index + boundary[0].length); + const data = block + .split(/\r?\n/) + .filter((line) => line.startsWith('data:')) + .map((line) => line.slice(5).trim()) + .join('\n'); + if (data && data !== '[DONE]') onFrame(JSON.parse(data) as unknown); + boundary = /\r?\n\r?\n/.exec(buffer); + } + if (done) { + ended = true; + break; } - - boundary = buffer.indexOf('\n\n'); - } - - if (done) { - break; } + } finally { + if (!ended) await reader.cancel().catch(() => undefined); + reader.releaseLock(); } } @@ -4101,6 +4102,7 @@ export const studioApi = { ): Promise { let generatedText = ''; let reasoningText = ''; + let completed = false; await streamSse( '/api/workflows/generator', @@ -4129,8 +4131,9 @@ export const studioApi = { } if (normalized.type === 'TEXT_MESSAGE_END') { + completed = true; generatedText = - generatedText || normalized.message || normalized.delta || ''; + normalized.message || normalized.delta || generatedText; options?.onText?.(generatedText); return; } @@ -4142,6 +4145,9 @@ export const studioApi = { options?.signal, ); + if (!completed || !generatedText.trim()) { + throw new Error('Workflow generation ended without a completed workflow.'); + } return generatedText; }, }; From bd76730fa615992845a7ebc7a3771e31b3ddd26d Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:55:57 +0800 Subject: [PATCH 144/167] fix: Partition console CI tests and narrow editor queries --- .github/workflows/ci.yml | 49 +++++++++++++++---- .../docs/testing-policy.md | 7 +++ .../workflow-activity-vnext/index.test.tsx | 43 ++++++++-------- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cf8827bcc..ae537869e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -233,19 +233,53 @@ jobs: - name: Test Stability Guards run: bash tools/ci/test_stability_guards.sh + console-web-tests: + if: needs.changes.outputs.docs_only == 'false' && needs.changes.outputs.console_web == 'true' + needs: changes + runs-on: ubuntu-latest + timeout-minutes: 35 + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] + steps: + - uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.2.1 + run_install: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: apps/aevatar-console-web/pnpm-lock.yaml + + - name: Install console-web dependencies + run: pnpm --dir apps/aevatar-console-web install --frozen-lockfile + + - name: Test console-web shard + run: pnpm --dir apps/aevatar-console-web test --runInBand --shard=${{ matrix.shard }}/4 + console-web: # Refactor (iter16/cluster-ci-systemic-refactor): # Old pattern: console-web skipped scheduled CI and ran for every PR, workflow_dispatch, main/dev push, or console_web changes # New principle: gate on changes.outputs.console_web per Phase 9 #716 r3 consensus A - if: needs.changes.outputs.docs_only == 'false' && needs.changes.outputs.console_web == 'true' - needs: changes + # Keep the existing required check name and fail it if any test shard fails + # or is cancelled. All tests run once across the four Jest partitions. + if: always() && needs.changes.outputs.docs_only == 'false' && needs.changes.outputs.console_web == 'true' + needs: [changes, console-web-tests] runs-on: ubuntu-latest - # The serial suite grew into this budget: recent runs took 19m02s, 19m35s, - # 19m57s, then 20m10s and 20m17s, the last two cancelled mid-test at the - # 20m cap. The ~18m of test work is real, so give it headroom rather than - # letting every run decide the build on runner jitter. timeout-minutes: 35 steps: + - name: Require all console-web test shards + env: + TEST_SHARDS_RESULT: ${{ needs.console-web-tests.result }} + run: test "$TEST_SHARDS_RESULT" = success + - uses: actions/checkout@v4 - name: Setup pnpm @@ -267,9 +301,6 @@ jobs: - name: Type-check console-web run: pnpm --dir apps/aevatar-console-web tsc - - name: Test console-web - run: pnpm --dir apps/aevatar-console-web test --runInBand - - name: Build console-web run: pnpm --dir apps/aevatar-console-web build diff --git a/apps/aevatar-console-web/docs/testing-policy.md b/apps/aevatar-console-web/docs/testing-policy.md index 4855adb0c5..e8b09db449 100644 --- a/apps/aevatar-console-web/docs/testing-policy.md +++ b/apps/aevatar-console-web/docs/testing-policy.md @@ -364,6 +364,13 @@ distinct user-facing risk justifies it. ## Coverage and Reporting +GitHub CI runs the complete frontend test inventory in four Jest shards on +separate runners. Each shard keeps serial execution within its own process. +The existing `console-web` check requires every shard to succeed before running +the full typecheck and production build; a failed, cancelled or skipped shard +fails that check. The 35-minute job budget remains in place. Local development +continues to use the focused commands above. + - Coverage is diagnostic evidence, not a reason to create low-value tests or exercise generated code. Do not organize test files as generic coverage buckets. diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx index 9495a4a39b..e32c9f62b7 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.test.tsx @@ -5139,13 +5139,17 @@ describe('Workflow Activity vNext editor', () => { await renderPublishedWorkflowPage(); - fireEvent.click(await screen.findByRole('button', { name: 'Run' })); - const input = await screen.findByRole('textbox', { + const runButton = await screen.findByRole('button', { name: 'Run' }); + fireEvent.click(runButton); + const runPanel = await screen.findByRole('complementary', { + name: 'Published run panel', + }); + const input = within(runPanel).getByRole('textbox', { name: 'Published run input', }); fireEvent.change(input, { target: { value: 'Review order 42' } }); fireEvent.click( - screen.getByRole('button', { name: 'Start published run' }), + within(runPanel).getByRole('button', { name: 'Start published run' }), ); const logs = await screen.findByRole('complementary', { @@ -5158,38 +5162,37 @@ describe('Workflow Activity vNext editor', () => { ); fireEvent.click( - screen.getByRole('button', { name: 'Close published run panel' }), + within(runPanel).getByRole('button', { + name: 'Close published run panel', + }), ); expect( screen.queryByRole('complementary', { name: 'Published run panel' }), ).not.toBeInTheDocument(); expect(logs).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: 'Run' })); - expect( - await screen.findByRole('textbox', { name: 'Published run input' }), - ).toHaveValue('Review order 42'); + fireEvent.click(runButton); + const reopenedPanel = await screen.findByRole('complementary', { + name: 'Published run panel', + }); + const reopenedInput = within(reopenedPanel).getByRole('textbox', { + name: 'Published run input', + }); + expect(reopenedInput).toHaveValue('Review order 42'); expect( - await within(logs).findByText( - 'Order 42 is ready for approval.', - {}, - { - timeout: 2500, - }, - ), + await within(logs).findByText('Order 42 is ready for approval.'), ).toBeInTheDocument(); fireEvent.click(within(logs).getByRole('button', { name: 'Clear logs' })); expect( screen.queryByRole('complementary', { name: 'Workflow run console' }), ).not.toBeInTheDocument(); - fireEvent.change( - screen.getByRole('textbox', { name: 'Published run input' }), - { target: { value: 'A different input' } }, - ); + fireEvent.change(reopenedInput, { target: { value: 'A different input' } }); fireEvent.click( - screen.getByRole('button', { name: 'Start published run' }), + within(reopenedPanel).getByRole('button', { + name: 'Start published run', + }), ); expect( await screen.findByRole('complementary', { From 1f4d5f2dcda25340473bda211f122698732e55f4 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:03:26 +0800 Subject: [PATCH 145/167] Fix workflow feedback validation failures --- .../channels/TelegramConnectionPage.tsx | 8 +++----- .../workflow-activity-vnext/workflows/NewWorkflowPage.tsx | 7 +++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index c67e81f7cb..c77c9d16e8 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -316,15 +316,13 @@ export default function TelegramConnectionPage({ {[ { id: 'telegram-label', - key: 'label', - title: 'Label', + title: t('channels.connect.label', 'Label'), value: label, change: setLabel, }, { id: 'telegram-skill', - key: 'skillName', - title: 'Skill name', + title: t('channels.connect.skillName', 'Skill name'), value: skillName, change: setSkillName, }, @@ -332,7 +330,7 @@ export default function TelegramConnectionPage({
diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx index c9a8c4ed0c..d55c8faaec 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/workflows/NewWorkflowPage.tsx @@ -167,7 +167,10 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ ); const finishSave = React.useCallback( - async (result: StudioWorkflowSaveResult, isCurrent = () => true) => { + async ( + result: StudioWorkflowSaveResult, + isCurrent: () => boolean = () => true, + ) => { if (!isCurrent()) return; if (result.kind === 'materialized') { navigateToWorkflow(result.workflow.workflowId); @@ -182,7 +185,7 @@ const NewWorkflowPage: React.FC<{ readonly scopeId: string }> = ({ const persistDraft = async ( nextYaml: string, workflowName: string, - isCurrent = () => true, + isCurrent: () => boolean = () => true, ) => { if (!directoryId) { setFailure( From 40cbac747bd56fb1ac83f4ac09041879e6918d8e Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:48:09 +0800 Subject: [PATCH 146/167] Restore optional Telegram skill name input --- .../docs/features/2026-09-14-channel-pages.md | 29 +++++---- .../src/locales/channelMessages.en-US.ts | 6 +- .../src/locales/channelMessages.zh-CN.ts | 6 +- .../channels/TelegramConnectionPage.test.tsx | 40 ++++++++---- .../channels/TelegramConnectionPage.tsx | 62 +++++++++++++------ .../channels/connectionStyles.ts | 2 + .../shared/api/channelConnectionApi.test.ts | 43 +++++++++++-- .../src/shared/api/channelsApi.ts | 9 ++- 8 files changed, 139 insertions(+), 58 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index b99f5a1455..e7b5c5f165 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -57,10 +57,10 @@ separate Console frontend origin or shown as an editable form field. ## Telegram connection form -The form requires a masked bot token and shows one optional Channel name field -(「Channel 名字」 in Chinese). It has no separate Skill name input. The service -selection may be empty. The token reveal control is -keyboard accessible. Unsaved form navigation asks whether to discard; form data +The form requires a masked bot token and shows independent optional Channel name +(「Channel 名字」 in Chinese) and Skill name inputs. They share a row on desktop +and stack on narrow screens. The service selection may be empty. The token +reveal control is keyboard accessible. Unsaved form navigation asks whether to discard; form data is never persisted. The token is used only for Telegram's official name lookup and the authenticated registration POST. It stays in component memory for a manual retry after failure and clears after successful admission. No registration @@ -132,16 +132,19 @@ Telegram. Inputs and still-authorized selections are preserved for an explicit m retry; no automatic registration retry runs. An in-flight request still blocks duplicate clicks, and an accepted registration stays locked during observation. -### Channel name and Telegram bot-name defaults +### Channel name, Skill name and Telegram bot-name defaults -The optional Channel name is the only naming decision. The registration adapter -trims it and sends the same resolved value as both `label` and -`default_skill_name`. A nonblank custom name bypasses Telegram name lookup. -When blank or omitted, the adapter calls Telegram's official `getMe` endpoint -once and appends a hyphen plus six random digits to the bot's trimmed -`first_name`, never its `username` (for example, `My Bot-482731`). The suffix is -generated in the frontend once per submission, in the range 100000–999999, and -the same generated name is sent in both fields. Custom names receive no suffix. +The registration adapter trims the two inputs independently: Channel name maps +to `label`, and Skill name maps to `default_skill_name`. If both are nonblank, +Telegram name lookup is skipped. Otherwise, the adapter calls Telegram's +official `getMe` endpoint once and uses the bot's trimmed `first_name`, never +its `username`, to resolve missing names. + +A blank Channel name defaults to the bot name plus a hyphen and six random +digits (for example, `My Bot-482731`). The suffix is generated in the frontend +once per submission, in the range 100000–999999. A blank Skill name defaults +to the bot name without the suffix, restoring the original independent field +behavior. Custom names receive no suffix and never overwrite the other input. The form locks during lookup/submission and preserves its input after failure; a manual retry resolves the current token/name again. diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 7b9c5dcce1..1a1242208d 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -84,6 +84,8 @@ export default { 'channels.connect.hideToken': 'Hide bot token', 'channels.connect.botFather': 'Get token from BotFather', 'channels.connect.name': 'Channel name', + 'channels.connect.skillName': 'Skill name', + 'channels.connect.skillNameDefault': 'Defaults to the Telegram bot name', 'channels.connect.optional': '(optional)', 'channels.connect.botNameDefault': 'Defaults to the bot name + 6 random digits', @@ -119,7 +121,7 @@ export default { 'channels.connect.error.services': 'The selected services are no longer available. Review your selection and try again.', 'channels.connect.error.skill': - 'Could not configure the bot skill. Check the Channel name and your access in Ornn.', + 'Could not configure the bot skill. Check the Skill name and your access in Ornn.', 'channels.connect.error.authorization': 'Your session or service access needs attention. Sign in again and review your NyxID access.', 'channels.connect.error.conflict': @@ -127,7 +129,7 @@ export default { 'channels.connect.error.configuration': 'Channel setup is unavailable. Ask your administrator to check the deployment configuration.', 'channels.connect.error.rejected': - 'Could not connect Telegram. Check the bot token, Channel name, and selected services, then try again.', + 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', 'channels.connect.error.uncertain': 'Could not confirm the connection. Please try again.', 'channels.connect.discardTitle': 'Discard this connection setup?', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 5b517369a4..bd99dd99bd 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -77,6 +77,8 @@ export default { 'channels.connect.hideToken': '隐藏机器人令牌', 'channels.connect.botFather': '从 BotFather 获取令牌', 'channels.connect.name': 'Channel 名字', + 'channels.connect.skillName': 'Skill 名称', + 'channels.connect.skillNameDefault': '默认使用 Telegram 机器人名字', 'channels.connect.optional': '(可选)', 'channels.connect.botNameDefault': '默认使用机器人名字 + 6 位随机数字', 'channels.connect.tokenRequired': '请输入从 BotFather 获取的机器人令牌。', @@ -104,7 +106,7 @@ export default { '暂时无法获取 Telegram 机器人名称,请重试。', 'channels.connect.error.services': '所选服务已不可用,请重新检查选择后重试。', 'channels.connect.error.skill': - '无法配置机器人 Skill,请检查 Channel 名字和你在 Ornn 中的访问权限。', + '无法配置机器人 Skill,请检查 Skill 名称和你在 Ornn 中的访问权限。', 'channels.connect.error.authorization': '登录状态或服务权限需要处理,请重新登录并检查 NyxID 访问权限。', 'channels.connect.error.conflict': @@ -112,7 +114,7 @@ export default { 'channels.connect.error.configuration': '渠道接入暂不可用,请联系管理员检查部署配置。', 'channels.connect.error.rejected': - '无法连接 Telegram,请检查机器人令牌、Channel 名字和所选服务后重试。', + '无法连接 Telegram,请检查机器人令牌、Skill 名称和所选服务后重试。', 'channels.connect.error.uncertain': '暂时无法确认连接结果,请重试。', 'channels.connect.discardTitle': '放弃此次连接设置?', 'channels.connect.discard': '放弃', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index 78111e5770..e0b20ff465 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -46,13 +46,16 @@ const service = { }; const servicePath = 'https://nyx.example.test/api/v1/user-services'; const listPath = '/api/channels/registrations'; -function enterChannelName() { +function enterBotDetails() { fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: 'TEST_ONLY_TOKEN' }, }); fireEvent.change(screen.getByLabelText(/^Channel name/), { target: { value: 'My team bot' }, }); + fireEvent.change(screen.getByLabelText(/^Skill name/), { + target: { value: 'my-team-skill' }, + }); } beforeEach(() => { fetchMock.mockReset(); @@ -105,7 +108,7 @@ it('reads registrations only after acceptance or Check again, never while idle o await screen.findByText(/No services are available/); await idleAndReturn(); expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); - enterChannelName(); + enterBotDetails(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( 'Could not confirm the connection. Please try again.', @@ -214,7 +217,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits screen.queryByRole('checkbox', { name: /GitHub work/ }), ).not.toBeInTheDocument(); expect(screen.getByText('2 selected')).toBeInTheDocument(); - enterChannelName(); + enterBotDetails(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); expect( @@ -224,12 +227,13 @@ it('searches services, enforces permissions, submits selected IDs once and waits ([, init]) => init?.method === 'POST', ); expect(posts).toHaveLength(1); + expect(screen.getByLabelText(/^Skill name/)).toBeDisabled(); expect(posts[0][0]).toBe(listPath); expect(JSON.parse(String(posts[0][1]?.body))).toEqual({ platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', label: 'My team bot', - default_skill_name: 'My team bot', + default_skill_name: 'my-team-skill', authorization_mode: 'explicit_service_allowlist', service_ids: ['user-service-github', 'user-service-model'], webhook_base_url: 'https://aevatar-console-backend-api.aevatar.ai', @@ -384,13 +388,16 @@ it('recovers inventory failure and defaults optional names with an explicitly em false, ); expect(screen.getByLabelText('Channel name (optional)')).not.toBeRequired(); - expect(screen.queryByLabelText(/^Skill name/)).not.toBeInTheDocument(); + expect(screen.getByLabelText('Skill name (optional)')).not.toBeRequired(); fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: telegramToken }, }); fireEvent.change(screen.getByLabelText('Channel name (optional)'), { target: { value: ' ' }, }); + fireEvent.change(screen.getByLabelText('Skill name (optional)'), { + target: { value: ' ' }, + }); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); expect( await screen.findByText( @@ -405,7 +412,7 @@ it('recovers inventory failure and defaults optional names with an explicitly em ); expect(payload).toMatchObject({ label: expect.stringMatching(/^My Telegram Bot-[1-9]\d{5}$/), - default_skill_name: payload.label, + default_skill_name: 'My Telegram Bot', service_ids: [], }); expect(screen.getByLabelText(/^Bot token/)).toHaveValue(telegramToken); @@ -415,7 +422,7 @@ it('recovers inventory failure and defaults optional names with an explicitly em ).toBeEnabled(); }); -it('shows a name-lookup toast and retries with the current token and custom field value', async () => { +it('shows a name-lookup toast and retries with the current token and custom field values', async () => { telegramFetch.mockRejectedValueOnce( new Error(`Failed https://api.telegram.org/bot${telegramToken}/getMe`), ); @@ -450,6 +457,9 @@ it('shows a name-lookup toast and retries with the current token and custom fiel fireEvent.change(screen.getByLabelText(/^Channel name/), { target: { value: 'Custom label' }, }); + fireEvent.change(screen.getByLabelText(/^Skill name/), { + target: { value: 'custom-skill' }, + }); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText(/Telegram setup was submitted/); expect(telegramFetch.mock.calls.map(([url]) => url)).toEqual([ @@ -459,7 +469,7 @@ it('shows a name-lookup toast and retries with the current token and custom fiel expect(JSON.parse(String(post?.[1]?.body))).toMatchObject({ bot_token: updatedToken, label: 'Custom label', - default_skill_name: 'Custom label', + default_skill_name: 'custom-skill', }); }); @@ -496,7 +506,7 @@ it.each([ }); renderWithQueryClient(); fireEvent.click(await screen.findByRole('checkbox', { name: 'Select all' })); - enterChannelName(); + enterBotDetails(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); expect(await screen.findByRole('alert')).toHaveTextContent( 'Could not confirm the connection. Please try again.', @@ -505,6 +515,8 @@ it.each([ expect(within(form).queryByRole('alert')).not.toBeInTheDocument(); expect(screen.getByLabelText(/^Bot token/)).toHaveValue('TEST_ONLY_TOKEN'); expect(screen.getByLabelText(/^Channel name/)).toHaveValue('My team bot'); + expect(screen.getByLabelText(/^Skill name/)).toHaveValue('my-team-skill'); + expect(screen.getByLabelText(/^Skill name/)).toBeEnabled(); expect(screen.getByRole('checkbox', { name: 'Select all' })).toBeChecked(); expect(screen.getByRole('checkbox', { name: 'Select all' })).toBeEnabled(); expect( @@ -548,7 +560,7 @@ it('removes revoked selections after a rejected submission and allows a retry wi }); renderWithQueryClient(); fireEvent.click(await screen.findByRole('checkbox', { name: /GitHub work/ })); - enterChannelName(); + enterBotDetails(); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await screen.findByText( 'The selected services are no longer available. Review your selection and try again.', @@ -583,17 +595,19 @@ it('removes revoked selections after a rejected submission and allows a retry wi expect(JSON.parse(String(posts[1][1]?.body)).service_ids).toEqual([]); }); -it('confirms discarding an edited setup without issuing a registration request', async () => { +it('confirms discarding a skill-only edit without issuing a registration request', async () => { renderWithQueryClient(); await screen.findByRole('checkbox', { name: /GitHub work/ }); - enterChannelName(); + fireEvent.change(screen.getByLabelText(/^Skill name/), { + target: { value: 'unsaved-skill' }, + }); fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); const dialog = await screen.findByRole('dialog', { name: 'Discard this connection setup?', }); expect(history.push).not.toHaveBeenCalled(); fireEvent.click(within(dialog).getByRole('button', { name: 'Stay' })); - expect(screen.getByLabelText(/^Channel name/)).toHaveValue('My team bot'); + expect(screen.getByLabelText(/^Skill name/)).toHaveValue('unsaved-skill'); fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); fireEvent.click( within(await screen.findByRole('dialog')).getByRole('button', { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index efdea8dc64..3086679557 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -43,7 +43,7 @@ function registrationErrorMessage(reason: ChannelRegistrationFailure) { ], skill: [ 'channels.connect.error.skill', - 'Could not configure the bot skill. Check the Channel name and your access in Ornn.', + 'Could not configure the bot skill. Check the Skill name and your access in Ornn.', ], authorization: [ 'channels.connect.error.authorization', @@ -59,7 +59,7 @@ function registrationErrorMessage(reason: ChannelRegistrationFailure) { ], rejected: [ 'channels.connect.error.rejected', - 'Could not connect Telegram. Check the bot token, Channel name, and selected services, then try again.', + 'Could not connect Telegram. Check the bot token, Skill name, and selected services, then try again.', ], uncertain: [ 'channels.connect.error.uncertain', @@ -79,6 +79,7 @@ export default function TelegramConnectionPage({ // a persisted draft, navigation URL, or navigation state. const [botToken, setBotToken] = React.useState(''); const [channelName, setChannelName] = React.useState(''); + const [skillName, setSkillName] = React.useState(''); const [selectedIds, setSelectedIds] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); const [submittedId, setSubmittedId] = React.useState(null); @@ -108,7 +109,9 @@ export default function TelegramConnectionPage({ ), ); const locked = submitting || submittedId !== null; - const dirty = Boolean(botToken || channelName || selectedIds.length); + const dirty = Boolean( + botToken || channelName || skillName || selectedIds.length, + ); React.useEffect(() => { if (!services.isSuccess || services.isFetching) return; @@ -187,6 +190,7 @@ export default function TelegramConnectionPage({ const receipt = await channelsApi.registerTelegram({ botToken, label: channelName, + skillName, serviceIds: selectedIds, webhookBaseUrl, }); @@ -310,23 +314,43 @@ export default function TelegramConnectionPage({

) : null}
-
-
- +
+
+
+ +
+ setChannelName(event.target.value)} + /> +
+
+
+ +
+ setSkillName(event.target.value)} + />
- setChannelName(event.target.value)} - />
span { color: var(--wa-muted); font-size: 11px; font-weight: 400; margin-left: 5px; } @@ -44,6 +45,7 @@ export const channelConnectionCss = ` .channels__connection-pending { color: var(--wa-muted); line-height: 22px; margin-top: 20px; } .channels__connection-pending p { margin: 0 0 12px; } @media (max-width: 767px) { + .channels__name-fields { gap: 0; grid-template-columns: 1fr; } .channels__main--connect .channels__content { padding-top: 20px; } .channels__connect-heading h1 { font-size: 24px; line-height: 32px; } .channels__connect-heading { margin-bottom: 20px; } diff --git a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts index f556f83200..be571cd30a 100644 --- a/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelConnectionApi.test.ts @@ -41,6 +41,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra await channelsApi.registerTelegram({ botToken: ' TEST_ONLY_TOKEN ', label: ' Label ', + skillName: ' my-bot-skill ', serviceIds: [], webhookBaseUrl: 'https://api.example.test', }), @@ -52,7 +53,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra platform: 'telegram', bot_token: 'TEST_ONLY_TOKEN', label: 'Label', - default_skill_name: 'Label', + default_skill_name: 'my-bot-skill', authorization_mode: 'explicit_service_allowlist', service_ids: [], webhook_base_url: 'https://api.example.test', @@ -67,6 +68,7 @@ it('preserves an explicit empty allowlist and retains only the accepted registra channelsApi.registerTelegram({ botToken: 'TEST_ONLY_TOKEN', label: 'Label', + skillName: 'my-bot-skill', serviceIds: [], webhookBaseUrl: 'https://api.example.test', }), @@ -74,12 +76,40 @@ it('preserves an explicit empty allowlist and retains only the accepted registra }); it.each([ - { label: undefined, randomValue: 0, expectedName: 'My Bot-100000' }, - { label: ' ', randomValue: 0.99999999, expectedName: 'My Bot-999999' }, -])('uses the bot name plus six random digits for both backend names when the label is $label', async ({ + { + label: undefined, + skillName: undefined, + randomValue: 0, + expectedName: 'My Bot-100000', + expectedSkillName: 'My Bot', + }, + { + label: ' ', + skillName: ' ', + randomValue: 0.99999999, + expectedName: 'My Bot-999999', + expectedSkillName: 'My Bot', + }, + { + label: ' Custom channel ', + skillName: '', + randomValue: 0, + expectedName: 'Custom channel', + expectedSkillName: 'My Bot', + }, + { + label: '', + skillName: ' custom-skill ', + randomValue: 0, + expectedName: 'My Bot-100000', + expectedSkillName: 'custom-skill', + }, +])('resolves label $label and skill $skillName independently with one bot lookup', async ({ label, + skillName, randomValue, expectedName, + expectedSkillName, }) => { const random = jest.spyOn(Math, 'random').mockReturnValue(randomValue); const token = '123456:TEST_ONLY_TOKEN'; @@ -102,6 +132,7 @@ it.each([ await channelsApi.registerTelegram({ botToken: ` ${token} `, label, + skillName, serviceIds: ['user-service-a'], webhookBaseUrl: 'https://api.example.test', }); @@ -119,11 +150,11 @@ it.each([ ); expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toMatchObject({ label: expectedName, - default_skill_name: expectedName, + default_skill_name: expectedSkillName, bot_token: token, service_ids: ['user-service-a'], }); - expect(random).toHaveBeenCalledTimes(1); + expect(random).toHaveBeenCalledTimes(label?.trim() ? 0 : 1); }); it('does not register using a username when Telegram omits a usable bot name', async () => { diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 67d8fcc531..ff737fb1f9 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -58,6 +58,7 @@ export class ChannelRegistrationError extends Error { export interface TelegramRegistrationInput { readonly botToken: string; readonly label?: string; + readonly skillName?: string; readonly serviceIds: readonly string[]; readonly webhookBaseUrl: string; } @@ -198,9 +199,11 @@ export const channelsApi = { ): Promise<{ readonly registrationId: string }> { const botToken = input.botToken.trim(); const label = input.label?.trim() ?? ''; + const skillName = input.skillName?.trim() ?? ''; + const botName = + !label || !skillName ? await readTelegramBotName(botToken) : ''; const channelName = - label || - `${await readTelegramBotName(botToken)}-${Math.floor(100_000 + Math.random() * 900_000)}`; + label || `${botName}-${Math.floor(100_000 + Math.random() * 900_000)}`; // This request carries the token only in the authenticated POST body. // Do not use a mutation cache, navigation state, or persistent draft. let response: Response; @@ -215,7 +218,7 @@ export const channelsApi = { platform: 'telegram', bot_token: botToken, label: channelName, - default_skill_name: channelName, + default_skill_name: skillName || botName, webhook_base_url: input.webhookBaseUrl, authorization_mode: 'explicit_service_allowlist', service_ids: [...input.serviceIds], From f27ffe2eda1721dcb0fcb60e7d17fb046556c30f Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:12:54 +0800 Subject: [PATCH 147/167] Use the standard refresh button on Channels --- .../pages/workflow-activity-vnext/channels/ChannelsPage.tsx | 5 +++-- .../src/pages/workflow-activity-vnext/channels/styles.ts | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 5eefc13571..29fcbcfe71 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -240,13 +240,14 @@ export default function ChannelsPage({

{registrations.isPending ? ( Date: Tue, 15 Sep 2026 17:55:16 +0800 Subject: [PATCH 148/167] Show channel authorizations and acknowledge connection requests --- .../docs/features/2026-09-14-channel-pages.md | 33 +++- .../src/locales/channelMessages.en-US.ts | 16 +- .../src/locales/channelMessages.zh-CN.ts | 11 +- .../ChannelAuthorizedServices.test.tsx | 185 ++++++++++++++++++ .../channels/ChannelAuthorizedServices.tsx | 101 ++++++++++ .../channels/ChannelDetailsPage.tsx | 10 + .../channels/ChannelIdentity.test.tsx | 1 + .../channels/TelegramConnectionPage.test.tsx | 113 +++++++---- .../channels/TelegramConnectionPage.tsx | 90 +++------ .../channels/connectionStyles.ts | 2 - .../channels/styles.ts | 4 + .../src/shared/api/channelServicesApi.test.ts | 26 ++- .../src/shared/api/channelServicesApi.ts | 50 ++++- .../src/shared/api/channelsApi.test.ts | 48 +++++ .../src/shared/api/channelsApi.ts | 29 +++ 15 files changed, 585 insertions(+), 134 deletions(-) create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index e7b5c5f165..b4d4afca98 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -121,16 +121,19 @@ other services. Registration uses existing `POST /api/channels/registrations` with `platform`, `bot_token`, `label`, `default_skill_name`, `webhook_base_url`, and the explicit selection above. A `202`/`status: accepted` response must contain -the registration ID. The page waits for that exact Telegram registration in -the owner list before showing success and opening details; inbound activity -continues to use its real status. The form does not query registrations before -acceptance or after a failed submission. Accepted registration triggers one -list read; if it is not visible yet, Check again performs one GET. No polling, -window-focus refresh, or reconnect refresh runs. Registration failures, including +the registration ID. After admission, the form clears its token, shows an +informational "request submitted" toast, and immediately returns to Channels. +It does not claim that provisioning has completed, wait for a list read, or +leave the user on a locked connection form. The Channels page owns the normal +list load and its existing manual Refresh action if the new row is not visible +yet. Inbound activity continues to use its real status. The form never queries +registrations; no polling, window-focus refresh, or reconnect refresh runs. +Registration failures, including 504 and network errors, show a shared error toast and restore editable fields and Connect Telegram. Inputs and still-authorized selections are preserved for an explicit manual retry; no automatic registration retry runs. An in-flight request still blocks -duplicate clicks, and an accepted registration stays locked during observation. +duplicate clicks. An accepted submission cannot be sent again while navigation +is completing; late responses after unmount cannot show a toast or navigate. ### Channel name, Skill name and Telegram bot-name defaults @@ -181,6 +184,22 @@ Channel details include Skill using the same name, optional version and Ornn link as the connected list. Missing skills show Not set. Reading names/skills does not request full runtime configuration or alter bot resources. +### Authorized services in channel details + +The detail page displays the registration's saved `authorization_mode` and +`service_ids` from `GET /api/channels/registrations`. For an explicit allowlist, +only those exact UserService IDs are shown. One account inventory read resolves +their labels and slugs; the current session's selectable grants and service +activity do not hide saved channel authorizations or add other services. +Only safe ID, label and slug fields enter the service-name query cache. + +Missing service names retain their saved IDs. A failed name lookup preserves +other details and the saved authorization list, shows a safe toast, and offers +a manual retry of the names alone. Explicit empty authorization shows no +services authorized; NyxID default authorization and unavailable or legacy +authorization details have distinct messages and do not query the inventory. +No automatic refresh or registration change is introduced by this display. + ## API and ownership Contracts were checked against `origin/feature/integrate` and the backend diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 1a1242208d..767d8f0104 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -57,6 +57,15 @@ export default { 'channels.breadcrumb': 'Channel navigation', 'channels.registration': 'Registration', 'channels.inboundMessages': 'Inbound messages', + 'channels.services.title': 'Authorized services', + 'channels.services.loading': 'Loading service names', + 'channels.services.empty': 'No services authorized.', + 'channels.services.default': + 'Uses NyxID default authorization; individual services are not listed.', + 'channels.services.unavailable': + 'Authorization details are unavailable for this channel.', + 'channels.services.namesError': + 'Could not load service names. Please try again.', 'channels.botId': 'Bot ID', 'channels.provider': 'Provider', 'channels.keyId': 'Agent key ID', @@ -110,10 +119,9 @@ export default { 'Only selected services will be available to this bot.', 'channels.connect.selectionChanged': 'Some selected services are no longer available. Deselect them before connecting.', - 'channels.connect.pending': - 'Telegram setup was submitted. Waiting for your channel to appear.', - 'channels.connect.success': - 'Telegram added. Send your bot a message to get started.', + 'channels.connect.accepted': + 'Telegram connection request submitted. Use Refresh if the channel is not visible yet.', + 'channels.connect.submitted': 'Request submitted', 'channels.connect.error.token': 'Check the bot token from BotFather and try again.', 'channels.connect.error.botName': diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index bd99dd99bd..bb471ca400 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -52,6 +52,12 @@ export default { 'channels.breadcrumb': '渠道导航', 'channels.registration': '接入记录', 'channels.inboundMessages': '消息入站', + 'channels.services.title': '已授权服务', + 'channels.services.loading': '正在加载服务名称', + 'channels.services.empty': '未授权任何服务。', + 'channels.services.default': '使用 NyxID 默认授权,未列出具体服务。', + 'channels.services.unavailable': '此渠道的授权详情暂不可用。', + 'channels.services.namesError': '无法加载服务名称,请重试。', 'channels.botId': '机器人 ID', 'channels.provider': '提供方', 'channels.keyId': 'Agent 密钥 ID', @@ -99,8 +105,9 @@ export default { 'channels.connect.selectionHelp': '此机器人只能使用选中的服务。', 'channels.connect.selectionChanged': '部分选中服务已不可用,请取消勾选后再连接。', - 'channels.connect.pending': '已提交 Telegram 接入请求,正在等待渠道显示。', - 'channels.connect.success': 'Telegram 已添加,给机器人发送消息即可开始。', + 'channels.connect.accepted': + 'Telegram 连接请求已提交。如果渠道尚未显示,可点击刷新查看。', + 'channels.connect.submitted': '请求已提交', 'channels.connect.error.token': '请检查 BotFather 提供的令牌后重试。', 'channels.connect.error.botName': '暂时无法获取 Telegram 机器人名称,请重试。', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx new file mode 100644 index 0000000000..5b51a8084a --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx @@ -0,0 +1,185 @@ +import { focusManager, onlineManager } from '@tanstack/react-query'; +import { act, fireEvent, screen } from '@testing-library/react'; +import * as React from 'react'; +import { authFetch } from '@/shared/auth/fetch'; +import { persistAuthSession } from '@/shared/auth/session'; +import { createNyxIDServiceSession } from '../../../../tests/fixtures/nyxidServiceSession'; +import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; +import ChannelAuthorizedServices from './ChannelAuthorizedServices'; +import ChannelDetailsPage from './ChannelDetailsPage'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ + baseUrl: 'https://nyx.example.test', + enabled: true, + }), +})); +const fetchMock = jest.mocked(authFetch); +const listPath = '/api/channels/registrations'; +const statusPath = `${listPath}/reg-selected/status`; +const inventoryPath = 'https://nyx.example.test/api/v1/user-services'; +const registration = { + id: 'reg-selected', + platform: 'telegram', + scope_id: 'scope-alpha', + owned: true, + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work', 'us-model', 'us-deleted'], + default_skill_name: 'review-skill', +}; +const service = { + id: 'us-work', + slug: 'api-github', + label: 'GitHub work', + is_active: false, + credential_source: { type: 'personal' }, + default_request_headers: [{ value: 'TEST_ONLY_SECRET' }], +}; +const inventory = { + services: [ + service, + { + ...service, + id: 'us-model', + label: 'Chrono Public', + slug: 'chrono-llm-public', + is_active: true, + }, + { + ...service, + id: 'us-not-selected', + label: 'Not authorized for this channel', + is_active: true, + }, + ], +}; +const response = (value: unknown, status = 200) => + ({ ok: status === 200, status, json: async () => value }) as Response; + +beforeEach(() => { + fetchMock.mockReset(); + persistAuthSession( + createNyxIDServiceSession({ allowed_service_ids: ['us-not-selected'] }), + ); + fetchMock.mockImplementation(async (input) => { + if (input === '/api/auth/me') return response({ authenticated: false }); + if (input === listPath) return response([registration]); + if (input === statusPath) + return response({ registration_id: 'reg-selected', status: 'active' }); + if (input === inventoryPath) return response(inventory); + throw new Error('Unexpected test request'); + }); +}); + +it('shows saved authorizations by exact ID, including services outside current grants, without automatic refresh', async () => { + jest.useFakeTimers(); + try { + const view = renderWithQueryClient( + , + ); + expect(await screen.findByText('Authorized services')).toBeInTheDocument(); + expect(await screen.findByText('GitHub work')).toBeInTheDocument(); + expect(screen.getByText('Chrono Public')).toBeInTheDocument(); + expect(screen.getByText('chrono-llm-public')).toBeInTheDocument(); + expect(screen.getByText('us-deleted')).toBeInTheDocument(); + expect( + screen.queryByText('Not authorized for this channel'), + ).not.toBeInTheDocument(); + expect( + JSON.stringify( + view.queryClient + .getQueryCache() + .getAll() + .map((query) => query.state.data), + ), + ).not.toContain('TEST_ONLY_SECRET'); + // The shared account header has its own reconnect behavior. Check only + // channel-owned requests, keeping registrations, status and names covered. + const channelReads = () => + fetchMock.mock.calls + .map(([input]) => input) + .filter((input) => input !== '/api/auth/me') + .sort(); + const expectedReads = [listPath, statusPath, inventoryPath].sort(); + expect(channelReads()).toEqual(expectedReads); + await act(async () => { + await jest.advanceTimersByTimeAsync(60_000); + focusManager.setFocused(false); + focusManager.setFocused(true); + onlineManager.setOnline(false); + onlineManager.setOnline(true); + }); + expect(channelReads()).toEqual(expectedReads); + view.unmount(); + } finally { + focusManager.setFocused(undefined); + onlineManager.setOnline(true); + jest.useRealTimers(); + } +}); + +it('keeps saved IDs and other details on name lookup failure, then retries only names', async () => { + let inventoryReads = 0; + fetchMock.mockImplementation(async (input) => { + if (input === '/api/auth/me') return response({ authenticated: false }); + if (input === listPath) return response([registration]); + if (input === statusPath) + return response({ registration_id: 'reg-selected', status: 'active' }); + if (input === inventoryPath) + return ++inventoryReads === 1 + ? response({ error: 'TEST_ONLY_SECRET' }, 503) + : response(inventory); + throw new Error('Unexpected test request'); + }); + renderWithQueryClient( + , + ); + expect( + await screen.findByText('Could not load service names. Please try again.'), + ).toBeInTheDocument(); + expect(screen.getByText('us-work')).toBeInTheDocument(); + expect(screen.getByText('review-skill')).toBeInTheDocument(); + expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); + fireEvent.click(screen.getByRole('button', { name: 'Try again' })); + expect(await screen.findByText('GitHub work')).toBeInTheDocument(); + expect(inventoryReads).toBe(2); + expect( + fetchMock.mock.calls.filter(([input]) => input === listPath), + ).toHaveLength(1); + expect( + fetchMock.mock.calls.filter(([input]) => input === statusPath), + ).toHaveLength(1); +}); + +it('keeps empty, default and unavailable authorization distinct without querying current service choices', () => { + renderWithQueryClient( + <> + + + + , + ); + expect(screen.getByText('No services authorized.')).toBeInTheDocument(); + expect( + screen.getByText( + 'Uses NyxID default authorization; individual services are not listed.', + ), + ).toBeInTheDocument(); + expect( + screen.getByText('Authorization details are unavailable for this channel.'), + ).toBeInTheDocument(); + expect(fetchMock).not.toHaveBeenCalled(); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx new file mode 100644 index 0000000000..054ff3fbe0 --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx @@ -0,0 +1,101 @@ +import { useQuery } from '@tanstack/react-query'; +import { Button } from 'antd'; +import * as React from 'react'; +import { listChannelServiceIdentities } from '@/shared/api/channelServicesApi'; +import type { ChannelServiceAuthorization } from '@/shared/api/channelsApi'; +import { t } from '@/shared/i18n/messages'; +import { AevatarLoadingDots } from '@/shared/ui/AevatarLoading'; +import { useConsoleToast } from '@/shared/ui/ConsoleToast'; + +export default function ChannelAuthorizedServices({ + scopeId, + authorization, +}: { + readonly scopeId: string; + readonly authorization: ChannelServiceAuthorization; +}) { + const serviceIds = + authorization.kind === 'explicit' ? authorization.serviceIds : []; + const names = useQuery({ + queryKey: ['channels', scopeId, 'service-identities'], + queryFn: ({ signal }) => listChannelServiceIdentities(signal), + enabled: Boolean(scopeId && serviceIds.length), + retry: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchInterval: false, + }); + const toast = useConsoleToast(); + const reportedError = React.useRef(0); + React.useEffect(() => { + if ( + !serviceIds.length || + !names.isError || + names.errorUpdatedAt === reportedError.current + ) + return; + reportedError.current = names.errorUpdatedAt; + toast.error( + t( + 'channels.services.namesError', + 'Could not load service names. Please try again.', + ), + ); + }, [serviceIds.length, names.isError, names.errorUpdatedAt, toast]); + + if (authorization.kind === 'nyxidDefault') + return ( + + {t( + 'channels.services.default', + 'Uses NyxID default authorization; individual services are not listed.', + )} + + ); + if (authorization.kind === 'unavailable') + return ( + + {t( + 'channels.services.unavailable', + 'Authorization details are unavailable for this channel.', + )} + + ); + if (!serviceIds.length) + return ( + {t('channels.services.empty', 'No services authorized.')} + ); + + return ( +
+ {names.isPending ? ( + + ) : null} +
    + {serviceIds.map((id) => { + const service = names.data?.find((item) => item.id === id); + return ( +
  • + {service ? ( + <> + {service.label} + {service.slug} + + ) : ( + {id} + )} +
  • + ); + })} +
+ {names.isError ? ( + + ) : null} +
+ ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index d977d25a05..24aa2333f5 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -12,6 +12,7 @@ import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { buildWorkflowActivitySectionHref } from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; +import ChannelAuthorizedServices from './ChannelAuthorizedServices'; import { ChannelIcon, ChannelLink, @@ -190,6 +191,15 @@ export default function ChannelDetailsPage({
+
+
{t('channels.services.title', 'Authorized services')}
+
+ +
+
{t('channels.inboundMessages', 'Inbound messages')}
diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx index 9d0e9b022a..a57df01f6b 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx @@ -22,6 +22,7 @@ describe('Channel identifier disclosure', () => { skill: null, workflowDeliveryStatus: null, owned: true, + serviceAuthorization: { kind: 'unavailable' }, }; const identityProps = { registration, diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index e0b20ff465..35ff8fe426 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -72,7 +72,7 @@ afterEach(() => { global.fetch = originalFetch; }); -it('reads registrations only after acceptance or Check again, never while idle or after failure', async () => { +it('returns to Channels after admission without waiting for a list read or adding automatic refresh', async () => { jest.useFakeTimers(); try { let attempts = 0; @@ -88,10 +88,6 @@ it('reads registrations only after acceptance or Check again, never while idle o } return input === servicePath ? response({ services: [] }) : response([]); }); - const listReads = () => - fetchMock.mock.calls.filter( - ([input, init]) => input === listPath && init?.method !== 'POST', - ).length; const idleAndReturn = async () => { await act(async () => { await jest.advanceTimersByTimeAsync(60_000); @@ -114,17 +110,32 @@ it('reads registrations only after acceptance or Check again, never while idle o 'Could not confirm the connection. Please try again.', ); await idleAndReturn(); - expect(listReads()).toBe(0); + expect(history.replace).not.toHaveBeenCalled(); expect(attempts).toBe(1); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); - await screen.findByRole('button', { name: 'Check again' }); - expect(listReads()).toBe(1); + await screen.findByText(/Telegram connection request submitted/); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ); + expect( + screen.queryByRole('button', { name: 'Check again' }), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Request submitted' }), + ).toBeDisabled(); + expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); + const leaving = new Event('beforeunload', { cancelable: true }); + window.dispatchEvent(leaving); + expect(leaving.defaultPrevented).toBe(false); + fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); await idleAndReturn(); - expect(listReads()).toBe(1); - expect(history.replace).not.toHaveBeenCalled(); - fireEvent.click(screen.getByRole('button', { name: 'Check again' })); - await waitFor(() => expect(listReads()).toBe(2)); + expect(history.replace).toHaveBeenCalledTimes(1); expect(attempts).toBe(2); + expect( + fetchMock.mock.calls.filter( + ([input, init]) => input === listPath && init?.method !== 'POST', + ), + ).toHaveLength(0); view.unmount(); } finally { focusManager.setFocused(undefined); @@ -136,12 +147,11 @@ it('reads registrations only after acceptance or Check again, never while idle o } }); -it('searches services, enforces permissions, submits selected IDs once and waits for the new registration', async () => { +it('searches services, enforces permissions and returns to Channels after one accepted submission', async () => { let resolvePost!: (value: Response) => void; const post = new Promise((resolve) => { resolvePost = resolve; }); - let observed = false; fetchMock.mockImplementation(async (input, init) => { if (init?.method === 'POST') return post; if (input === servicePath) @@ -176,18 +186,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits }, ], }); - return response( - observed - ? [ - { - id: 'registration-created', - platform: 'telegram', - scope_id: 'scope-alpha', - owned: true, - }, - ] - : [], - ); + return response([]); }); window.history.replaceState( {}, @@ -251,7 +250,7 @@ it('searches services, enforces permissions, submits selected IDs once and waits ), ); expect( - await screen.findByText(/Telegram setup was submitted/), + await screen.findByText(/Telegram connection request submitted/), ).toBeInTheDocument(); expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); expect( @@ -259,7 +258,9 @@ it('searches services, enforces permissions, submits selected IDs once and waits 'Telegram added. Send your bot a message to get started.', ), ).not.toBeInTheDocument(); - expect(history.replace).not.toHaveBeenCalled(); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ); expect( JSON.stringify( view.queryClient @@ -269,18 +270,11 @@ it('searches services, enforces permissions, submits selected IDs once and waits ), ).not.toContain('TEST_ONLY'); expect(view.queryClient.getMutationCache().getAll()).toHaveLength(0); - observed = true; - fireEvent.click(screen.getByRole('button', { name: 'Check again' })); - await waitFor(() => - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-created', - ), - ); expect( - await screen.findByText( - 'Telegram added. Send your bot a message to get started.', + fetchMock.mock.calls.filter( + ([input, init]) => input === listPath && init?.method !== 'POST', ), - ).toBeInTheDocument(); + ).toHaveLength(0); }); it('selects and clears available results while preserving selections outside the search', async () => { @@ -461,7 +455,7 @@ it('shows a name-lookup toast and retries with the current token and custom fiel target: { value: 'custom-skill' }, }); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); - await screen.findByText(/Telegram setup was submitted/); + await screen.findByText(/Telegram connection request submitted/); expect(telegramFetch.mock.calls.map(([url]) => url)).toEqual([ `https://api.telegram.org/bot${telegramToken}/getMe`, ]); @@ -528,15 +522,13 @@ it.each([ fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await waitFor(() => expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-retried', + '/scopes/scope-alpha/workflow-activity-vnext/channels', ), ); expect(attempts).toBe(2); expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); expect( - await screen.findByText( - 'Telegram added. Send your bot a message to get started.', - ), + await screen.findByText(/Telegram connection request submitted/), ).toBeInTheDocument(); }); @@ -621,3 +613,38 @@ it('confirms discarding a skill-only edit without issuing a registration request false, ); }); + +it('ignores an accepted response after leaving the connection form', async () => { + let resolvePost!: (value: Response) => void; + const post = new Promise((resolve) => { + resolvePost = resolve; + }); + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') return post; + return input === servicePath ? response({ services: [] }) : response([]); + }); + const view = renderWithQueryClient( + , + ); + await screen.findByText(/No services are available/); + enterBotDetails(); + fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); + view.unmount(); + await act(async () => { + resolvePost( + response( + { status: 'accepted', registration_id: 'registration-late' }, + 202, + ), + ); + }); + expect(history.replace).not.toHaveBeenCalled(); + expect( + screen.queryByText(/Telegram connection request submitted/), + ).not.toBeInTheDocument(); + expect( + fetchMock.mock.calls.filter( + ([input, init]) => input === listPath && init?.method !== 'POST', + ), + ).toHaveLength(0); +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index 3086679557..e57effb9d7 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -16,15 +16,11 @@ import { import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; -import { - buildChannelDetailsHref, - buildWorkflowActivitySectionHref, -} from '../navigation'; +import { buildWorkflowActivitySectionHref } from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import ChannelServicePicker from './ChannelServicePicker'; import { getChannelWebhookBaseUrl } from './config'; import { channelConnectionCss } from './connectionStyles'; -import { useChannelRegistrations } from './queries'; import { channelsCss } from './styles'; function registrationErrorMessage(reason: ChannelRegistrationFailure) { @@ -82,17 +78,14 @@ export default function TelegramConnectionPage({ const [skillName, setSkillName] = React.useState(''); const [selectedIds, setSelectedIds] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); - const [submittedId, setSubmittedId] = React.useState(null); + const [submissionAccepted, setSubmissionAccepted] = React.useState(false); const [validationAttempted, setValidationAttempted] = React.useState(false); const [leaveTarget, setLeaveTarget] = React.useState(null); const inFlight = React.useRef(false); const mounted = React.useRef(true); - const completed = React.useRef(false); const toast = useConsoleToast(); const webhookBaseUrl = getChannelWebhookBaseUrl(); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); - // Read the list only to confirm an accepted submission or on Check again. - const registrations = useChannelRegistrations(scopeId, false); const services = useQuery({ queryKey: ['channels', scopeId, 'service-choices'], queryFn: ({ signal }) => listChannelServices(signal), @@ -108,7 +101,7 @@ export default function TelegramConnectionPage({ (service) => service.id === id && service.active && service.allowed, ), ); - const locked = submitting || submittedId !== null; + const locked = submitting || submissionAccepted; const dirty = Boolean( botToken || channelName || skillName || selectedIds.length, ); @@ -129,46 +122,18 @@ export default function TelegramConnectionPage({ }; }, []); React.useEffect(() => { - if (!dirty && !submitting) return; + if (submissionAccepted || (!dirty && !submitting)) return; const warn = (event: BeforeUnloadEvent) => { event.preventDefault(); event.returnValue = ''; }; window.addEventListener('beforeunload', warn); return () => window.removeEventListener('beforeunload', warn); - }, [dirty, submitting]); - React.useEffect(() => { - if ( - !submittedId || - !registrations.isSuccess || - registrations.isFetching || - completed.current - ) - return; - const registration = registrations.data.find( - (row) => row.id === submittedId && row.platform === 'telegram', - ); - if (!registration) return; - completed.current = true; - toast.success( - t( - 'channels.connect.success', - 'Telegram added. Send your bot a message to get started.', - ), - ); - history.replace(buildChannelDetailsHref(scopeId, registration.id)); - }, [ - submittedId, - registrations.data, - registrations.isSuccess, - registrations.isFetching, - scopeId, - toast, - ]); + }, [dirty, submitting, submissionAccepted]); function navigate(target: string) { if (submitting) return; - if (dirty && !submittedId) setLeaveTarget(target); + if (dirty && !submissionAccepted) setLeaveTarget(target); else history.push(target); } @@ -187,17 +152,13 @@ export default function TelegramConnectionPage({ inFlight.current = true; setSubmitting(true); try { - const receipt = await channelsApi.registerTelegram({ + await channelsApi.registerTelegram({ botToken, label: channelName, skillName, serviceIds: selectedIds, webhookBaseUrl, }); - if (!mounted.current) return; - setBotToken(''); - setSubmittedId(receipt.registrationId); - await registrations.refetch(); } catch (failure) { if (!mounted.current) return; const reason = @@ -206,10 +167,23 @@ export default function TelegramConnectionPage({ : 'uncertain'; if (reason === 'services') void services.refetch(); toast.error(registrationErrorMessage(reason)); + return; } finally { inFlight.current = false; if (mounted.current) setSubmitting(false); } + if (!mounted.current) return; + setBotToken(''); + setSubmissionAccepted(true); + // Admission is not completion. The list owns loading the resulting channel; + // do not leave the form waiting on a one-shot read or start background polls. + toast.info( + t( + 'channels.connect.accepted', + 'Telegram connection request submitted. Use Refresh if the channel is not visible yet.', + ), + ); + history.replace(listHref); } return ( @@ -301,7 +275,7 @@ export default function TelegramConnectionPage({ )} aria-invalid={ - validationAttempted && !botToken.trim() && !submittedId + validationAttempted && !botToken.trim() && !submissionAccepted } onChange={(event) => setBotToken(event.target.value)} /> @@ -375,25 +349,9 @@ export default function TelegramConnectionPage({ {registrationErrorMessage('configuration')}

) : null} - {submittedId ? ( -
-

- {t( - 'channels.connect.pending', - 'Telegram setup was submitted. Waiting for your channel to appear.', - )} -

- -
- ) : null}
@@ -409,7 +367,9 @@ export default function TelegramConnectionPage({ invalidSelection } > - {t('channels.connect.title', 'Connect Telegram')} + {submissionAccepted + ? t('channels.connect.submitted', 'Request submitted') + : t('channels.connect.title', 'Connect Telegram')}
diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts index 7c1aabfa21..c54d8a4fec 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts @@ -42,8 +42,6 @@ export const channelConnectionCss = ` .channels__service-state p { margin: 0 0 12px; } .channels__form-actions { align-items: center; border-top: 1px solid var(--channels-border); display: flex; gap: 12px; justify-content: space-between; margin-top: 28px; padding-top: 20px; } .channels__form-actions .ant-btn { font-size: 12px; min-height: 40px; padding-inline: 18px; } -.channels__connection-pending { color: var(--wa-muted); line-height: 22px; margin-top: 20px; } -.channels__connection-pending p { margin: 0 0 12px; } @media (max-width: 767px) { .channels__name-fields { gap: 0; grid-template-columns: 1fr; } .channels__main--connect .channels__content { padding-top: 20px; } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index 1946c2d696..79a89b61e7 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -77,6 +77,10 @@ export const channelsCss = ` .channels__facts > div { align-items: baseline; display: grid; gap: 16px; grid-template-columns: 160px minmax(0, 1fr); padding: 10px 0; } .channels__facts dt { color: var(--wa-muted); font-size: 12px; } .channels__facts dd { font-size: 12px; margin: 0; overflow-wrap: anywhere; text-align: right; } +.channels__authorized-services ul { display: grid; gap: 12px; list-style: none; margin: 0; padding: 0; } +.channels__authorized-services li { display: flex; flex-direction: column; gap: 2px; } +.channels__authorized-services strong { font-weight: 500; } +.channels__authorized-services > .ant-btn { margin-top: 12px; } .channels__identifier-link { align-items: baseline; color: var(--wa-blue); display: inline-flex; gap: 6px; max-width: 100%; } .channels__identifier-link span:first-child { min-width: 0; text-decoration: underline; text-underline-offset: 3px; } .channels__identifier-link .anticon { flex: 0 0 auto; font-size: 11px; } diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts index 4f00dfa632..f7d7ed480b 100644 --- a/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.test.ts @@ -1,7 +1,10 @@ import { authFetch } from '@/shared/auth/fetch'; import { persistAuthSession } from '@/shared/auth/session'; import { createNyxIDServiceSession } from '../../../tests/fixtures/nyxidServiceSession'; -import { listChannelServices } from './channelServicesApi'; +import { + listChannelServiceIdentities, + listChannelServices, +} from './channelServicesApi'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); jest.mock('@/shared/auth/config', () => ({ @@ -25,6 +28,27 @@ const personal = { const response = (value: unknown, status = 200) => ({ ok: status === 200, status, json: async () => value }) as Response; +it('reads safe service names independently of current selectable grants and activity', async () => { + persistAuthSession(createNyxIDServiceSession({ allowed_service_ids: [] })); + fetchMock.mockResolvedValue( + response({ + services: [ + personal, + { + ...personal, + id: 'us-inactive', + is_active: false, + label: 'Old service', + }, + ], + }), + ); + expect(await listChannelServiceIdentities()).toEqual([ + { id: 'us-work', slug: 'api-github', label: 'GitHub work' }, + { id: 'us-inactive', slug: 'api-github', label: 'Old service' }, + ]); +}); + afterEach(() => { fetchMock.mockReset(); global.fetch = originalFetch; diff --git a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts index 5d888244b8..79567bc4cf 100644 --- a/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelServicesApi.ts @@ -21,6 +21,11 @@ export interface ChannelServiceChoice { readonly organizationName: string | null; } +export type ChannelServiceIdentity = Pick< + ChannelServiceChoice, + 'id' | 'slug' | 'label' +>; + function decodeService(value: unknown): ChannelServiceChoice { const row = expectRecord(value, 'User service'); const source = expectRecord(row.credential_source, 'Credential source'); @@ -47,34 +52,59 @@ function decodeService(value: unknown): ChannelServiceChoice { }; } -export async function listChannelServices( +async function readChannelServiceInventory( + accessToken: string, signal?: AbortSignal, ): Promise { const config = getNyxIDRuntimeConfig(); if (config.configurationError || !config.baseUrl) throw new Error('NyxID is unavailable.'); - const session = await ensureActiveAuthSession(); - if (!session) throw new ChannelApiError(401); - const grants = readAccessTokenServiceGrants( - session.tokens.accessToken, - session.user.sub, - ); const response = await authFetch(`${config.baseUrl}/api/v1/user-services`, { signal, credentials: 'omit', cache: 'no-store', headers: { Accept: 'application/json', - // Pin the inventory to the exact bearer whose grants were read above. - Authorization: `Bearer ${session.tokens.accessToken}`, + Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) throw new ChannelApiError(response.status); const body = expectRecord(await response.json(), 'User services'); - const authorizedIds = new Set(grants.allowedServiceIds); const services = expectArray(body.services, 'User services', decodeService); if (new Set(services.map((service) => service.id)).size !== services.length) throw new Error('Ambiguous user service identity.'); + return services; +} + +// Display names do not establish a channel's authorization. The registration's +// saved service IDs remain authoritative even if this session's grants changed. +export async function listChannelServiceIdentities( + signal?: AbortSignal, +): Promise { + const session = await ensureActiveAuthSession(); + if (!session) throw new ChannelApiError(401); + const services = await readChannelServiceInventory( + session.tokens.accessToken, + signal, + ); + return services.map(({ id, slug, label }) => ({ id, slug, label })); +} + +export async function listChannelServices( + signal?: AbortSignal, +): Promise { + const session = await ensureActiveAuthSession(); + if (!session) throw new ChannelApiError(401); + const grants = readAccessTokenServiceGrants( + session.tokens.accessToken, + session.user.sub, + ); + // Pin inventory and selectable choices to the same authenticated bearer. + const services = await readChannelServiceInventory( + session.tokens.accessToken, + signal, + ); + const authorizedIds = new Set(grants.allowedServiceIds); return services.filter( (service) => service.active && diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts index fb09fef043..c71380d57f 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts @@ -29,6 +29,8 @@ describe('channel API boundary', () => { response([ { ...registration, + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work', 'us-model'], default_skill: { name: 'review.skill', version: '2.4' }, agent_key: { api_key_id: 'key-current', raw_key: 'TEST_ONLY_SECRET' }, runtime_config: { instructions: 'private instructions' }, @@ -51,6 +53,10 @@ describe('channel API boundary', () => { skill: { name: 'review.skill', version: '2.4' }, workflowDeliveryStatus: 'enabled', owned: true, + serviceAuthorization: { + kind: 'explicit', + serviceIds: ['us-work', 'us-model'], + }, }, ]); expect(JSON.stringify(result)).not.toMatch( @@ -80,6 +86,48 @@ describe('channel API boundary', () => { await expect(channelsApi.list()).rejects.toThrow(); }); + it('distinguishes explicit empty, NyxID default, missing and unknown service authorization', async () => { + fetchMock.mockResolvedValue( + response([ + { + ...registration, + authorization_mode: 'explicit_service_allowlist', + service_ids: [], + }, + { ...registration, authorization_mode: 'nyxid_default' }, + { ...registration, authorization_mode: 'explicit_service_allowlist' }, + { + ...registration, + authorization_mode: 'future_mode', + service_ids: ['us-work'], + }, + ]), + ); + expect( + (await channelsApi.list()).map((row) => row.serviceAuthorization), + ).toEqual([ + { kind: 'explicit', serviceIds: [] }, + { kind: 'nyxidDefault' }, + { kind: 'unavailable' }, + { kind: 'unavailable' }, + ]); + }); + + it('rejects malformed saved service IDs rather than inventing an authorization list', async () => { + fetchMock.mockResolvedValue( + response([ + { + ...registration, + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work', null], + }, + ]), + ); + await expect(channelsApi.list()).rejects.toThrow( + 'Invalid authorized service identity.', + ); + }); + it('encodes opaque registration IDs and rejects a status for a different registration', async () => { const id = 'registration:alpha/one + two'; fetchMock.mockResolvedValue( diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index ff737fb1f9..55b0561fa8 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -7,6 +7,11 @@ import { readString, } from './http/decoders'; +export type ChannelServiceAuthorization = + | { readonly kind: 'explicit'; readonly serviceIds: readonly string[] } + | { readonly kind: 'nyxidDefault' } + | { readonly kind: 'unavailable' }; + export interface ChannelRegistration { readonly id: string; readonly platform: string; @@ -20,6 +25,7 @@ export interface ChannelRegistration { } | null; readonly workflowDeliveryStatus: string | null; readonly owned: boolean; + readonly serviceAuthorization: ChannelServiceAuthorization; } export interface ChannelStatus { @@ -171,9 +177,32 @@ function decodeRegistration(value: unknown): ChannelRegistration { 'Workflow delivery status', ) || null, owned: expectBoolean(row.owned, 'Channel ownership'), + serviceAuthorization: decodeServiceAuthorization(row), }; } +function decodeServiceAuthorization( + row: Record, +): ChannelServiceAuthorization { + if (row.authorization_mode === 'nyxid_default') + return { kind: 'nyxidDefault' }; + if ( + row.authorization_mode !== 'explicit_service_allowlist' || + row.service_ids == null + ) + return { kind: 'unavailable' }; + const serviceIds = expectArray( + row.service_ids, + 'Authorized services', + (id) => { + if (typeof id !== 'string' || !id.trim()) + throw new Error('Invalid authorized service identity.'); + return id; + }, + ); + return { kind: 'explicit', serviceIds: [...new Set(serviceIds)] }; +} + async function request( path: string, decode: (value: unknown) => T, From e4bf75a4808336af7d50aa031f8249de40477612 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 18:12:05 +0800 Subject: [PATCH 149/167] Make Studio member binding regression deterministic --- .../src/pages/studio/index.test.tsx | 93 ++++++++++++++----- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/studio/index.test.tsx b/apps/aevatar-console-web/src/pages/studio/index.test.tsx index 87a5a5e194..46ac68598c 100644 --- a/apps/aevatar-console-web/src/pages/studio/index.test.tsx +++ b/apps/aevatar-console-web/src/pages/studio/index.test.tsx @@ -6312,7 +6312,24 @@ describe('StudioPage', () => { ).toContain('Read model has not materialized this run yet.'); }); - it('normalizes legacy workflow:default links and keeps the bound member contract when switching away and back', async () => { + it('keeps the explicit member binding contract when switching away and back', async () => { + // Member authority and draft identity come from the member contract; a + // published service ID in workflow focus must not select the bind subject. + mockStudioMembers = [{ ...mockStudioMembers[0], displayName: 'draft2' }]; + (studioApi.getMember as jest.Mock).mockResolvedValue({ + summary: mockStudioMembers[0], + implementationRef: { + implementationKind: 'workflow', + workflowId: 'workflow-1', + workflowRevision: 'rev-2', + }, + lastBinding: { + publishedServiceId: 'default', + revisionId: 'rev-2', + implementationKind: 'workflow', + boundAt: '2026-04-27T08:05:00Z', + }, + }); mockWorkflowFile = { ...mockWorkflowFile, workflowId: 'workflow-1', @@ -6325,7 +6342,23 @@ describe('StudioPage', () => { name: 'draft2', }, }; - (studioApi.listWorkflows as jest.Mock).mockResolvedValueOnce([ + (studioApi.getWorkflow as jest.Mock).mockImplementation( + async (workflowId: string) => { + if (workflowId === 'workflow-1') return mockWorkflowFile; + if (workflowId === 'workflow-2') + return { + ...mockWorkflowFile, + workflowId: 'workflow-2', + name: 'draft1', + fileName: 'draft1.yaml', + filePath: '/tmp/workflows/draft1.yaml', + yaml: 'name: draft1\nsteps: []\n', + document: { ...mockParsedDocument, name: 'draft1' }, + }; + throw new Error('Unexpected workflow identity in member binding test'); + }, + ); + (studioApi.listWorkflows as jest.Mock).mockResolvedValue([ { workflowId: 'workflow-1', name: 'draft2', @@ -6368,29 +6401,29 @@ describe('StudioPage', () => { ], }; let serviceCatalogVisible = false; - mockScopeRuntimeApi.listServices.mockImplementation(async () => { - if (!serviceCatalogVisible) { - serviceCatalogVisible = true; - return []; - } - - return [draft2Service]; - }); + mockScopeRuntimeApi.listServices.mockImplementation(async () => + serviceCatalogVisible ? [draft2Service] : [], + ); (studioApi.getScopeBinding as jest.Mock).mockResolvedValueOnce(null); - (studioApi.getMemberBindingRun as jest.Mock).mockResolvedValueOnce({ - bindingRunId: 'bind-member-workflow-1', - scopeId: 'scope-1', - memberId: 'workspace-demo', - status: 'succeeded', - result: { - publishedServiceId: 'default', - revisionId: 'rev-2', - implementationKind: 'workflow', - expectedActorId: 'actor-default', + (studioApi.getMemberBindingRun as jest.Mock).mockImplementationOnce( + async () => { + serviceCatalogVisible = true; + return { + bindingRunId: 'bind-member-workflow-1', + scopeId: 'scope-1', + memberId: 'workspace-demo', + status: 'succeeded', + result: { + publishedServiceId: 'default', + revisionId: 'rev-2', + implementationKind: 'workflow', + expectedActorId: 'actor-default', + }, + failure: null, + updatedAt: '2026-04-27T08:15:01Z', + }; }, - failure: null, - updatedAt: '2026-04-27T08:15:01Z', - }); + ); mockScopeRuntimeApi.getServiceRevisions.mockImplementation(async () => mockBuildServiceRevisionCatalog({ serviceId: 'default', @@ -6400,7 +6433,7 @@ describe('StudioPage', () => { ); renderStudioPage( - '/studio?scopeId=scope-1&step=build&focus=workflow%3Adefault&tab=studio', + '/studio?scopeId=scope-1&member=member%3Aworkspace-demo&step=build&focus=workflow%3Aworkflow-1&tab=studio', ); expect( @@ -6411,6 +6444,7 @@ describe('StudioPage', () => { expect(await screen.findByTestId('studio-bind-surface')).toBeTruthy(); await waitFor(() => { expect(screen.getByText('candidate:draft2')).toBeTruthy(); + expect(screen.getByText('member:workspace-demo')).toBeTruthy(); }); await act(async () => { @@ -6420,6 +6454,13 @@ describe('StudioPage', () => { }); await waitFor(() => { + expect(studioApi.bindMemberWorkflow).toHaveBeenCalledWith( + expect.objectContaining({ + memberId: 'workspace-demo', + workflowId: 'workflow-1', + }), + ); + expect(studioApi.bindScopeWorkflow).not.toHaveBeenCalled(); expect(screen.getByText('service:default')).toBeTruthy(); const searchParams = new URLSearchParams(window.location.search); expect(searchParams.get('member')).toBe('member:workspace-demo'); @@ -6429,6 +6470,10 @@ describe('StudioPage', () => { const rail = await screen.findByLabelText('Team members'); fireEvent.click(within(rail).getByRole('button', { name: 'draft1' })); + await waitFor(() => { + expect(screen.getByText('candidate:draft1')).toBeTruthy(); + expect(screen.getByText('service:no-service')).toBeTruthy(); + }); fireEvent.click(within(rail).getByRole('button', { name: 'draft2' })); await waitFor(() => { From 839ebdb87fc15fa36fae47ae0dcf6353406f9e5d Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 19:07:42 +0800 Subject: [PATCH 150/167] Add channel runtime editor with shared connection controls --- apps/aevatar-console-web/config/routes.ts | 5 + .../docs/2026-09-15-channel-runtime-editor.md | 79 +++ .../src/locales/channelMessages.en-US.ts | 35 ++ .../src/locales/channelMessages.zh-CN.ts | 29 + .../channels/ChannelDetailsPage.tsx | 15 +- .../channels/ChannelEditPage.test.tsx | 354 +++++++++++ .../channels/ChannelEditPage.tsx | 572 ++++++++++++++++++ .../channels/ChannelServicePicker.tsx | 52 +- .../channels/ChannelSkillField.tsx | 43 ++ .../channels/TelegramConnectionPage.tsx | 41 +- .../channels/connectionStyles.ts | 8 + .../channels/queries.ts | 17 + .../pages/workflow-activity-vnext/index.tsx | 11 + .../workflow-activity-vnext/navigation.ts | 7 + .../api/channelRuntimeConfigApi.test.ts | 73 +++ .../src/shared/api/channelRuntimeConfigApi.ts | 241 ++++++++ .../src/shared/api/channelsApi.ts | 2 +- .../tests/fixtures/channelRuntimeConfig.ts | 19 + 18 files changed, 1555 insertions(+), 48 deletions(-) create mode 100644 apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx create mode 100644 apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx create mode 100644 apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts create mode 100644 apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts create mode 100644 apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts diff --git a/apps/aevatar-console-web/config/routes.ts b/apps/aevatar-console-web/config/routes.ts index cbcdb25671..934838e6bb 100644 --- a/apps/aevatar-console-web/config/routes.ts +++ b/apps/aevatar-console-web/config/routes.ts @@ -98,6 +98,11 @@ export default [ component: './workflow-activity-vnext', hideInMenu: true, }, + { + path: '/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId/edit', + component: './workflow-activity-vnext', + hideInMenu: true, + }, { path: '/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId', component: './workflow-activity-vnext', diff --git a/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md new file mode 100644 index 0000000000..7428dae328 --- /dev/null +++ b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md @@ -0,0 +1,79 @@ +# Channel Runtime Editor + +Channel details now provides an Edit action at +`/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId/edit`. +The editor follows the approved [Figma frame](https://www.figma.com/design/FaVJx5IZeQX9jHcUi55ndB/?node-id=63-327) +and the existing Telegram connection form. + +## Shared Components + +- `ChannelSkillField` is shared by connection and editing, including its label, + optional state, input, and field error presentation. +- Both pages use `ChannelServicePicker`, `useChannelServiceChoices`, the existing + connection styles, shell, loading skeleton, and toast provider. +- The primary form contains Skill name and Services. Advanced settings is + collapsed initially and contains Skill version and Bot instructions. +- Channel names and bot tokens are not editable through this API. Tool sets, + extra tool names, and credential source are retained from the loaded config. + +## API Behavior + +The API contract was checked against `origin/feature/integrate` at +`dc4862789e0f70b7dd27e7d785f63f91c308118b`, specifically +`ChannelCallbackEndpoints.cs` and `ChannelRegistrationServiceSelection.cs`. + +- GET and POST use `/api/channels/registrations/{registrationId}/runtime-config`. + Detail identity and scope must match the route. +- Opening the editor requires a fresh successful GET, including when Query has + cached data from an earlier visit. Subsequent readback never resets user input. +- POST sends the complete known runtime config because omitted fields are not + patches. Strings edited by the user are trimmed as the backend parser expects. +- Clearing Skill name also clears its version. Removing service authorization + removes selectors for the deselected service, preserving other selectors. +- Services use the same authenticated inventory and bearer grant filtering as + creation. Saved services absent from current choices remain visible as + unavailable until deliberately removed. They are never silently dropped. +- Existing `nyxid_default` registrations retain their mode until the user + explicitly switches to individual selection. Zero individually selected + services is sent as an explicit empty allowlist. +- `202 Accepted` does not produce a saved toast. Readback must match the submitted + configuration and have an authoritative state version newer than the initial + GET. Delayed or failed confirmation preserves the request and offers Check + again, which issues only GET. There is no background polling. +- Backend field errors map to localized fields without retaining raw diagnostic + messages. Unrecognized config modes fail closed; unexpected credential fields + are discarded by the adapter. +- Cancel and shell navigation protect unsaved changes. Reload/close uses the + native before-unload warning. Late responses after unmount cannot navigate or + report success. + +## Design Baseline + +- Baseline directory: `docs/design-baselines/workflow-activity-vnext/`. +- Primary design: `aevatar-workflow-activity-vnext.excalidraw`. +- SHA-256: `30e74d7b410ae72c4c91432355436679033679c54c10b1702908435b001577de`. +- Contract: `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-design.md`. +- User paths: `docs/superpowers/specs/2026-08-04-workflow-activity-vnext-user-paths.md`. +- The approved Figma frame supplements the baseline for Channel editing. +- Authentication, callback, session, returnTo, and Umi localization are reused. +- Production data comes only from real APIs and API-acknowledged user actions. + Fixtures remain test-only; no backend, mock, or credential fallback is added. + +## Verification Scope + +Eight new integration cases cover exact identity, safe decoding, unsupported +config, detail-to-edit navigation, whole-config preservation, accepted readback, +explicit clearing, unavailable services, field errors, cached detail revalidation, +legacy authorization, confirmation retry, unsaved navigation, and unmounts. +Existing creation, channel listing/details, API, navigation, route configuration, +and locale tests protect the reused surfaces. + +Browser verification uses the configured remote backend: actual configuration +prefill, service selection, collapsed/expanded advanced fields, and desktop and +390px mobile layouts. It does not submit a configuration change to the live bot. +The local preview runs on port 5193 with its OAuth callback on the same origin. + +Local verification is restricted to affected Jest files, changed-file Biome, +the test stability guard, baseline integrity, and diff checks. Full frontend +tests, typecheck, and production build are delegated to GitHub CI; no native +affected TypeScript target is available. diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 767d8f0104..7c9287f80b 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -1,4 +1,39 @@ export default { + 'channels.edit': 'Edit', + 'channels.edit.title': 'Edit {platform}', + 'channels.edit.loadingTitle': 'Edit channel', + 'channels.edit.loading': 'Loading channel configuration', + 'channels.edit.configuration': 'Configuration', + 'channels.edit.save': 'Save changes', + 'channels.edit.advanced': 'Advanced settings', + 'channels.edit.version': 'Skill version', + 'channels.edit.instructions': 'Bot instructions', + 'channels.edit.defaults': 'NyxID defaults', + 'channels.edit.useDefaults': 'Use NyxID defaults', + 'channels.edit.unavailableService': 'Unavailable service', + 'channels.edit.servicesError': + 'Could not load your services. Try again before saving.', + 'channels.edit.servicesEmpty': + 'No services are available with your current authorization.', + 'channels.edit.missingServices': + 'Some saved services are no longer available. Deselect them before saving.', + 'channels.edit.selectionError': + 'Review your selected services and try again.', + 'channels.edit.skillError': + 'Check the skill name. Use no more than 128 characters.', + 'channels.edit.versionError': + 'Check the skill version and name. Use no more than 128 characters.', + 'channels.edit.instructionsError': + 'Check the bot instructions. Use no more than 4,000 characters.', + 'channels.edit.failed': + 'Could not save channel changes. Review your choices and try again.', + 'channels.edit.saved': 'Channel changes saved.', + 'channels.edit.confirming': 'Changes submitted. Waiting for confirmation.', + 'channels.edit.checkFailed': + 'Changes were submitted, but could not be confirmed. Check again.', + 'channels.edit.check': 'Check again', + 'channels.edit.discardTitle': 'Discard your changes?', + 'channels.edit.discardHelp': 'Your unsaved channel changes will be lost.', 'workflowActivityVNext.nav.channels': 'Channels', 'channels.title': 'Connect your channels to your agent', 'channels.description': diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index bb471ca400..71963aaf42 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -1,4 +1,33 @@ export default { + 'channels.edit': '编辑', + 'channels.edit.title': '编辑 {platform}', + 'channels.edit.loadingTitle': '编辑渠道', + 'channels.edit.loading': '正在加载渠道配置', + 'channels.edit.configuration': '配置', + 'channels.edit.save': '保存更改', + 'channels.edit.advanced': '高级设置', + 'channels.edit.version': 'Skill 版本', + 'channels.edit.instructions': '机器人行为指令', + 'channels.edit.defaults': 'NyxID 默认授权', + 'channels.edit.useDefaults': '使用 NyxID 默认授权', + 'channels.edit.unavailableService': '不可用的服务', + 'channels.edit.servicesError': '无法加载服务,请重试后再保存。', + 'channels.edit.servicesEmpty': '当前授权范围内暂无可用服务。', + 'channels.edit.missingServices': + '部分已保存的服务已不可用,请取消勾选后再保存。', + 'channels.edit.selectionError': '请检查选中的服务后重试。', + 'channels.edit.skillError': '请检查 Skill 名称,最多允许 128 个字符。', + 'channels.edit.versionError': + '请检查 Skill 版本和名称,版本最多允许 128 个字符。', + 'channels.edit.instructionsError': + '请检查机器人行为指令,最多允许 4,000 个字符。', + 'channels.edit.failed': '无法保存渠道更改,请检查填写内容后重试。', + 'channels.edit.saved': '渠道更改已保存。', + 'channels.edit.confirming': '更改已提交,正在等待确认。', + 'channels.edit.checkFailed': '更改已提交,但暂时无法确认结果,请重新检查。', + 'channels.edit.check': '重新检查', + 'channels.edit.discardTitle': '放弃更改?', + 'channels.edit.discardHelp': '未保存的渠道更改将会丢失。', 'workflowActivityVNext.nav.channels': '渠道', 'channels.title': '将渠道连接到你的 Agent', 'channels.description': '选择渠道,连接你的机器人并选择它可以使用的服务。', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index 24aa2333f5..c684821351 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -1,6 +1,7 @@ import { ArrowLeftOutlined, DeleteOutlined, + EditOutlined, ExportOutlined, } from '@ant-design/icons'; import { Button, Modal } from 'antd'; @@ -10,7 +11,10 @@ import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; -import { buildWorkflowActivitySectionHref } from '../navigation'; +import { + buildChannelEditHref, + buildWorkflowActivitySectionHref, +} from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import ChannelAuthorizedServices from './ChannelAuthorizedServices'; import { @@ -264,6 +268,15 @@ export default function ChannelDetailsPage({
+ +
+ ) : null} +
+ + +
+ + setLeaveTarget(null)} + onOk={() => { + if (leaveTarget) history.push(leaveTarget); + setLeaveTarget(null); + }} + okText={t('channels.connect.discard', 'Discard')} + cancelText={t('channels.connect.stay', 'Stay')} + > + {t( + 'channels.edit.discardHelp', + 'Your unsaved channel changes will be lost.', + )} + + + ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx index 6536d8580b..7123c0798c 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx @@ -14,6 +14,8 @@ export default function ChannelServicePicker({ refreshing, disabled, retry, + editing = false, + usesDefaults = false, }: { readonly services: readonly ChannelServiceChoice[]; readonly selectedIds: readonly string[]; @@ -23,6 +25,8 @@ export default function ChannelServicePicker({ readonly refreshing: boolean; readonly disabled: boolean; readonly retry: () => void; + readonly editing?: boolean; + readonly usesDefaults?: boolean; }) { const [search, setSearch] = React.useState(''); const term = search.trim().toLocaleLowerCase(); @@ -47,9 +51,11 @@ export default function ChannelServicePicker({ {t('channels.connect.services', 'Services')} - {t('channels.connect.selected', '{count} selected', { - count: selectedIds.length, - })} + {usesDefaults + ? t('channels.edit.defaults', 'NyxID defaults') + : t('channels.connect.selected', '{count} selected', { + count: selectedIds.length, + })}

@@ -67,10 +73,15 @@ export default function ChannelServicePicker({ ) : failed ? (

- {t( - 'channels.connect.servicesError', - 'Could not load your services. Retry before connecting.', - )} + {editing + ? t( + 'channels.edit.servicesError', + 'Could not load your services. Try again before saving.', + ) + : t( + 'channels.connect.servicesError', + 'Could not load your services. Retry before connecting.', + )}

) : !services.length ? (
- {t( - 'channels.connect.servicesEmpty', - 'No services are available with your current NyxID authorization. You can connect this bot without service access.', - )} + {editing + ? t( + 'channels.edit.servicesEmpty', + 'No services are available with your current authorization.', + ) + : t( + 'channels.connect.servicesEmpty', + 'No services are available with your current NyxID authorization. You can connect this bot without service access.', + )}
) : (
@@ -170,12 +186,14 @@ export default function ChannelServicePicker({
)} -

- {t( - 'channels.connect.selectionHelp', - 'Only selected services will be available to this bot.', - )} -

+ {!usesDefaults ? ( +

+ {t( + 'channels.connect.selectionHelp', + 'Only selected services will be available to this bot.', + )} +

+ ) : null} ); } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx new file mode 100644 index 0000000000..e4aa77c76f --- /dev/null +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx @@ -0,0 +1,43 @@ +import { Input } from 'antd'; +import * as React from 'react'; +import { t } from '@/shared/i18n/messages'; + +export default function ChannelSkillField({ + value, + onChange, + disabled, + placeholder, + error, +}: { + readonly value: string; + readonly onChange: (value: string) => void; + readonly disabled: boolean; + readonly placeholder?: string; + readonly error?: string; +}) { + const id = React.useId(); + return ( +
+
+ +
+ onChange(event.target.value)} + /> + {error ? ( + + ) : null} +
+ ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index e57effb9d7..ea95e6437d 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -4,10 +4,8 @@ import { EyeInvisibleOutlined, EyeOutlined, } from '@ant-design/icons'; -import { useQuery } from '@tanstack/react-query'; import { Button, Input, Modal } from 'antd'; import * as React from 'react'; -import { listChannelServices } from '@/shared/api/channelServicesApi'; import { ChannelRegistrationError, type ChannelRegistrationFailure, @@ -19,8 +17,10 @@ import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { buildWorkflowActivitySectionHref } from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import ChannelServicePicker from './ChannelServicePicker'; +import ChannelSkillField from './ChannelSkillField'; import { getChannelWebhookBaseUrl } from './config'; import { channelConnectionCss } from './connectionStyles'; +import { useChannelServiceChoices } from './queries'; import { channelsCss } from './styles'; function registrationErrorMessage(reason: ChannelRegistrationFailure) { @@ -86,15 +86,7 @@ export default function TelegramConnectionPage({ const toast = useConsoleToast(); const webhookBaseUrl = getChannelWebhookBaseUrl(); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); - const services = useQuery({ - queryKey: ['channels', scopeId, 'service-choices'], - queryFn: ({ signal }) => listChannelServices(signal), - enabled: Boolean(scopeId), - retry: false, - staleTime: 0, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - }); + const services = useChannelServiceChoices(scopeId); const invalidSelection = selectedIds.some( (id) => !services.data?.some( @@ -307,24 +299,15 @@ export default function TelegramConnectionPage({ onChange={(event) => setChannelName(event.target.value)} />
-
-
- -
- setSkillName(event.target.value)} - /> -
+
+ ['channels', scopeId, 'runtime-config', registrationId] as const, + services: (scopeId: string) => + ['channels', scopeId, 'service-choices'] as const, list: (scopeId: string) => ['channels', scopeId, 'registrations'] as const, bots: (scopeId: string) => ['channels', scopeId, 'bot-identities'] as const, status: (scopeId: string, registrationId: string) => ['channels', scopeId, 'status', registrationId] as const, }; +export function useChannelServiceChoices(scopeId: string) { + return useQuery({ + queryKey: channelKeys.services(scopeId), + queryFn: ({ signal }) => listChannelServices(signal), + enabled: Boolean(scopeId), + retry: false, + staleTime: 0, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }); +} + export function useChannelBotIdentities(scopeId: string, enabled: boolean) { return useQuery({ queryKey: channelKeys.bots(scopeId), diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx index 9baf2a3e99..77a6d38274 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx @@ -3,6 +3,7 @@ import { t } from '@/shared/i18n/messages'; import ActivityPage from './activity/ActivityPage'; import RunDetailPage from './activity/RunDetailPage'; import ChannelDetailsPage from './channels/ChannelDetailsPage'; +import ChannelEditPage from './channels/ChannelEditPage'; import ChannelsPage from './channels/ChannelsPage'; import TelegramConnectionPage from './channels/TelegramConnectionPage'; import { useConsoleLocation } from './hooks/useConsoleLocation'; @@ -60,6 +61,16 @@ const WorkflowActivityVNextPage: React.FC = () => { if (pathname.endsWith('/channels/connect/telegram')) { return ; } + const channelEditMatch = /\/channels\/([^/]+)\/edit$/.exec(pathname); + if (channelEditMatch) { + return ( + + ); + } const channelMatch = /\/channels\/([^/]+)$/.exec(pathname); if (channelMatch) { return ( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts index 75ecc3d008..a98da0faf7 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/navigation.ts @@ -87,3 +87,10 @@ export function buildChannelDetailsHref( export function buildTelegramConnectionHref(scopeId: string): string { return `${buildWorkflowActivitySectionHref(scopeId, 'channels')}/connect/telegram`; } + +export function buildChannelEditHref( + scopeId: string, + registrationId: string, +): string { + return `${buildChannelDetailsHref(scopeId, registrationId)}/edit`; +} diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts new file mode 100644 index 0000000000..94e534a580 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts @@ -0,0 +1,73 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { channelRuntimeConfigFixture } from '../../../tests/fixtures/channelRuntimeConfig'; +import { channelRuntimeConfigApi } from './channelRuntimeConfigApi'; +import { ChannelApiError } from './channelsApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +const fetchMock = jest.mocked(authFetch); +const response = (value: unknown) => + ({ ok: true, status: 200, json: async () => value }) as Response; + +it('reads the exact identity and legacy skill while discarding unexpected credential fields', async () => { + fetchMock.mockResolvedValue( + response({ + ...channelRuntimeConfigFixture, + registration_id: 'registration/a', + runtime_config: { + ...channelRuntimeConfigFixture.runtime_config, + default_skill: { name: '', version: '' }, + access_token: 'TEST_ONLY_SECRET', + }, + agent_key: { raw_key: 'TEST_ONLY_SECRET' }, + webhook_url: 'TEST_ONLY_SECRET', + }), + ); + const detail = await channelRuntimeConfigApi.get( + 'registration/a', + 'scope-alpha', + ); + expect(fetchMock.mock.calls[0][0]).toBe( + '/api/channels/registrations/registration%2Fa/runtime-config', + ); + expect(detail.runtimeConfig.defaultSkill).toEqual({ + name: 'team-helper', + version: '2.1', + }); + expect(JSON.stringify(detail)).not.toContain('TEST_ONLY_SECRET'); + fetchMock.mockResolvedValue( + response({ ...channelRuntimeConfigFixture, scope_id: 'scope-other' }), + ); + await expect( + channelRuntimeConfigApi.get('registration-alpha', 'scope-alpha'), + ).rejects.toEqual(new ChannelApiError(404)); +}); + +it('rejects an unrecognized config or receipt instead of replacing it with defaults', async () => { + fetchMock.mockResolvedValue(response(channelRuntimeConfigFixture)); + const detail = await channelRuntimeConfigApi.get( + 'registration-alpha', + 'scope-alpha', + ); + fetchMock.mockResolvedValue( + response({ + ...channelRuntimeConfigFixture, + runtime_config: { + ...channelRuntimeConfigFixture.runtime_config, + credential_source_mode: 'future_mode', + }, + }), + ); + await expect( + channelRuntimeConfigApi.get('registration-alpha', 'scope-alpha'), + ).rejects.toThrow('Unsupported channel credential source.'); + fetchMock.mockResolvedValue( + response({ + status: 'accepted', + registration_id: 'registration-other', + command_id: 'command-alpha', + }), + ); + await expect( + channelRuntimeConfigApi.update('registration-alpha', detail), + ).rejects.toThrow('not acknowledged'); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts new file mode 100644 index 0000000000..d073a05455 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts @@ -0,0 +1,241 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { ChannelApiError, registrationPath } from './channelsApi'; +import { expectArray, expectRecord, readString } from './http/decoders'; + +export interface ChannelRuntimeConfig { + readonly instructions: string; + readonly defaultSkill: { readonly name: string; readonly version: string }; + readonly toolSetRefs: readonly string[]; + readonly extraToolNames: readonly string[]; + readonly serviceSelectors: readonly { + readonly serviceSlug: string; + readonly endpointNames: readonly string[]; + }[]; + readonly credentialSourceMode: + | 'registration_agent_key' + | 'sender_binding' + | 'unspecified'; +} + +export interface ChannelConfigUpdate { + readonly authorizationMode: 'nyxid_default' | 'explicit_service_allowlist'; + readonly serviceIds: readonly string[]; + readonly runtimeConfig: ChannelRuntimeConfig; +} + +export interface ChannelConfigDetail extends ChannelConfigUpdate { + readonly registrationId: string; + readonly scopeId: string; + readonly platform: string; + readonly stateVersion: number; +} + +export interface ChannelConfigReceipt { + readonly registrationId: string; + readonly commandId: string; +} + +export type ChannelConfigField = + | 'skill' + | 'version' + | 'instructions' + | 'services'; +export class ChannelConfigError extends ChannelApiError { + constructor( + status: number, + readonly fields: readonly ChannelConfigField[], + ) { + super(status); + this.name = 'ChannelConfigError'; + } +} + +function strings(value: unknown, label: string): string[] { + return expectArray(value, label, (item) => { + if (typeof item !== 'string') throw new Error(`Invalid ${label}.`); + return item; + }); +} + +function decodeDetail( + value: unknown, + registrationId: string, + scopeId: string, +): ChannelConfigDetail { + const row = expectRecord(value, 'Channel configuration'); + if (row.registration_id !== registrationId || row.scope_id !== scopeId) + throw new ChannelApiError(404); + if ( + row.authorization_mode !== 'nyxid_default' && + row.authorization_mode !== 'explicit_service_allowlist' + ) + throw new Error('Unsupported channel authorization.'); + if ( + typeof row.state_version !== 'number' || + !Number.isSafeInteger(row.state_version) || + row.state_version < 0 + ) + throw new Error('Invalid channel configuration version.'); + const config = expectRecord( + row.runtime_config, + 'Channel runtime configuration', + ); + // The top-level skill includes the backend's legacy registration fallback. + const skill = expectRecord( + row.default_skill ?? config.default_skill, + 'Channel skill', + ); + const mode = config.credential_source_mode; + if ( + mode !== 'registration_agent_key' && + mode !== 'sender_binding' && + mode !== 'unspecified' + ) + throw new Error('Unsupported channel credential source.'); + return { + registrationId, + scopeId, + platform: readString(row, 'platform', 'Channel platform'), + stateVersion: row.state_version, + authorizationMode: row.authorization_mode, + serviceIds: + row.authorization_mode === 'nyxid_default' + ? [] + : strings(row.service_ids, 'Service IDs'), + // Project only the editable contract; never cache the transport object. + runtimeConfig: { + instructions: readString(config, 'instructions', 'Bot instructions'), + defaultSkill: { + name: readString(skill, 'name', 'Skill name'), + version: readString(skill, 'version', 'Skill version'), + }, + toolSetRefs: strings(config.tool_set_refs, 'Tool set references'), + extraToolNames: strings(config.extra_tool_names, 'Extra tool names'), + serviceSelectors: expectArray( + config.nyxid_service_selectors, + 'Service selectors', + (value) => { + const selector = expectRecord(value, 'Service selector'); + return { + serviceSlug: readString(selector, 'service_slug', 'Service slug'), + endpointNames: strings(selector.endpoint_names, 'Endpoint names'), + }; + }, + ), + credentialSourceMode: mode, + }, + }; +} + +export function channelConfigPayload(input: ChannelConfigUpdate) { + const config = input.runtimeConfig; + return { + authorization_mode: input.authorizationMode, + service_ids: [...input.serviceIds].sort(), + runtime_config: { + instructions: config.instructions.trim(), + default_skill: { + name: config.defaultSkill.name.trim(), + version: config.defaultSkill.version.trim(), + }, + tool_set_refs: [...config.toolSetRefs], + extra_tool_names: [...config.extraToolNames], + nyxid_service_selectors: config.serviceSelectors.map((selector) => ({ + service_slug: selector.serviceSlug, + endpoint_names: [...selector.endpointNames], + })), + credential_source_mode: config.credentialSourceMode, + }, + }; +} + +export function channelConfigMatches( + actual: ChannelConfigUpdate, + expected: ChannelConfigUpdate, +): boolean { + return ( + JSON.stringify(channelConfigPayload(actual)) === + JSON.stringify(channelConfigPayload(expected)) + ); +} + +function decodeFieldErrors(value: unknown): ChannelConfigField[] { + if (!value || typeof value !== 'object') return []; + const fields = new Set(); + if ( + 'error' in value && + typeof value.error === 'string' && + [ + 'invalid_service_ids', + 'nyxid_user_service_not_accessible', + 'service_owner_forbidden', + 'channel_authorization_contract_invalid', + ].includes(value.error) + ) + fields.add('services'); + if ('field_errors' in value && Array.isArray(value.field_errors)) { + for (const error of value.field_errors) { + if (!error || typeof error !== 'object' || !('field' in error)) continue; + const field = error.field; + if (field === 'runtime_config.instructions') fields.add('instructions'); + else if (field === 'runtime_config.default_skill.name') + fields.add('skill'); + else if (field === 'runtime_config.default_skill.version') + fields.add('version'); + else if ( + typeof field === 'string' && + (field === 'service_ids' || + field.startsWith('runtime_config.nyxid_service_selectors')) + ) + fields.add('services'); + } + } + return [...fields]; +} + +export const channelRuntimeConfigApi = { + async get( + registrationId: string, + scopeId: string, + signal?: AbortSignal, + ): Promise { + const response = await authFetch( + `${registrationPath(registrationId)}/runtime-config`, + { + signal, + cache: 'no-store', + headers: { Accept: 'application/json' }, + }, + ); + if (!response.ok) throw new ChannelApiError(response.status); + return decodeDetail(await response.json(), registrationId, scopeId); + }, + async update( + registrationId: string, + input: ChannelConfigUpdate, + ): Promise { + const response = await authFetch( + `${registrationPath(registrationId)}/runtime-config`, + { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(channelConfigPayload(input)), + }, + ); + const value: unknown = await response.json().catch(() => null); + if (!response.ok) + throw new ChannelConfigError(response.status, decodeFieldErrors(value)); + const receipt = expectRecord(value, 'Channel configuration update'); + if ( + receipt.status !== 'accepted' || + receipt.registration_id !== registrationId + ) + throw new Error('Channel configuration update was not acknowledged.'); + const commandId = readString(receipt, 'command_id', 'Channel command'); + if (!commandId.trim()) throw new Error('Missing channel command.'); + return { registrationId, commandId }; + }, +}; diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 55b0561fa8..040a17dd9d 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -216,7 +216,7 @@ async function request( return decode(await response.json()); } -function registrationPath(registrationId: string): string { +export function registrationPath(registrationId: string): string { if (!registrationId.trim()) throw new Error('Missing channel registration ID.'); return `/api/channels/registrations/${encodeURIComponent(registrationId)}`; diff --git a/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts b/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts new file mode 100644 index 0000000000..945574aa8a --- /dev/null +++ b/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts @@ -0,0 +1,19 @@ +export const channelRuntimeConfigFixture = { + registration_id: 'registration-alpha', + scope_id: 'scope-alpha', + platform: 'telegram', + state_version: 12, + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], + default_skill: { name: 'team-helper', version: '2.1' }, + runtime_config: { + instructions: 'Keep replies concise.', + default_skill: { name: 'team-helper', version: '2.1' }, + tool_set_refs: ['approved-tools'], + extra_tool_names: ['calendar_lookup'], + nyxid_service_selectors: [ + { service_slug: 'api-github', endpoint_names: ['read_issue'] }, + ], + credential_source_mode: 'registration_agent_key', + }, +}; From 21297397aa16faf733c61b1548e57ed7eb059777 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 20:27:02 +0800 Subject: [PATCH 151/167] Remove advanced settings from channel editor --- .../docs/2026-09-15-channel-runtime-editor.md | 18 +-- .../src/locales/channelMessages.en-US.ts | 7 -- .../src/locales/channelMessages.zh-CN.ts | 7 -- .../channels/ChannelEditPage.test.tsx | 21 ++-- .../channels/ChannelEditPage.tsx | 118 +++--------------- .../channels/connectionStyles.ts | 5 - 6 files changed, 37 insertions(+), 139 deletions(-) diff --git a/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md index 7428dae328..60f7787214 100644 --- a/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md +++ b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md @@ -11,10 +11,11 @@ and the existing Telegram connection form. optional state, input, and field error presentation. - Both pages use `ChannelServicePicker`, `useChannelServiceChoices`, the existing connection styles, shell, loading skeleton, and toast provider. -- The primary form contains Skill name and Services. Advanced settings is - collapsed initially and contains Skill version and Bot instructions. +- The form contains only Skill name and Services, with no Advanced settings + section or inputs for Skill version and Bot instructions. - Channel names and bot tokens are not editable through this API. Tool sets, - extra tool names, and credential source are retained from the loaded config. + extra tool names, instructions, skill version, and credential source are + retained from the loaded config. ## API Behavior @@ -40,9 +41,10 @@ The API contract was checked against `origin/feature/integrate` at configuration and have an authoritative state version newer than the initial GET. Delayed or failed confirmation preserves the request and offers Check again, which issues only GET. There is no background polling. -- Backend field errors map to localized fields without retaining raw diagnostic - messages. Unrecognized config modes fail closed; unexpected credential fields - are discarded by the adapter. +- Backend errors for editable fields map to localized field messages; errors + for other fields use the shared save-failure toast. Raw diagnostic messages + are not retained. Unrecognized config modes fail closed; unexpected credential + fields are discarded by the adapter. - Cancel and shell navigation protect unsaved changes. Reload/close uses the native before-unload warning. Late responses after unmount cannot navigate or report success. @@ -69,8 +71,8 @@ Existing creation, channel listing/details, API, navigation, route configuration and locale tests protect the reused surfaces. Browser verification uses the configured remote backend: actual configuration -prefill, service selection, collapsed/expanded advanced fields, and desktop and -390px mobile layouts. It does not submit a configuration change to the live bot. +prefill, service selection, the two-field form, and desktop and 390px mobile +layouts. It does not submit a configuration change to the live bot. The local preview runs on port 5193 with its OAuth callback on the same origin. Local verification is restricted to affected Jest files, changed-file Biome, diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 7c9287f80b..27f8f2b5bb 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -5,9 +5,6 @@ export default { 'channels.edit.loading': 'Loading channel configuration', 'channels.edit.configuration': 'Configuration', 'channels.edit.save': 'Save changes', - 'channels.edit.advanced': 'Advanced settings', - 'channels.edit.version': 'Skill version', - 'channels.edit.instructions': 'Bot instructions', 'channels.edit.defaults': 'NyxID defaults', 'channels.edit.useDefaults': 'Use NyxID defaults', 'channels.edit.unavailableService': 'Unavailable service', @@ -21,10 +18,6 @@ export default { 'Review your selected services and try again.', 'channels.edit.skillError': 'Check the skill name. Use no more than 128 characters.', - 'channels.edit.versionError': - 'Check the skill version and name. Use no more than 128 characters.', - 'channels.edit.instructionsError': - 'Check the bot instructions. Use no more than 4,000 characters.', 'channels.edit.failed': 'Could not save channel changes. Review your choices and try again.', 'channels.edit.saved': 'Channel changes saved.', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 71963aaf42..942e34bf14 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -5,9 +5,6 @@ export default { 'channels.edit.loading': '正在加载渠道配置', 'channels.edit.configuration': '配置', 'channels.edit.save': '保存更改', - 'channels.edit.advanced': '高级设置', - 'channels.edit.version': 'Skill 版本', - 'channels.edit.instructions': '机器人行为指令', 'channels.edit.defaults': 'NyxID 默认授权', 'channels.edit.useDefaults': '使用 NyxID 默认授权', 'channels.edit.unavailableService': '不可用的服务', @@ -17,10 +14,6 @@ export default { '部分已保存的服务已不可用,请取消勾选后再保存。', 'channels.edit.selectionError': '请检查选中的服务后重试。', 'channels.edit.skillError': '请检查 Skill 名称,最多允许 128 个字符。', - 'channels.edit.versionError': - '请检查 Skill 版本和名称,版本最多允许 128 个字符。', - 'channels.edit.instructionsError': - '请检查机器人行为指令,最多允许 4,000 个字符。', 'channels.edit.failed': '无法保存渠道更改,请检查填写内容后重试。', 'channels.edit.saved': '渠道更改已保存。', 'channels.edit.confirming': '更改已提交,正在等待确认。', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx index 4932f3bda0..be2c800859 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx @@ -131,9 +131,11 @@ it('opens Edit from channel details and saves once, preserving hidden config unt await screen.findByRole('checkbox', { name: /GitHub work/ }), ).toBeChecked(); expect(screen.getByRole('button', { name: 'Save changes' })).toBeDisabled(); - expect(screen.getByLabelText('Bot instructions')).not.toBeVisible(); + expect(screen.queryByText('Advanced settings')).not.toBeInTheDocument(); expect( - screen.queryByLabelText(/Bot token|Channel name|Introduction/), + screen.queryByLabelText( + /Skill version|Bot instructions|Bot token|Channel name|Introduction/, + ), ).not.toBeInTheDocument(); editSkill(' changed-helper '); save(); @@ -198,7 +200,7 @@ it('keeps unavailable saved services visible until explicitly deselected and han error: 'invalid_runtime_config', field_errors: [ { - field: 'runtime_config.instructions', + field: 'runtime_config.default_skill.name', code: 'too_long', message: 'TEST_ONLY_SECRET', }, @@ -220,15 +222,18 @@ it('keeps unavailable saved services visible until explicitly deselected and han ); save(); await screen.findByText( - 'Check the bot instructions. Use no more than 4,000 characters.', + 'Check the skill name. Use no more than 128 characters.', ); expect(rejected).toBe(true); - expect( - await screen.findByRole('textbox', { name: 'Bot instructions' }), - ).toBeVisible(); + expect(screen.getByLabelText(/^Skill name/)).toHaveAttribute( + 'aria-invalid', + 'true', + ); expect(screen.getByLabelText(/^Skill name/)).toHaveValue('changed-helper'); expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); - expect(screen.getByRole('button', { name: 'Save changes' })).toBeEnabled(); + expect( + await screen.findByRole('button', { name: 'Save changes' }), + ).toBeEnabled(); }); it('revalidates a cached detail and retries an unavailable response without exposing a stale editable form', async () => { diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx index 05c9a1c4b3..d58877c258 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx @@ -1,6 +1,6 @@ import { ArrowLeftOutlined } from '@ant-design/icons'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { Button, Checkbox, Input, Modal } from 'antd'; +import { Button, Checkbox, Modal } from 'antd'; import * as React from 'react'; import { type ChannelConfigDetail, @@ -158,19 +158,12 @@ function ChannelEditForm({ const [skillName, setSkillName] = React.useState( baseline.runtimeConfig.defaultSkill.name, ); - const [version, setVersion] = React.useState( - baseline.runtimeConfig.defaultSkill.version, - ); - const [instructions, setInstructions] = React.useState( - baseline.runtimeConfig.instructions, - ); const [selectedIds, setSelectedIds] = React.useState([ ...baseline.serviceIds, ]); const [authorizationMode, setAuthorizationMode] = React.useState( baseline.authorizationMode, ); - const [advancedOpen, setAdvancedOpen] = React.useState(false); const [errors, setErrors] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); const [pending, setPending] = React.useState(null); @@ -219,8 +212,12 @@ function ChannelEditForm({ serviceIds: usesDefaults ? [] : selectedIds, runtimeConfig: { ...baseline.runtimeConfig, - instructions, - defaultSkill: { name: skillName, version }, + defaultSkill: { + name: skillName, + version: skillName.trim() + ? baseline.runtimeConfig.defaultSkill.version + : '', + }, serviceSelectors: selectionChanged ? baseline.runtimeConfig.serviceSelectors.filter( (selector) => @@ -300,15 +297,8 @@ function ChannelEditForm({ return; const invalid: ChannelConfigField[] = []; if (skillName.trim().length > 128) invalid.push('skill'); - if (version.trim().length > 128 || (version.trim() && !skillName.trim())) - invalid.push('version'); - if (instructions.trim().length > 4000) invalid.push('instructions'); setErrors(invalid); - if (invalid.length) { - if (invalid.includes('version') || invalid.includes('instructions')) - setAdvancedOpen(true); - return; - } + if (invalid.length) return; inFlight.current = true; setSubmitting(true); let accepted: PendingSave; @@ -324,11 +314,6 @@ function ChannelEditForm({ if (!mounted.current) return; if (error instanceof ChannelConfigError) { setErrors(error.fields); - if ( - error.fields.includes('version') || - error.fields.includes('instructions') - ) - setAdvancedOpen(true); if (error.fields.includes('services')) void services.refetch(); } toast.error( @@ -360,27 +345,17 @@ function ChannelEditForm({ await observe(accepted); } - const errorText = (field: ChannelConfigField) => + const errorText = (field: 'skill' | 'services') => errors.includes(field) ? field === 'skill' ? t( 'channels.edit.skillError', 'Check the skill name. Use no more than 128 characters.', ) - : field === 'version' - ? t( - 'channels.edit.versionError', - 'Check the skill version and name. Use no more than 128 characters.', - ) - : field === 'instructions' - ? t( - 'channels.edit.instructionsError', - 'Check the bot instructions. Use no more than 4,000 characters.', - ) - : t( - 'channels.edit.selectionError', - 'Review your selected services and try again.', - ) + : t( + 'channels.edit.selectionError', + 'Review your selected services and try again.', + ) : undefined; return ( @@ -396,10 +371,7 @@ function ChannelEditForm({ { - setSkillName(value); - if (!value.trim()) setVersion(''); - }} + onChange={setSkillName} disabled={locked} error={errorText('skill')} /> @@ -446,68 +418,6 @@ function ChannelEditForm({ {errorText('services')}

) : null} -
setAdvancedOpen(event.currentTarget.open)} - > - {t('channels.edit.advanced', 'Advanced settings')} -
-
- -
- setVersion(event.target.value)} - aria-invalid={Boolean(errorText('version'))} - aria-describedby={ - errorText('version') ? 'channel-version-error' : undefined - } - /> - {errorText('version') ? ( - - ) : null} -
-
-
- -
- setInstructions(event.target.value)} - aria-invalid={Boolean(errorText('instructions'))} - aria-describedby={ - errorText('instructions') - ? 'channel-instructions-error' - : undefined - } - /> - {errorText('instructions') ? ( - - ) : null} -
-
{pending && !completed.current ? (

diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts index 8d699e6437..1db52412b4 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts @@ -42,11 +42,6 @@ export const channelConnectionCss = ` .channels__service-state p { margin: 0 0 12px; } .channels__form-actions { align-items: center; border-top: 1px solid var(--channels-border); display: flex; gap: 12px; justify-content: space-between; margin-top: 28px; padding-top: 20px; } .channels__form-actions .ant-btn { font-size: 12px; min-height: 40px; padding-inline: 18px; } -.channels__advanced { border-top: 1px solid var(--channels-border); margin-top: 24px; padding-top: 20px; } -.channels__advanced summary { color: var(--wa-muted); cursor: pointer; font-size: 12px; line-height: 20px; } -.channels__advanced summary:focus-visible { outline: 2px solid var(--wa-blue); outline-offset: 4px; } -.channels__advanced[open] summary { margin-bottom: 20px; } -.channels__advanced .channels__field:last-child { margin-bottom: 0; } .channels__save-status { border-top: 1px solid var(--channels-border); color: var(--wa-muted); font-size: 12px; line-height: 20px; margin-top: 24px; padding-top: 16px; } .channels__save-status p { margin: 0 0 12px; } .channels__connection-form .ant-btn { white-space: normal; height: auto; } From da6f11e3ee6ab99c676720e06fbd527eecabc51c Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 20:36:53 +0800 Subject: [PATCH 152/167] Simplify channel authorized service labels --- .../docs/features/2026-09-14-channel-pages.md | 4 ++++ .../channels/ChannelAuthorizedServices.test.tsx | 12 ++++++++++-- .../channels/ChannelAuthorizedServices.tsx | 5 +---- .../pages/workflow-activity-vnext/channels/styles.ts | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index b4d4afca98..f323a3080a 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -193,6 +193,10 @@ their labels and slugs; the current session's selectable grants and service activity do not hide saved channel authorizations or add other services. Only safe ID, label and slug fields enter the service-name query cache. +Services appear as compact labels arranged horizontally, wrapping when the +available width is filled. Each service shows its resolved display name once; +the slug is not repeated on a second line. Long names wrap within their label. + Missing service names retain their saved IDs. A failed name lookup preserves other details and the saved authorization list, shows a safe toast, and offers a manual retry of the names alone. Explicit empty authorization shows no diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx index 5b51a8084a..2044ac5319 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.test.tsx @@ -25,7 +25,7 @@ const registration = { scope_id: 'scope-alpha', owned: true, authorization_mode: 'explicit_service_allowlist', - service_ids: ['us-work', 'us-model', 'us-deleted'], + service_ids: ['us-work', 'us-model', 'us-ornn', 'us-deleted'], default_skill_name: 'review-skill', }; const service = { @@ -46,6 +46,13 @@ const inventory = { slug: 'chrono-llm-public', is_active: true, }, + { + ...service, + id: 'us-ornn', + label: 'ornn-api', + slug: 'ornn-api', + is_active: true, + }, { ...service, id: 'us-not-selected', @@ -84,7 +91,8 @@ it('shows saved authorizations by exact ID, including services outside current g expect(await screen.findByText('Authorized services')).toBeInTheDocument(); expect(await screen.findByText('GitHub work')).toBeInTheDocument(); expect(screen.getByText('Chrono Public')).toBeInTheDocument(); - expect(screen.getByText('chrono-llm-public')).toBeInTheDocument(); + expect(screen.queryByText('chrono-llm-public')).not.toBeInTheDocument(); + expect(screen.getAllByText('ornn-api')).toHaveLength(1); expect(screen.getByText('us-deleted')).toBeInTheDocument(); expect( screen.queryByText('Not authorized for this channel'), diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx index 054ff3fbe0..679dba0395 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelAuthorizedServices.tsx @@ -80,10 +80,7 @@ export default function ChannelAuthorizedServices({ return (

  • {service ? ( - <> - {service.label} - {service.slug} - + {service.label} ) : ( {id} )} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index 79a89b61e7..44bee7b052 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -77,8 +77,8 @@ export const channelsCss = ` .channels__facts > div { align-items: baseline; display: grid; gap: 16px; grid-template-columns: 160px minmax(0, 1fr); padding: 10px 0; } .channels__facts dt { color: var(--wa-muted); font-size: 12px; } .channels__facts dd { font-size: 12px; margin: 0; overflow-wrap: anywhere; text-align: right; } -.channels__authorized-services ul { display: grid; gap: 12px; list-style: none; margin: 0; padding: 0; } -.channels__authorized-services li { display: flex; flex-direction: column; gap: 2px; } +.channels__authorized-services ul { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; list-style: none; margin: 0; padding: 0; } +.channels__authorized-services li { background: var(--wa-subtle); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); max-width: 100%; min-width: 0; padding: 4px 8px; text-align: left; } .channels__authorized-services strong { font-weight: 500; } .channels__authorized-services > .ant-btn { margin-top: 12px; } .channels__identifier-link { align-items: baseline; color: var(--wa-blue); display: inline-flex; gap: 6px; max-width: 100%; } From 83f53da00c599296d5e080cfb877addb7b2cc43b Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 20:41:31 +0800 Subject: [PATCH 153/167] Move channel detail actions to the top toolbar --- .../docs/2026-09-15-channel-runtime-editor.md | 2 + .../channels/ChannelDetailsPage.tsx | 78 ++++++++++--------- .../channels/styles.ts | 6 +- 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md index 60f7787214..e7d4f9d791 100644 --- a/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md +++ b/apps/aevatar-console-web/docs/2026-09-15-channel-runtime-editor.md @@ -2,6 +2,8 @@ Channel details now provides an Edit action at `/scopes/:scopeId/workflow-activity-vnext/channels/:registrationId/edit`. +Edit and Remove sit at the top right beside the breadcrumb, with a 12px gap. +On narrow screens, the action group wraps below the breadcrumb and stays right aligned. The editor follows the approved [Figma frame](https://www.figma.com/design/FaVJx5IZeQX9jHcUi55ndB/?node-id=63-327) and the existing Telegram connection form. diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index c684821351..4568c86c3b 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -115,23 +115,47 @@ export default function ChannelDetailsPage({ contentClassName="channels__content" > - +
    + + {registration ? ( +
    + + +
    + ) : null} +
    {registrations.isPending ? ( -
    - - -
    {removalAccepted ? (

    diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index 79a89b61e7..aac99de744 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -69,6 +69,8 @@ export const channelsCss = ` .channels__main--detail .channels__content { padding-top: 40px; } .channels__breadcrumb { align-items: center; color: var(--wa-muted); display: flex; flex-wrap: wrap; font-size: 12px; gap: 12px; margin-bottom: 24px; } .channels__breadcrumb a { align-items: center; color: var(--wa-blue); display: inline-flex; gap: 8px; min-height: 28px; } +.channels__detail-toolbar { align-items: center; display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; margin-bottom: var(--channels-space); } +.channels__detail-toolbar .channels__breadcrumb { margin-bottom: 0; } .channels__detail { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); padding: var(--channels-space); } .channels__detail-heading { align-items: center; display: flex; gap: 14px; margin-bottom: 24px; } .channels__detail-heading h1 { font-size: 16px; font-weight: 600; line-height: 24px; margin: 0; overflow-wrap: anywhere; } @@ -87,8 +89,8 @@ export const channelsCss = ` .channels__identifier-link:hover { color: var(--wa-blue); text-decoration-thickness: 2px; } .channels__facts .channels__badge { background: none; border: none; color: var(--wa-ink); font-size: 12px; padding: 0; } .channels__facts .channels__dot { display: none; } -.channels__detail-actions { margin-top: 20px; } -.channels__detail-actions .ant-btn { background: transparent; } +.channels__detail-actions { align-items: center; display: flex; flex: 0 0 auto; gap: 12px; margin-left: auto; } +.channels__detail-actions .ant-btn { background: transparent; min-height: var(--channels-action-height); } .channels__removal { align-items: center; display: flex; flex-wrap: wrap; gap: 16px; margin-top: 20px; } .channels__removal p { color: var(--wa-muted); line-height: 20px; margin: 0; } .channels__content a:focus-visible { border-radius: 3px; outline: 2px solid var(--wa-blue); outline-offset: 4px; } From 02c35ab3858de91fdb168214a5c053b4a28ac8a5 Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Tue, 15 Sep 2026 20:43:33 +0800 Subject: [PATCH 154/167] Show loading over the connected channels table during refresh --- .../docs/features/2026-09-14-channel-pages.md | 7 ++ .../channels/ChannelsPage.test.tsx | 91 +++++++++++++++++++ .../channels/ChannelsPage.tsx | 15 ++- .../channels/styles.ts | 2 +- 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index b4d4afca98..f0567cdf1b 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -11,6 +11,13 @@ runtime-configuration editor is outside this iteration of issue #3617. Channel refresh is explicitly user driven. Do not add background polling or refresh on focus/reconnection to these pages. +Manual Refresh keeps the connected table mounted and displays the shared +loading overlay over that table while the registration list, bot names, and +active status refreshes settle. Table actions are inert during the refresh; +the button blocks duplicate submissions. Completion or failure removes the +overlay and restores interaction, with the existing error toast and manual +retry behavior retained. + The page header and content are horizontally centered within the area beside the navigation rail, using the same maximum width and responsive side padding. The connection form and details share this alignment; the form keeps its diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index ba79f62981..f04b353c75 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -118,6 +118,97 @@ describe('Channel pages', () => { } }); + it.each([ + 200, 503, + ])('keeps the table busy until all manual refresh reads settle when the list returns %s', async (listStatus) => { + renderWithQueryClient(); + await screen.findByText('Team channel'); + await screen.findByText('Active'); + const table = screen.getByRole('table', { name: /^Connected/ }); + const refreshButton = screen.getByRole('button', { + name: 'Refresh channels', + }); + const pendingList = deferred(); + const pendingStatus = deferred(); + const pendingNames = deferred(); + fetchMock.mockClear(); + fetchMock.mockImplementation(async (input) => { + if (input === '/api/channels/registrations') return pendingList.promise; + if (input === statusPath) return pendingStatus.promise; + if (input === botsPath) return pendingNames.promise; + throw new Error(`Unexpected test request: ${String(input)}`); + }); + + fireEvent.click(refreshButton); + expect(refreshButton).toBeDisabled(); + expect( + screen.getByRole('status', { name: 'Loading channels' }), + ).toBeInTheDocument(); + expect(table.closest('[aria-busy]')).toHaveAttribute('aria-busy', 'true'); + expect(table).toHaveAttribute('inert'); + expect(within(table).getByText('Team channel')).toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Connect Telegram' }).closest('[inert]'), + ).toBeNull(); + fireEvent.click(refreshButton); + expect(fetchMock.mock.calls.map(([input]) => input).sort()).toEqual( + ['/api/channels/registrations', statusPath, botsPath].sort(), + ); + + await act(async () => { + pendingList.resolve(response([registration], listStatus)); + }); + expect(refreshButton).toBeDisabled(); + expect(table.closest('[aria-busy]')).toHaveAttribute('aria-busy', 'true'); + await act(async () => { + pendingStatus.resolve( + response({ registration_id: registration.id, status: 'active' }), + ); + }); + expect( + screen.getByRole('status', { name: 'Loading channels' }), + ).toBeInTheDocument(); + expect(refreshButton).toBeDisabled(); + + await act(async () => { + pendingNames.resolve( + response({ bots: [{ ...bot, label: 'Updated channel' }] }), + ); + }); + await waitFor(() => expect(refreshButton).toBeEnabled()); + expect(screen.getByRole('table', { name: /^Connected/ })).toBe(table); + expect(table.closest('[aria-busy]')).toHaveAttribute('aria-busy', 'false'); + expect(table).not.toHaveAttribute('inert'); + expect( + screen.queryByRole('status', { name: 'Loading channels' }), + ).not.toBeInTheDocument(); + expect( + await within(table).findByText('Updated channel'), + ).toBeInTheDocument(); + if (listStatus === 503) { + expect(mockToast.error).toHaveBeenCalledTimes(1); + expect(mockToast.error).toHaveBeenCalledWith( + 'Could not refresh channels. Try again.', + ); + } else { + expect(mockToast.error).not.toHaveBeenCalled(); + } + + fetchMock.mockImplementation(async (input) => { + if (input === '/api/channels/registrations') + return response([registration]); + if (input === statusPath) + return response({ registration_id: registration.id, status: 'active' }); + if (input === botsPath) return response({ bots: [bot] }); + throw new Error(`Unexpected test request: ${String(input)}`); + }); + fireEvent.click(refreshButton); + expect(refreshButton).toBeDisabled(); + await waitFor(() => expect(refreshButton).toBeEnabled()); + expect(fetchMock).toHaveBeenCalledTimes(6); + expect(await within(table).findByText('Team channel')).toBeInTheDocument(); + }); + it('loads the connected table, exact Ornn skill link and first-level navigation without enabling unsupported platforms', async () => { const pendingStatus = deferred(); const pendingNames = deferred(); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 29fcbcfe71..d58d267526 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import type { ChannelRegistration } from '@/shared/api/channelsApi'; import { t } from '@/shared/i18n/messages'; import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; +import { AevatarLoadingOverlay } from '@/shared/ui/AevatarLoading'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { buildChannelDetailsHref, @@ -98,6 +99,7 @@ export default function ChannelsPage({ const toast = useConsoleToast(); const queryClient = useQueryClient(); const [refreshing, setRefreshing] = React.useState(false); + const refreshInFlight = React.useRef(false); const reportedNamesError = React.useRef(0); React.useEffect(() => { if ( @@ -124,6 +126,8 @@ export default function ChannelsPage({ toast, ]); async function refresh() { + if (refreshInFlight.current) return; + refreshInFlight.current = true; setRefreshing(true); try { const [result] = await Promise.all([ @@ -142,6 +146,7 @@ export default function ChannelsPage({ t('channels.error.refresh', 'Could not refresh channels. Try again.'), ); } finally { + refreshInFlight.current = false; setRefreshing(false); } } @@ -242,7 +247,7 @@ export default function ChannelsPage({ -

    - ) : null}
    diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts index 1db52412b4..a44f6e4aa1 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/connectionStyles.ts @@ -42,8 +42,6 @@ export const channelConnectionCss = ` .channels__service-state p { margin: 0 0 12px; } .channels__form-actions { align-items: center; border-top: 1px solid var(--channels-border); display: flex; gap: 12px; justify-content: space-between; margin-top: 28px; padding-top: 20px; } .channels__form-actions .ant-btn { font-size: 12px; min-height: 40px; padding-inline: 18px; } -.channels__save-status { border-top: 1px solid var(--channels-border); color: var(--wa-muted); font-size: 12px; line-height: 20px; margin-top: 24px; padding-top: 16px; } -.channels__save-status p { margin: 0 0 12px; } .channels__connection-form .ant-btn { white-space: normal; height: auto; } @media (max-width: 767px) { .channels__name-fields { gap: 0; grid-template-columns: 1fr; } From 80077747d0a874f5685364cc62d4c6178cc4c16d Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 16 Sep 2026 10:50:44 +0800 Subject: [PATCH 156/167] Restore Telegram creation success details --- .../docs/features/2026-09-14-channel-pages.md | 23 +++- .../src/locales/channelMessages.en-US.ts | 9 +- .../src/locales/channelMessages.zh-CN.ts | 8 +- .../channels/ChannelDetailsPage.tsx | 43 +++++-- .../channels/ChannelsPage.test.tsx | 61 +++++++++ .../channels/TelegramConnectionPage.test.tsx | 120 ++++++++++++++---- .../channels/TelegramConnectionPage.tsx | 80 +++++++++--- .../channels/connectionStyles.ts | 2 + 8 files changed, 284 insertions(+), 62 deletions(-) diff --git a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md index ae9b3e7b12..1acceaf47d 100644 --- a/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md +++ b/apps/aevatar-console-web/docs/features/2026-09-14-channel-pages.md @@ -128,13 +128,17 @@ other services. Registration uses existing `POST /api/channels/registrations` with `platform`, `bot_token`, `label`, `default_skill_name`, `webhook_base_url`, and the explicit selection above. A `202`/`status: accepted` response must contain -the registration ID. After admission, the form clears its token, shows an -informational "request submitted" toast, and immediately returns to Channels. -It does not claim that provisioning has completed, wait for a list read, or -leave the user on a locked connection form. The Channels page owns the normal -list load and its existing manual Refresh action if the new row is not visible -yet. Inbound activity continues to use its real status. The form never queries -registrations; no polling, window-focus refresh, or reconnect refresh runs. +the registration ID. After admission, the form clears its token, retains only +the safe returned ID, and explicitly refreshes registrations once. A successful +fresh read must contain that exact ID, route scope and Telegram platform before +the success toast and navigation to the newly created channel's existing detail +page. The form does not return to the collection automatically or infer success +from cached rows after a failed read. Until confirmation, it preserves the +submitted choices and shows a pending state with Check again. This action only +repeats the GET, never the create POST; failed confirmation reads show a safe +error toast and remain retryable. No polling, window-focus refresh, or reconnect +refresh runs. Inbound activity continues to use its real status, independently +of registration creation. Registration failures, including 504 and network errors, show a shared error toast and restore editable fields and Connect Telegram. Inputs and still-authorized selections are preserved for an explicit manual @@ -187,6 +191,11 @@ connected rows and actions, shows a safe error toast and can be retried with Refresh. Refresh reads the names once with the list/status reads. No per-row name request, automatic retry, polling, focus or reconnect refresh is added. +Channel details use the same bot-identity query and exact join for their Channel +name heading. Missing or failed name reads keep the detail facts and actions +available with a local Reload channel name action. The title never substitutes +a compact bot ID, registration ID or Skill name for the label. + Channel details include Skill using the same name, optional version and Ornn link as the connected list. Missing skills show Not set. Reading names/skills does not request full runtime configuration or alter bot resources. diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 2f012c4de0..2a360d2a42 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -46,6 +46,7 @@ export default { 'channels.connectedDescription': 'Channels connected to your account.', 'channels.name.loading': 'Loading name…', 'channels.name.unavailable': 'Name unavailable', + 'channels.name.retry': 'Reload channel name', 'channels.column.name': 'Channel name', 'channels.skill.notSet': 'Not set', 'channels.skill.version': 'Version {version}', @@ -147,8 +148,12 @@ export default { 'Only selected services will be available to this bot.', 'channels.connect.selectionChanged': 'Some selected services are no longer available. Deselect them before connecting.', - 'channels.connect.accepted': - 'Telegram connection request submitted. Use Refresh if the channel is not visible yet.', + 'channels.connect.pending': + 'Your request was submitted. Check again to open your channel when it is ready.', + 'channels.connect.check': 'Check again', + 'channels.connect.success': 'Telegram channel created.', + 'channels.connect.error.confirmation': + 'Could not check your channel. Please check again.', 'channels.connect.submitted': 'Request submitted', 'channels.connect.error.token': 'Check the bot token from BotFather and try again.', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index cd8d603feb..d97beb8474 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -38,6 +38,7 @@ export default { 'channels.connectedDescription': '连接到你账户的渠道。', 'channels.name.loading': '正在加载名称…', 'channels.name.unavailable': '名称暂不可用', + 'channels.name.retry': '重新加载渠道名称', 'channels.column.name': 'Channel 名字', 'channels.skill.notSet': '未设置', 'channels.skill.version': '版本 {version}', @@ -127,8 +128,11 @@ export default { 'channels.connect.selectionHelp': '此机器人只能使用选中的服务。', 'channels.connect.selectionChanged': '部分选中服务已不可用,请取消勾选后再连接。', - 'channels.connect.accepted': - 'Telegram 连接请求已提交。如果渠道尚未显示,可点击刷新查看。', + 'channels.connect.pending': + '请求已提交。可点击再次检查,准备完成后将打开你的渠道详情。', + 'channels.connect.check': '再次检查', + 'channels.connect.success': 'Telegram 渠道已创建。', + 'channels.connect.error.confirmation': '暂时无法查看渠道,请再次检查。', 'channels.connect.submitted': '请求已提交', 'channels.connect.error.token': '请检查 BotFather 提供的令牌后重试。', 'channels.connect.error.botName': diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index 4568c86c3b..da28c88749 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -3,8 +3,9 @@ import { DeleteOutlined, EditOutlined, ExportOutlined, + ReloadOutlined, } from '@ant-design/icons'; -import { Button, Modal } from 'antd'; +import { Button, Modal, Tooltip } from 'antd'; import * as React from 'react'; import { channelsApi } from '@/shared/api/channelsApi'; import { t } from '@/shared/i18n/messages'; @@ -22,12 +23,15 @@ import { ChannelLink, ChannelLoadError, ChannelSkill, - compactChannelIdentifier, DeliveryStatus, InboundStatus, platformName, } from './presentation'; -import { useChannelRegistrations, useChannelStatus } from './queries'; +import { + useChannelBotIdentities, + useChannelRegistrations, + useChannelStatus, +} from './queries'; import { channelsCss } from './styles'; // Figma links to the NyxID website, separate from its API/OIDC authority. @@ -47,8 +51,13 @@ export default function ChannelDetailsPage({ const [removeError, setRemoveError] = React.useState(false); const registrations = useChannelRegistrations(scopeId); const registration = registrations.data?.find( - (row) => row.id === registrationId, + (row) => row.id === registrationId && row.scopeId === scopeId, ); + const bots = useChannelBotIdentities(scopeId, Boolean(registration?.botId)); + const channelName = bots.data?.find( + (bot) => + bot.id === registration?.botId && bot.platform === registration?.platform, + )?.label; const status = useChannelStatus(scopeId, registration ? registrationId : ''); const toast = useConsoleToast(); const completed = React.useRef(false); @@ -192,12 +201,26 @@ export default function ChannelDetailsPage({

    - {platformName(registration.platform)} ·{' '} - - {compactChannelIdentifier( - registration.botId ?? registration.id, - )} - + {channelName ?? + (registration.botId && bots.isPending + ? t('channels.name.loading', 'Loading name…') + : t('channels.name.unavailable', 'Name unavailable'))} + {registration.botId && !channelName && !bots.isPending ? ( + +

    {t('channels.registration', 'Registration')} {registration.id} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx index f04b353c75..90f0055bdd 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.test.tsx @@ -385,6 +385,67 @@ describe('Channel pages', () => { ).toBeInTheDocument(); }); + it('recovers the detail name independently and requires the exact bot ID and platform', async () => { + let nameReads = 0; + fetchMock.mockImplementation(async (input) => { + if (input === '/api/channels/registrations') + return response([registration]); + if (input === statusPath) + return response({ registration_id: registration.id, status: 'active' }); + if (input === botsPath) { + nameReads += 1; + if (nameReads === 1) return response({}, 503); + return response({ + bots: + nameReads === 2 + ? [ + { ...bot, id: registration.id, label: 'Wrong identity' }, + { ...bot, platform: 'lark', label: 'Wrong platform' }, + ] + : [bot], + }); + } + throw new Error('Unexpected request'); + }); + renderWithQueryClient( + , + ); + await screen.findByRole('heading', { level: 1, name: 'Name unavailable' }); + expect( + screen.getByRole('link', { name: 'Open review.skill in Ornn' }), + ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Edit' })).toBeEnabled(); + fireEvent.click( + screen.getByRole('button', { name: 'Reload channel name' }), + ); + await waitFor(() => expect(nameReads).toBe(2)); + await waitFor(() => + expect( + screen.getByRole('button', { name: 'Reload channel name' }), + ).toBeEnabled(), + ); + expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent( + 'Name unavailable', + ); + expect(document.body).not.toHaveTextContent('Wrong identity'); + expect(document.body).not.toHaveTextContent('Wrong platform'); + fireEvent.click( + screen.getByRole('button', { name: 'Reload channel name' }), + ); + await screen.findByRole('heading', { level: 1, name: 'Team channel' }); + expect( + fetchMock.mock.calls.filter( + ([input]) => input === '/api/channels/registrations', + ), + ).toHaveLength(1); + expect( + fetchMock.mock.calls.filter(([input]) => input === statusPath), + ).toHaveLength(1); + }); + it('links exact bot and agent key IDs to NyxID, hides Scope and keeps removal safe', async () => { fetchMock.mockImplementation(async (input) => input === statusPath diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx index 35ff8fe426..e5d9ff267e 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.test.tsx @@ -13,6 +13,7 @@ import { history } from '@/shared/navigation/history'; import { createNyxIDServiceSession } from '../../../../tests/fixtures/nyxidServiceSession'; import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; import WorkflowActivityVNextPage from '../index'; +import { channelKeys } from './queries'; import TelegramConnectionPage from './TelegramConnectionPage'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); @@ -46,6 +47,16 @@ const service = { }; const servicePath = 'https://nyx.example.test/api/v1/user-services'; const listPath = '/api/channels/registrations'; +const createdRegistration = { + id: 'registration-created', + platform: 'telegram', + scope_id: 'scope-alpha', + owned: true, + nyx_channel_bot_id: 'bot-created', + default_skill: { name: 'saved-skill', version: '1.2' }, + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], +}; function enterBotDetails() { fireEvent.change(screen.getByLabelText(/^Bot token/), { target: { value: 'TEST_ONLY_TOKEN' }, @@ -72,10 +83,11 @@ afterEach(() => { global.fetch = originalFetch; }); -it('returns to Channels after admission without waiting for a list read or adding automatic refresh', async () => { +it('confirms the exact submitted channel with fresh reads and retries only GET without automatic refresh', async () => { jest.useFakeTimers(); try { let attempts = 0; + let reads = 0; fetchMock.mockImplementation(async (input, init) => { if (init?.method === 'POST') { attempts += 1; @@ -86,6 +98,25 @@ it('returns to Channels after admission without waiting for a list read or addin 202, ); } + if (input === listPath) { + reads += 1; + if (reads === 1) return response({}, 503); + if (reads === 2) + return response([ + { + ...createdRegistration, + id: 'registration-new', + scope_id: 'other-scope', + }, + { + ...createdRegistration, + id: 'registration-new', + platform: 'lark', + }, + createdRegistration, + ]); + return response([{ ...createdRegistration, id: 'registration-new' }]); + } return input === servicePath ? response({ services: [] }) : response([]); }); const idleAndReturn = async () => { @@ -101,6 +132,9 @@ it('returns to Channels after admission without waiting for a list read or addin const view = renderWithQueryClient( , ); + view.queryClient.setQueryData(channelKeys.list('scope-alpha'), [ + { id: 'registration-new', scopeId: 'scope-alpha', platform: 'telegram' }, + ]); await screen.findByText(/No services are available/); await idleAndReturn(); expect(fetchMock.mock.calls.map(([input]) => input)).toEqual([servicePath]); @@ -113,12 +147,12 @@ it('returns to Channels after admission without waiting for a list read or addin expect(history.replace).not.toHaveBeenCalled(); expect(attempts).toBe(1); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); - await screen.findByText(/Telegram connection request submitted/); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels', + await screen.findByText( + 'Could not check your channel. Please check again.', ); + expect(history.replace).not.toHaveBeenCalled(); expect( - screen.queryByRole('button', { name: 'Check again' }), + screen.queryByText('Telegram channel created.'), ).not.toBeInTheDocument(); expect( screen.getByRole('button', { name: 'Request submitted' }), @@ -129,13 +163,23 @@ it('returns to Channels after admission without waiting for a list read or addin expect(leaving.defaultPrevented).toBe(false); fireEvent.submit(screen.getByRole('form', { name: 'Connect Telegram' })); await idleAndReturn(); - expect(history.replace).toHaveBeenCalledTimes(1); + expect(history.replace).not.toHaveBeenCalled(); expect(attempts).toBe(2); - expect( - fetchMock.mock.calls.filter( - ([input, init]) => input === listPath && init?.method !== 'POST', - ), - ).toHaveLength(0); + expect(reads).toBe(1); + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + await waitFor(() => + expect(screen.getByRole('button', { name: 'Check again' })).toBeEnabled(), + ); + await idleAndReturn(); + expect(reads).toBe(2); + expect(history.replace).not.toHaveBeenCalled(); + fireEvent.click(screen.getByRole('button', { name: 'Check again' })); + await screen.findByText('Telegram channel created.'); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-new', + ); + expect(attempts).toBe(2); + expect(reads).toBe(3); view.unmount(); } finally { focusManager.setFocused(undefined); @@ -147,13 +191,30 @@ it('returns to Channels after admission without waiting for a list read or addin } }); -it('searches services, enforces permissions and returns to Channels after one accepted submission', async () => { +it('creates once and opens the new channel details with its saved name, skill and service grants', async () => { + jest.mocked(history.replace).mockRestore(); let resolvePost!: (value: Response) => void; const post = new Promise((resolve) => { resolvePost = resolve; }); fetchMock.mockImplementation(async (input, init) => { if (init?.method === 'POST') return post; + if (input === listPath) return response([createdRegistration]); + if (input === 'https://nyx.example.test/api/v1/channel-bots') + return response({ + bots: [ + { + id: 'bot-created', + platform: 'telegram', + label: 'Saved channel name', + }, + ], + }); + if (input === `${listPath}/registration-created/status`) + return response({ + registration_id: 'registration-created', + status: 'pending_webhook', + }); if (input === servicePath) return response({ services: [ @@ -250,17 +311,26 @@ it('searches services, enforces permissions and returns to Channels after one ac ), ); expect( - await screen.findByText(/Telegram connection request submitted/), + await screen.findByText('Telegram channel created.'), ).toBeInTheDocument(); - expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); expect( - screen.queryByText( - 'Telegram added. Send your bot a message to get started.', - ), - ).not.toBeInTheDocument(); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels', + await screen.findByRole('heading', { + level: 1, + name: 'Saved channel name', + }), + ).toBeInTheDocument(); + expect(window.location.pathname).toBe( + '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-created', ); + const details = screen.getByRole('region', { name: 'Channel details' }); + expect( + within(details).getByRole('link', { name: 'Open saved-skill in Ornn' }), + ).toBeInTheDocument(); + expect(within(details).getByText('Version 1.2')).toBeInTheDocument(); + expect(await within(details).findByText('GitHub work')).toBeInTheDocument(); + expect(within(details).queryByText('Chrono Public')).not.toBeInTheDocument(); + expect(screen.queryByLabelText(/^Bot token/)).not.toBeInTheDocument(); + expect(document.body).not.toHaveTextContent('TEST_ONLY'); expect( JSON.stringify( view.queryClient @@ -274,7 +344,7 @@ it('searches services, enforces permissions and returns to Channels after one ac fetchMock.mock.calls.filter( ([input, init]) => input === listPath && init?.method !== 'POST', ), - ).toHaveLength(0); + ).toHaveLength(2); }); it('selects and clears available results while preserving selections outside the search', async () => { @@ -455,7 +525,7 @@ it('shows a name-lookup toast and retries with the current token and custom fiel target: { value: 'custom-skill' }, }); fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); - await screen.findByText(/Telegram connection request submitted/); + await screen.findByText(/Your request was submitted/); expect(telegramFetch.mock.calls.map(([url]) => url)).toEqual([ `https://api.telegram.org/bot${telegramToken}/getMe`, ]); @@ -522,13 +592,13 @@ it.each([ fireEvent.click(screen.getByRole('button', { name: 'Connect Telegram' })); await waitFor(() => expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels', + '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-retried', ), ); expect(attempts).toBe(2); expect(screen.getByLabelText(/^Bot token/)).toHaveValue(''); expect( - await screen.findByText(/Telegram connection request submitted/), + await screen.findByText('Telegram channel created.'), ).toBeInTheDocument(); }); @@ -640,7 +710,7 @@ it('ignores an accepted response after leaving the connection form', async () => }); expect(history.replace).not.toHaveBeenCalled(); expect( - screen.queryByText(/Telegram connection request submitted/), + screen.queryByText(/Your request was submitted/), ).not.toBeInTheDocument(); expect( fetchMock.mock.calls.filter( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx index ea95e6437d..2de310d3db 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/TelegramConnectionPage.tsx @@ -14,13 +14,16 @@ import { import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; -import { buildWorkflowActivitySectionHref } from '../navigation'; +import { + buildChannelDetailsHref, + buildWorkflowActivitySectionHref, +} from '../navigation'; import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; import ChannelServicePicker from './ChannelServicePicker'; import ChannelSkillField from './ChannelSkillField'; import { getChannelWebhookBaseUrl } from './config'; import { channelConnectionCss } from './connectionStyles'; -import { useChannelServiceChoices } from './queries'; +import { useChannelRegistrations, useChannelServiceChoices } from './queries'; import { channelsCss } from './styles'; function registrationErrorMessage(reason: ChannelRegistrationFailure) { @@ -78,15 +81,19 @@ export default function TelegramConnectionPage({ const [skillName, setSkillName] = React.useState(''); const [selectedIds, setSelectedIds] = React.useState([]); const [submitting, setSubmitting] = React.useState(false); - const [submissionAccepted, setSubmissionAccepted] = React.useState(false); + const [submittedId, setSubmittedId] = React.useState(null); const [validationAttempted, setValidationAttempted] = React.useState(false); const [leaveTarget, setLeaveTarget] = React.useState(null); const inFlight = React.useRef(false); + const checking = React.useRef(false); + const completed = React.useRef(false); const mounted = React.useRef(true); const toast = useConsoleToast(); const webhookBaseUrl = getChannelWebhookBaseUrl(); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); const services = useChannelServiceChoices(scopeId); + const registrations = useChannelRegistrations(scopeId, false); + const submissionAccepted = submittedId !== null; const invalidSelection = selectedIds.some( (id) => !services.data?.some( @@ -129,6 +136,39 @@ export default function TelegramConnectionPage({ else history.push(target); } + async function checkRegistration(registrationId: string) { + if (checking.current || completed.current) return; + checking.current = true; + try { + // Only a successful fresh read can confirm this submission, not cached data. + const result = await registrations.refetch(); + if (!mounted.current) return; + if (result.isError) { + toast.error( + t( + 'channels.connect.error.confirmation', + 'Could not check your channel. Please check again.', + ), + ); + return; + } + if ( + !result.data?.some( + (row) => + row.id === registrationId && + row.scopeId === scopeId && + row.platform === 'telegram', + ) + ) + return; + completed.current = true; + toast.success(t('channels.connect.success', 'Telegram channel created.')); + history.replace(buildChannelDetailsHref(scopeId, registrationId)); + } finally { + checking.current = false; + } + } + async function connect(event: React.FormEvent) { event.preventDefault(); if (inFlight.current || locked) return; @@ -144,13 +184,17 @@ export default function TelegramConnectionPage({ inFlight.current = true; setSubmitting(true); try { - await channelsApi.registerTelegram({ + const receipt = await channelsApi.registerTelegram({ botToken, label: channelName, skillName, serviceIds: selectedIds, webhookBaseUrl, }); + if (!mounted.current) return; + setBotToken(''); + setSubmittedId(receipt.registrationId); + void checkRegistration(receipt.registrationId); } catch (failure) { if (!mounted.current) return; const reason = @@ -164,18 +208,6 @@ export default function TelegramConnectionPage({ inFlight.current = false; if (mounted.current) setSubmitting(false); } - if (!mounted.current) return; - setBotToken(''); - setSubmissionAccepted(true); - // Admission is not completion. The list owns loading the resulting channel; - // do not leave the form waiting on a one-shot read or start background polls. - toast.info( - t( - 'channels.connect.accepted', - 'Telegram connection request submitted. Use Refresh if the channel is not visible yet.', - ), - ); - history.replace(listHref); } return ( @@ -332,6 +364,22 @@ export default function TelegramConnectionPage({ {registrationErrorMessage('configuration')}

    ) : null} + {submittedId ? ( +
    +

    + {t( + 'channels.connect.pending', + 'Your request was submitted. Check again to open your channel when it is ready.', + )} +

    + +
    + ) : null}
    - {!initial && !config.isError ? ( + {!initial && !error ? ( - ) : !initial && config.isError ? ( + ) : !initial && error ? ( void config.refetch()} + error={error} + pending={pending} + retry={() => { + void config.refetch(); + void registrations.refetch(); + void bots.refetch(); + }} /> ) : initial ? ( { @@ -137,11 +160,13 @@ export default function ChannelEditPage({ function ChannelEditForm({ initial, + initialBot, title, setNavigate, readBack, }: { readonly initial: ChannelConfigDetail; + readonly initialBot: ChannelBotIdentity; readonly title: string; readonly setNavigate: React.Dispatch< React.SetStateAction<(target: string) => void> @@ -152,13 +177,12 @@ function ChannelEditForm({ const [skillName, setSkillName] = React.useState( baseline.runtimeConfig.defaultSkill.name, ); - const [selectedIds, setSelectedIds] = React.useState([ - ...baseline.serviceIds, - ]); - const [authorizationMode, setAuthorizationMode] = React.useState( - baseline.authorizationMode, - ); - const [errors, setErrors] = React.useState([]); + const [label, setLabel] = React.useState(initialBot.label ?? ''); + const [savedLabel, setSavedLabel] = React.useState(initialBot.label ?? ''); + const labelId = React.useId(); + const [errors, setErrors] = React.useState< + readonly (ChannelConfigField | 'label')[] + >([]); const [submitting, setSubmitting] = React.useState(false); const [leaveTarget, setLeaveTarget] = React.useState(null); const inFlight = React.useRef(false); @@ -166,41 +190,12 @@ function ChannelEditForm({ const completed = React.useRef(false); const toast = useConsoleToast(); const queryClient = useQueryClient(); - const services = useChannelServiceChoices(baseline.scopeId); const detailsHref = buildChannelDetailsHref( baseline.scopeId, baseline.registrationId, ); - const usesDefaults = authorizationMode === 'nyxid_default'; - const selectionChanged = - authorizationMode !== baseline.authorizationMode || - JSON.stringify([...selectedIds].sort()) !== - JSON.stringify([...baseline.serviceIds].sort()); - const missingIds = selectedIds.filter( - (id) => !services.data?.some((service) => service.id === id), - ); - const choices: readonly ChannelServiceChoice[] = [ - ...(services.data ?? []), - ...missingIds.map( - (id): ChannelServiceChoice => ({ - id, - slug: id, - label: t('channels.edit.unavailableService', 'Unavailable service'), - active: false, - allowed: false, - source: 'unknown', - organizationName: null, - }), - ), - ]; - const selectedSlugs = new Set( - services.data - ?.filter((service) => selectedIds.includes(service.id)) - .map((service) => service.slug), - ); const update: ChannelConfigUpdate = { - authorizationMode, - serviceIds: usesDefaults ? [] : selectedIds, + ...baseline, runtimeConfig: { ...baseline.runtimeConfig, defaultSkill: { @@ -209,18 +204,11 @@ function ChannelEditForm({ ? baseline.runtimeConfig.defaultSkill.version : '', }, - serviceSelectors: selectionChanged - ? baseline.runtimeConfig.serviceSelectors.filter( - (selector) => - !usesDefaults && selectedSlugs.has(selector.serviceSlug), - ) - : baseline.runtimeConfig.serviceSelectors, }, }; - const dirty = !channelConfigMatches(update, baseline); - const serviceBlocked = - !usesDefaults && - (!services.isSuccess || services.isFetching || missingIds.length > 0); + const labelChanged = label.trim() !== savedLabel; + const skillChanged = !channelConfigMatches(update, baseline); + const dirty = labelChanged || skillChanged; React.useEffect(() => { mounted.current = true; @@ -245,14 +233,16 @@ function ChannelEditForm({ }); }, [dirty, setNavigate]); - async function finishSave(expected: ChannelConfigUpdate) { - let confirmed = false; + async function finishSave(expected?: ChannelConfigUpdate) { + let confirmed = !expected; let readFailed = false; try { - const actual = await readBack(); - confirmed = - actual.stateVersion > baseline.stateVersion && - channelConfigMatches(actual, expected); + if (expected) { + const actual = await readBack(); + confirmed = + actual.stateVersion > baseline.stateVersion && + channelConfigMatches(actual, expected); + } } catch { readFailed = true; } @@ -286,52 +276,87 @@ function ChannelEditForm({ async function save(event: React.FormEvent) { event.preventDefault(); - if ( - inFlight.current || - submitting || - !dirty || - serviceBlocked || - completed.current - ) - return; - const invalid: ChannelConfigField[] = []; + if (inFlight.current || submitting || !dirty || completed.current) return; + const invalid: (ChannelConfigField | 'label')[] = []; + if (labelChanged && !isValidChannelBotLabel(label)) invalid.push('label'); if (skillName.trim().length > 128) invalid.push('skill'); setErrors(invalid); if (invalid.length) return; inFlight.current = true; setSubmitting(true); + let labelSaved = false; + let updatingLabel = labelChanged; try { - await channelRuntimeConfigApi.update(baseline.registrationId, update); + if (labelChanged) { + const updated = await updateChannelBotLabel(initialBot, label); + queryClient.setQueryData( + channelKeys.bots(baseline.scopeId), + (current) => + current?.map((bot) => (bot.id === updated.id ? updated : bot)), + ); + void queryClient.invalidateQueries({ + queryKey: channelKeys.bots(baseline.scopeId), + refetchType: 'none', + }); + if (!mounted.current) return; + setSavedLabel(updated.label ?? ''); + labelSaved = true; + updatingLabel = false; + } + if (skillChanged) + await channelRuntimeConfigApi.update( + baseline.registrationId, + update.runtimeConfig, + ); if (!mounted.current) return; // Read once for truthful feedback, then leave the editor even if the // accepted change is not visible yet. Never poll or resubmit to confirm. - await finishSave(update); + await finishSave(skillChanged ? update : undefined); } catch (error) { if (!mounted.current) return; if (error instanceof ChannelConfigError) { setErrors(error.fields); - if (error.fields.includes('services')) void services.refetch(); } + if ( + updatingLabel && + error instanceof ChannelApiError && + error.status === 400 + ) + setErrors(['label']); toast.error( - error instanceof ChannelApiError && [401, 403].includes(error.status) + !updatingLabel && + (labelSaved || savedLabel !== (initialBot.label ?? '')) ? t( - 'channels.connect.error.authorization', - 'Your session or service access needs attention. Sign in again and review your NyxID access.', + 'channels.edit.partialSave', + 'Label saved, but the skill name could not be updated. Try saving again.', ) - : error instanceof ChannelApiError && - error.status === 404 && - !( - error instanceof ChannelConfigError && - error.fields.includes('services') - ) + : (error instanceof ChannelApiError && + [401, 403].includes(error.status)) || + (error instanceof ChannelConfigError && + error.fields.includes('services')) ? t( - 'channels.error.unavailable', - 'This channel is unavailable or you do not have access.', + 'channels.connect.error.authorization', + 'Your session or service access needs attention. Sign in again and review your NyxID access.', ) - : t( - 'channels.edit.failed', - 'Could not save channel changes. Review your choices and try again.', - ), + : error instanceof ChannelApiError && + error.status === 404 && + !( + error instanceof ChannelConfigError && + error.fields.includes('services') + ) + ? t( + 'channels.error.unavailable', + 'This channel is unavailable or you do not have access.', + ) + : updatingLabel + ? t( + 'channels.edit.labelFailed', + 'Could not save the label. Check it and try again.', + ) + : t( + 'channels.edit.failed', + 'Could not save channel changes. Review your choices and try again.', + ), ); return; } finally { @@ -340,18 +365,18 @@ function ChannelEditForm({ } } - const errorText = (field: 'skill' | 'services') => - errors.includes(field) - ? field === 'skill' - ? t( - 'channels.edit.skillError', - 'Check the skill name. Use no more than 128 characters.', - ) - : t( - 'channels.edit.selectionError', - 'Review your selected services and try again.', - ) - : undefined; + const labelError = errors.includes('label') + ? t( + 'channels.edit.labelError', + 'Enter a non-empty label. If it is too long, shorten it and try again.', + ) + : undefined; + const skillError = errors.includes('skill') + ? t( + 'channels.edit.skillError', + 'Check the skill name. Use no more than 128 characters.', + ) + : undefined; return ( <> @@ -364,55 +389,34 @@ function ChannelEditForm({

    {t('channels.edit.configuration', 'Configuration')}

    +
    +
    + +
    + setLabel(event.target.value)} + /> + {labelError ? ( + + ) : null} +
    - {baseline.authorizationMode === 'nyxid_default' ? ( -
    - { - setAuthorizationMode( - event.target.checked - ? 'nyxid_default' - : 'explicit_service_allowlist', - ); - setSelectedIds([]); - }} - > - {t('channels.edit.useDefaults', 'Use NyxID defaults')} - -
    - ) : null} - void services.refetch()} - editing - usesDefaults={usesDefaults} + error={skillError} /> - {missingIds.length > 0 && services.isSuccess && !submitting ? ( -

    - {t( - 'channels.edit.missingServices', - 'Some saved services are no longer available. Deselect them before saving.', - )} -

    - ) : null} - {errorText('services') ? ( -

    - {errorText('services')} -

    - ) : null}
    diff --git a/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts index bdc65936f0..cced05ca23 100644 --- a/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelBotsApi.test.ts @@ -1,5 +1,8 @@ import { authFetch } from '@/shared/auth/fetch'; -import { listChannelBotIdentities } from './channelBotsApi'; +import { + listChannelBotIdentities, + updateChannelBotLabel, +} from './channelBotsApi'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); jest.mock('@/shared/auth/config', () => ({ @@ -56,3 +59,61 @@ it('rejects ambiguous identities and HTTP failures without retaining error bodie await expect(listChannelBotIdentities()).rejects.toThrow('403'); expect(json).not.toHaveBeenCalled(); }); + +it('PATCHes only a trimmed label with an encoded bot ID and retains only verified identity fields', async () => { + const bot = { id: 'bot/a', platform: 'telegram', label: 'Original label' }; + fetchMock.mockResolvedValue( + response({ + ...bot, + label: 'Updated label', + webhook_secret: 'TEST_ONLY_SECRET', + }), + ); + expect(await updateChannelBotLabel(bot, ' Updated label ')).toEqual({ + ...bot, + label: 'Updated label', + }); + expect(fetchMock).toHaveBeenCalledWith( + 'https://nyx.example.test/api/v1/channel-bots/bot%2Fa', + { + method: 'PATCH', + credentials: 'omit', + cache: 'no-store', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ label: 'Updated label' }), + }, + ); +}); + +it.each([ + { id: 'other-bot', platform: 'telegram', label: 'Updated' }, + { id: 'bot-alpha', platform: 'lark', label: 'Updated' }, + { id: 'bot-alpha', platform: 'telegram', label: 'Original' }, +])('rejects mismatched label update acknowledgements: %j', async (result) => { + fetchMock.mockResolvedValue(response(result)); + await expect( + updateChannelBotLabel( + { id: 'bot-alpha', platform: 'telegram', label: 'Original' }, + 'Updated', + ), + ).rejects.toThrow('not confirmed'); +}); + +it('does not retain upstream label error bodies', async () => { + const json = jest.fn().mockResolvedValue({ token: 'TEST_ONLY_SECRET' }); + fetchMock.mockResolvedValue({ + ok: false, + status: 403, + json, + } as unknown as Response); + await expect( + updateChannelBotLabel( + { id: 'bot-alpha', platform: 'telegram', label: 'Original' }, + 'Updated', + ), + ).rejects.toThrow('403'); + expect(json).not.toHaveBeenCalled(); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts b/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts index 5440db001b..b2136793f5 100644 --- a/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelBotsApi.ts @@ -24,20 +24,62 @@ export async function listChannelBotIdentities( }); if (!response.ok) throw new ChannelApiError(response.status); const body = expectRecord(await response.json(), 'Channel bots'); - const bots = expectArray(body.bots, 'Channel bots', (value) => { - const bot = expectRecord(value, 'Channel bot'); - const id = readString(bot, 'id', 'Channel bot ID'); - const platform = readString(bot, 'platform', 'Channel bot platform'); - if (!id.trim() || !platform.trim()) - throw new Error('Missing channel bot identity.'); - // Keep only safe identity fields, never the full upstream object. - return { - id, - platform, - label: readString(bot, 'label', 'Channel bot label').trim() || null, - }; - }); + const bots = expectArray(body.bots, 'Channel bots', decodeBotIdentity); if (new Set(bots.map((bot) => bot.id)).size !== bots.length) throw new Error('Ambiguous channel bot identity.'); return bots; } + +function decodeBotIdentity(value: unknown): ChannelBotIdentity { + const bot = expectRecord(value, 'Channel bot'); + const id = readString(bot, 'id', 'Channel bot ID'); + const platform = readString(bot, 'platform', 'Channel bot platform'); + if (!id.trim() || !platform.trim()) + throw new Error('Missing channel bot identity.'); + // Keep only safe identity fields, never the full upstream object. + return { + id, + platform, + label: readString(bot, 'label', 'Channel bot label').trim() || null, + }; +} + +export function isValidChannelBotLabel(label: string): boolean { + const value = label.trim(); + // NyxID validates the trimmed UTF-8 byte length, not JavaScript code units. + return Boolean(value) && new TextEncoder().encode(value).length <= 128; +} + +export async function updateChannelBotLabel( + bot: ChannelBotIdentity, + label: string, +): Promise { + const config = getNyxIDRuntimeConfig(); + if (config.configurationError || !config.baseUrl) + throw new Error('NyxID is unavailable.'); + const value = label.trim(); + if (!bot.id.trim() || !isValidChannelBotLabel(value)) + throw new Error('Invalid channel bot label.'); + const response = await authFetch( + `${config.baseUrl}/api/v1/channel-bots/${encodeURIComponent(bot.id)}`, + { + method: 'PATCH', + credentials: 'omit', + cache: 'no-store', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ label: value }), + }, + ); + if (!response.ok) throw new ChannelApiError(response.status); + const updated = decodeBotIdentity(await response.json()); + if ( + updated.id !== bot.id || + updated.platform !== bot.platform || + updated.label !== value + ) + throw new Error('Channel label update was not confirmed.'); + return updated; +} diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts index 94e534a580..55b0d6d51d 100644 --- a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts @@ -68,6 +68,6 @@ it('rejects an unrecognized config or receipt instead of replacing it with defau }), ); await expect( - channelRuntimeConfigApi.update('registration-alpha', detail), + channelRuntimeConfigApi.update('registration-alpha', detail.runtimeConfig), ).rejects.toThrow('not acknowledged'); }); diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts index d073a05455..1c64721e82 100644 --- a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts @@ -132,20 +132,24 @@ export function channelConfigPayload(input: ChannelConfigUpdate) { return { authorization_mode: input.authorizationMode, service_ids: [...input.serviceIds].sort(), - runtime_config: { - instructions: config.instructions.trim(), - default_skill: { - name: config.defaultSkill.name.trim(), - version: config.defaultSkill.version.trim(), - }, - tool_set_refs: [...config.toolSetRefs], - extra_tool_names: [...config.extraToolNames], - nyxid_service_selectors: config.serviceSelectors.map((selector) => ({ - service_slug: selector.serviceSlug, - endpoint_names: [...selector.endpointNames], - })), - credential_source_mode: config.credentialSourceMode, + runtime_config: runtimeConfigPayload(config), + }; +} + +function runtimeConfigPayload(config: ChannelRuntimeConfig) { + return { + instructions: config.instructions, + default_skill: { + name: config.defaultSkill.name.trim(), + version: config.defaultSkill.version, }, + tool_set_refs: [...config.toolSetRefs], + extra_tool_names: [...config.extraToolNames], + nyxid_service_selectors: config.serviceSelectors.map((selector) => ({ + service_slug: selector.serviceSlug, + endpoint_names: [...selector.endpointNames], + })), + credential_source_mode: config.credentialSourceMode, }; } @@ -212,7 +216,7 @@ export const channelRuntimeConfigApi = { }, async update( registrationId: string, - input: ChannelConfigUpdate, + input: ChannelRuntimeConfig, ): Promise { const response = await authFetch( `${registrationPath(registrationId)}/runtime-config`, @@ -222,7 +226,8 @@ export const channelRuntimeConfigApi = { Accept: 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify(channelConfigPayload(input)), + // Omitting service selection preserves the current backend authorization. + body: JSON.stringify({ runtime_config: runtimeConfigPayload(input) }), }, ); const value: unknown = await response.json().catch(() => null); From d09c8a042cdb1fefad80234fccae10bce06b8eda Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Wed, 16 Sep 2026 12:47:12 +0800 Subject: [PATCH 158/167] fix(channels): reuse the shared tooltip in channel details --- .../channels/ChannelDetailsPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index da28c88749..fb526693ee 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -5,12 +5,13 @@ import { ExportOutlined, ReloadOutlined, } from '@ant-design/icons'; -import { Button, Modal, Tooltip } from 'antd'; +import { Button, Modal } from 'antd'; import * as React from 'react'; import { channelsApi } from '@/shared/api/channelsApi'; import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; +import AevatarTooltip from '@/shared/ui/AevatarTooltip'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { buildChannelEditHref, @@ -206,7 +207,7 @@ export default function ChannelDetailsPage({ ? t('channels.name.loading', 'Loading name…') : t('channels.name.unavailable', 'Name unavailable'))} {registration.botId && !channelName && !bots.isPending ? ( - + +
    + + setLeaveTarget(null)} + onOk={() => { + if (leaveTarget) history.push(leaveTarget); + setLeaveTarget(null); + }} + okText={t('channels.connect.discard', 'Discard')} + cancelText={t('channels.connect.stay', 'Stay')} + > + {t( + 'channels.edit.discardHelp', + 'Your unsaved channel changes will be lost.', + )} + + + ); +} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx index fb526693ee..a829528c81 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelDetailsPage.tsx @@ -3,7 +3,6 @@ import { DeleteOutlined, EditOutlined, ExportOutlined, - ReloadOutlined, } from '@ant-design/icons'; import { Button, Modal } from 'antd'; import * as React from 'react'; @@ -11,7 +10,6 @@ import { channelsApi } from '@/shared/api/channelsApi'; import { t } from '@/shared/i18n/messages'; import { history } from '@/shared/navigation/history'; import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; -import AevatarTooltip from '@/shared/ui/AevatarTooltip'; import { useConsoleToast } from '@/shared/ui/ConsoleToast'; import { buildChannelEditHref, @@ -28,11 +26,7 @@ import { InboundStatus, platformName, } from './presentation'; -import { - useChannelBotIdentities, - useChannelRegistrations, - useChannelStatus, -} from './queries'; +import { useChannelDetail, useChannelRegistrations } from './queries'; import { channelsCss } from './styles'; // Figma links to the NyxID website, separate from its API/OIDC authority. @@ -50,16 +44,13 @@ export default function ChannelDetailsPage({ const [removalAccepted, setRemovalAccepted] = React.useState(false); const [hasWarnings, setHasWarnings] = React.useState(false); const [removeError, setRemoveError] = React.useState(false); - const registrations = useChannelRegistrations(scopeId); - const registration = registrations.data?.find( - (row) => row.id === registrationId && row.scopeId === scopeId, + const detail = useChannelDetail(scopeId, registrationId); + const registrations = useChannelRegistrations(scopeId, removalAccepted); + const registration = detail.data; + const stillListed = registrations.data?.some( + (row) => row.id === registrationId, ); - const bots = useChannelBotIdentities(scopeId, Boolean(registration?.botId)); - const channelName = bots.data?.find( - (bot) => - bot.id === registration?.botId && bot.platform === registration?.platform, - )?.label; - const status = useChannelStatus(scopeId, registration ? registrationId : ''); + const channelName = registration?.label; const toast = useConsoleToast(); const completed = React.useRef(false); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); @@ -69,7 +60,7 @@ export default function ChannelDetailsPage({ !removalAccepted || !registrations.isSuccess || registrations.isFetching || - registration || + stillListed || completed.current ) return; @@ -87,7 +78,7 @@ export default function ChannelDetailsPage({ removalAccepted, registrations.isSuccess, registrations.isFetching, - registration, + stillListed, hasWarnings, listHref, toast, @@ -166,17 +157,17 @@ export default function ChannelDetailsPage({
    ) : null}
    - {registrations.isPending ? ( + {detail.isPending ? ( - ) : registrations.isError && !registration ? ( + ) : detail.isError && !registration ? ( void registrations.refetch()} + error={detail.error} + pending={detail.isFetching} + retry={() => void detail.refetch()} /> ) : !registration ? (
    @@ -203,34 +194,13 @@ export default function ChannelDetailsPage({

    {channelName ?? - (registration.botId && bots.isPending - ? t('channels.name.loading', 'Loading name…') - : t('channels.name.unavailable', 'Name unavailable'))} - {registration.botId && !channelName && !bots.isPending ? ( - -

    {t('channels.registration', 'Registration')} {registration.id}

    - +
    @@ -255,20 +225,14 @@ export default function ChannelDetailsPage({
    {t('channels.inboundMessages', 'Inbound messages')}
    - +
    {t('channels.column.delivery', 'Workflow delivery')}
    diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx index 44615769e8..9d49b89b6d 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx @@ -1,24 +1,18 @@ import { act, + cleanup, fireEvent, screen, waitFor, - within, } from '@testing-library/react'; import * as React from 'react'; -import { channelRuntimeConfigApi } from '@/shared/api/channelRuntimeConfigApi'; import { authFetch } from '@/shared/auth/fetch'; import { persistAuthSession } from '@/shared/auth/session'; import { history } from '@/shared/navigation/history'; -import { channelRuntimeConfigFixture as fixture } from '../../../../tests/fixtures/channelRuntimeConfig'; import { createNyxIDServiceSession } from '../../../../tests/fixtures/nyxidServiceSession'; -import { - createTestQueryClient, - renderWithQueryClient, -} from '../../../../tests/reactQueryTestUtils'; +import { renderWithQueryClient } from '../../../../tests/reactQueryTestUtils'; import WorkflowActivityVNextPage from '../index'; -import ChannelEditPage from './ChannelEditPage'; -import { channelKeys } from './queries'; +import ChannelConfigurationPage from './ChannelConfigurationPage'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); jest.mock('@/shared/auth/config', () => ({ @@ -32,511 +26,255 @@ jest.mock('@/shared/studio/api', () => ({ getAuthSession: jest.fn().mockResolvedValue({ authenticated: false }), }, })); +const mockToast = { + success: jest.fn(), + warning: jest.fn(), + error: jest.fn(), + info: jest.fn(), +}; +jest.mock('@/shared/ui/ConsoleToast', () => ({ + ...jest.requireActual('@/shared/ui/ConsoleToast'), + useConsoleToast: () => mockToast, +})); const fetchMock = jest.mocked(authFetch); -const path = '/api/channels/registrations/registration-alpha/runtime-config'; -const servicePath = 'https://nyx.example.test/api/v1/user-services'; const response = (value: unknown, status = 200) => ({ ok: status >= 200 && status < 300, status, json: async () => value, }) as Response; -const botsPath = 'https://nyx.example.test/api/v1/channel-bots'; -const bot = { - id: 'bot-nyx-alpha', - platform: 'telegram', - label: 'Team channel', -}; -const registration = { - id: 'registration-alpha', - scope_id: 'scope-alpha', - platform: 'telegram', - nyx_channel_bot_id: bot.id, +const row = { + id: 'reg-alpha', + nyx_channel_bot_id: 'bot-alpha', + platform: 'discord', + label: 'Support bot', owned: true, + binding_status: 'bound', + availability_status: 'available', + nyx_status: 'active', + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], + state_version: 12, +}; +const unbound = { + ...row, + id: null, + binding_status: 'unbound', + skill_name: '', + authorization_mode: null, + service_ids: [], }; -function identityResponse(input: unknown) { - if (input === botsPath) return response({ bots: [bot] }); - if (input === '/api/channels/registrations') return response([registration]); - return undefined; -} const receipt = { status: 'accepted', - registration_id: 'registration-alpha', - command_id: 'command-save', + registration_id: 'reg-alpha', + command_id: 'cmd-one', }; +function catalogue(input: RequestInfo | URL) { + if (String(input).includes('/skill-search')) + return response({ + data: { + items: [ + { + guid: 'skill-guid', + name: 'support', + description: '', + myAccessReason: 'shared-via-org', + }, + { guid: 'skill-guid-new', name: 'new-support', description: '' }, + ], + meta: { hasMore: false }, + }, + error: null, + }); + if (String(input).endsWith('/user-services')) + return response({ + services: [ + { + id: 'user-service-github', + slug: 'api-github', + label: 'GitHub', + is_active: true, + credential_source: { type: 'personal' }, + }, + ], + }); + return null; +} const posts = () => fetchMock.mock.calls.filter(([, init]) => init?.method === 'POST'); -const postBody = () => JSON.parse(String(posts().at(-1)?.[1]?.body)); -const patches = () => - fetchMock.mock.calls.filter(([, init]) => init?.method === 'PATCH'); -const editLabel = (value: string) => - fireEvent.change(screen.getByLabelText('Label'), { target: { value } }); -const editSkill = (value: string) => - fireEvent.change(screen.getByLabelText(/^Skill name/), { target: { value } }); -const save = () => - fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); -const renderEditor = () => - renderWithQueryClient( - , - ); beforeEach(() => { fetchMock.mockReset(); + Object.values(mockToast).forEach((mock) => { + mock.mockReset(); + }); persistAuthSession(createNyxIDServiceSession()); jest.spyOn(history, 'push').mockImplementation(() => {}); jest.spyOn(history, 'replace').mockImplementation(() => {}); - fetchMock.mockImplementation(async (input, init) => - init?.method === 'POST' - ? response(receipt, 202) - : (identityResponse(input) ?? response(fixture)), - ); }); - -it('opens Edit from channel details and saves once, preserving hidden config and returning after one matching readback', async () => { - let resolvePost!: (value: Response) => void; - const pendingPost = new Promise((resolve) => { - resolvePost = resolve; - }); - let current = fixture; - fetchMock.mockImplementation(async (input, init) => { - if (init?.method === 'POST') return pendingPost; - const identity = identityResponse(input); - if (identity) return identity; - if (String(input).endsWith('/status')) - return response({ - registration_id: 'registration-alpha', - status: 'active', - }); - return response(current); - }); - window.history.replaceState( - {}, - '', - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha', +async function chooseSkill(name: string) { + fireEvent.mouseDown(await screen.findByRole('combobox')); + fireEvent.click( + await screen.findByText(name, { + selector: '.ant-select-item-option-content', + }), ); - const view = renderWithQueryClient(); - fireEvent.click(await screen.findByRole('button', { name: 'Edit' })); - expect(history.push).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha/edit', - ); - act(() => { +} + +it('binds an existing bot with shared Ornn skills, keeps selection on refresh and waits for bound inventory', async () => { + jest.useFakeTimers(); + try { + let committed = false; + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') return response(receipt, 202); + return catalogue(input) ?? response([committed ? row : unbound]); + }); window.history.replaceState( {}, '', - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha/edit', + '/scopes/scope-alpha/workflow-activity-vnext/channels/bind/bot-alpha', ); - window.dispatchEvent(new PopStateEvent('popstate')); - }); - expect(await screen.findByLabelText(/^Skill name/)).toHaveValue( - 'team-helper', - ); - expect(await screen.findByLabelText('Label')).toHaveValue('Team channel'); - expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); - expect(screen.queryByText('Services')).not.toBeInTheDocument(); - expect(fetchMock.mock.calls.some(([input]) => input === servicePath)).toBe( - false, - ); - expect(screen.getByRole('button', { name: 'Save changes' })).toBeDisabled(); - expect(screen.queryByText('Advanced settings')).not.toBeInTheDocument(); - expect( - screen.queryByLabelText( - /Skill version|Bot instructions|Bot token|Channel name|Introduction/, - ), - ).not.toBeInTheDocument(); - editSkill(' changed-helper '); - save(); - fireEvent.submit(screen.getByRole('form', { name: 'Edit Telegram' })); - expect(posts()).toHaveLength(1); - expect(posts()[0][0]).toBe(path); - expect(postBody()).toEqual({ - runtime_config: { - ...fixture.runtime_config, - default_skill: { name: 'changed-helper', version: '2.1' }, - }, - }); - expect(screen.getByLabelText(/^Skill name/)).toBeDisabled(); - current = { - ...fixture, - state_version: 13, - default_skill: { name: 'changed-helper', version: '2.1' }, - runtime_config: { - ...fixture.runtime_config, - default_skill: { name: 'changed-helper', version: '2.1' }, - }, - }; - await act(async () => resolvePost(response(receipt, 202))); - await screen.findByText('Channel changes saved.'); - expect( - screen.queryByRole('button', { name: 'Check again' }), - ).not.toBeInTheDocument(); - expect(posts()).toHaveLength(1); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 3, - ); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha', - ); - expect(view.queryClient.getMutationCache().getAll()).toHaveLength(0); -}); - -it('returns to details when an accepted change is still delayed, without requiring another confirmation or resubmitting', async () => { - renderEditor(); - await screen.findByLabelText('Label'); - editSkill(''); - save(); - await screen.findByText( - 'Changes submitted. They may take a moment to appear in channel details.', - ); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha', - ); - expect(screen.queryByText('Channel changes saved.')).not.toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'Check again' }), - ).not.toBeInTheDocument(); - fireEvent.submit(screen.getByRole('form', { name: 'Edit Telegram' })); - expect(posts()).toHaveLength(1); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 3, - ); - expect(postBody()).toEqual({ - runtime_config: { - ...fixture.runtime_config, - default_skill: { name: '', version: '' }, - }, - }); -}); - -it('does not fetch service choices and handles rejected skill fields without leaking diagnostics', async () => { - let rejected = false; - fetchMock.mockImplementation(async (input, init) => { - const identity = identityResponse(input); - if (identity) return identity; - if (init?.method === 'POST') { - rejected = true; - return response( - { - error: 'invalid_runtime_config', - field_errors: [ - { - field: 'runtime_config.default_skill.name', - code: 'too_long', - message: 'TEST_ONLY_SECRET', - }, - ], - }, - 400, - ); - } - return response(fixture); - }); - renderEditor(); - await screen.findByLabelText('Label'); - expect(fetchMock.mock.calls.some(([input]) => input === servicePath)).toBe( - false, - ); - editSkill('changed-helper'); - expect(screen.getByRole('button', { name: 'Save changes' })).toBeEnabled(); - save(); - await screen.findByText( - 'Check the skill name. Use no more than 128 characters.', - ); - expect(rejected).toBe(true); - expect(screen.getByLabelText(/^Skill name/)).toHaveAttribute( - 'aria-invalid', - 'true', - ); - expect(screen.getByLabelText(/^Skill name/)).toHaveValue('changed-helper'); - expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); - expect( - await screen.findByRole('button', { name: 'Save changes' }), - ).toBeEnabled(); - expect(history.replace).not.toHaveBeenCalled(); - expect( - await screen.findByText( - 'Could not save channel changes. Review your choices and try again.', - ), - ).toBeInTheDocument(); - editSkill('retry-helper'); - save(); - await waitFor(() => expect(posts()).toHaveLength(2)); - expect(postBody().runtime_config.default_skill.name).toBe('retry-helper'); -}); - -it('revalidates a cached detail and retries an unavailable response without exposing a stale editable form', async () => { - const queryClient = createTestQueryClient(); - queryClient.setQueryData( - channelKeys.config('scope-alpha', 'registration-alpha'), - await channelRuntimeConfigApi.get('registration-alpha', 'scope-alpha'), - ); - fetchMock.mockClear(); - fetchMock.mockResolvedValueOnce(response({ error: 'TEST_ONLY_SECRET' }, 404)); - renderWithQueryClient( - , - queryClient, - ); - expect(screen.queryByLabelText(/^Skill name/)).not.toBeInTheDocument(); - await screen.findByText( - 'This channel is unavailable or you do not have access.', - ); - expect( - screen.queryByRole('button', { name: 'Save changes' }), - ).not.toBeInTheDocument(); - expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 1, - ); - fireEvent.click(screen.getByRole('button', { name: 'Try again' })); - expect(await screen.findByLabelText(/^Skill name/)).toHaveValue( - 'team-helper', - ); - expect(posts()).toHaveLength(0); -}); - -it('preserves legacy default authorization and returns to details with accurate feedback when readback fails', async () => { - let submitted = false; - const legacy = { - ...fixture, - authorization_mode: 'nyxid_default', - service_ids: null, - runtime_config: { ...fixture.runtime_config, nyxid_service_selectors: [] }, - }; - fetchMock.mockImplementation(async (input, init) => { - const identity = identityResponse(input); - if (identity) return identity; - if (init?.method === 'POST') { - submitted = true; - return response(receipt, 202); - } - if (submitted) return response({}, 503); - return response(legacy); - }); - renderEditor(); - await screen.findByLabelText('Label'); - expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); - editSkill('updated'); - save(); - await screen.findByText( - 'Changes submitted, but the latest configuration could not be loaded. Refresh the channel details to view it.', - ); - expect(postBody()).not.toHaveProperty('authorization_mode'); - expect(postBody()).not.toHaveProperty('service_ids'); - expect(postBody().runtime_config.nyxid_service_selectors).toEqual([]); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha', - ); - expect(screen.queryByText('Channel changes saved.')).not.toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'Check again' }), - ).not.toBeInTheDocument(); - expect(posts()).toHaveLength(1); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 3, - ); -}); - -it('keeps Save pending through readback and ignores its result after leaving the editor', async () => { - let resolveRead!: (value: Response) => void; - const pendingRead = new Promise((resolve) => { - resolveRead = resolve; - }); - fetchMock.mockImplementation(async (input, init) => { - const identity = identityResponse(input); - if (identity) return identity; - if (init?.method === 'POST') return response(receipt, 202); - return posts().length ? pendingRead : response(fixture); - }); - const view = renderEditor(); - await screen.findByLabelText('Label'); - editSkill('updated'); - save(); - await waitFor(() => + renderWithQueryClient(); + await chooseSkill('support'); expect( - fetchMock.mock.calls.filter(([input]) => input === path), - ).toHaveLength(3), - ); - expect(screen.getByRole('button', { name: /Save changes/ })).toBeDisabled(); - expect(screen.getByRole('button', { name: 'Cancel' })).toBeDisabled(); - expect( - screen.queryByRole('button', { name: 'Check again' }), - ).not.toBeInTheDocument(); - fireEvent.submit(screen.getByRole('form', { name: 'Edit Telegram' })); - expect(posts()).toHaveLength(1); - view.unmount(); - await act(async () => - resolveRead( - response({ - ...fixture, - state_version: 13, - default_skill: { name: 'updated', version: '2.1' }, - }), - ), - ); - expect(history.replace).not.toHaveBeenCalled(); - expect(screen.queryByText('Channel changes saved.')).not.toBeInTheDocument(); + screen.queryByLabelText(/Bot token|Channel name|Label/), + ).not.toBeInTheDocument(); + fireEvent.click(await screen.findByRole('checkbox', { name: /GitHub/ })); + fireEvent.click(screen.getByRole('button', { name: 'Refresh skills' })); + await waitFor(() => + expect( + screen.getByRole('button', { name: 'Refresh skills' }), + ).toBeEnabled(), + ); + expect( + screen.getByText('support', { selector: '.ant-select-content' }), + ).toBeInTheDocument(); + fireEvent.mouseDown(screen.getByRole('combobox')); + const create = await screen.findByRole('link', { + name: /Create new skill/, + }); + expect(create).toHaveAttribute( + 'href', + 'https://ornn.chrono-ai.fun/skills/new/generate', + ); + expect(create).toHaveAttribute('target', '_blank'); + fireEvent.mouseEnter(create); + expect(await screen.findByRole('tooltip')).toHaveTextContent( + 'return here and refresh', + ); + fireEvent.keyDown(screen.getByRole('combobox'), { key: 'Escape' }); + fireEvent.click(screen.getByRole('button', { name: 'Bind bot' })); + await screen.findByText('Confirming your changes...'); + expect(JSON.parse(String(posts()[0][1]?.body))).toEqual({ + nyx_channel_bot_id: 'bot-alpha', + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], + }); + expect(mockToast.success).not.toHaveBeenCalled(); + expect(history.replace).not.toHaveBeenCalled(); + expect( + screen.queryByRole('button', { name: /Check again/ }), + ).not.toBeInTheDocument(); + committed = true; + await act(async () => { + await jest.advanceTimersByTimeAsync(1500); + }); + await waitFor(() => + expect(mockToast.success).toHaveBeenCalledWith('Bot bound successfully.'), + ); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels', + ); + expect(posts()).toHaveLength(1); + } finally { + cleanup(); + jest.useRealTimers(); + } }); -it('protects unsaved navigation and ignores a response after the editor unmounts', async () => { - let resolvePost!: (value: Response) => void; - const deferred = new Promise((resolve) => { - resolvePost = resolve; - }); - fetchMock.mockImplementation(async (input, init) => - init?.method === 'POST' - ? deferred - : (identityResponse(input) ?? +it('preserves Label editing and requires a newer matching configuration before reporting saved', async () => { + jest.useFakeTimers(); + try { + let committed = false; + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'PATCH') + return response({ + id: 'bot-alpha', + platform: 'discord', + label: 'Renamed bot', + }); + if (init?.method === 'POST') return response(receipt, 202); + if (input === '/api/channels/registrations') return response([row]); + return ( + catalogue(input) ?? response({ - ...fixture, - service_ids: [], - runtime_config: { - ...fixture.runtime_config, - }, - })), - ); - const view = renderEditor(); - await screen.findByLabelText('Label'); - editSkill('unsaved'); - const unload = new Event('beforeunload', { cancelable: true }); - window.dispatchEvent(unload); - expect(unload.defaultPrevented).toBe(true); - fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); - const dialog = await screen.findByRole('dialog', { - name: 'Discard your changes?', - }); - expect(history.push).not.toHaveBeenCalled(); - fireEvent.click(within(dialog).getByRole('button', { name: 'Stay' })); - expect(screen.getByLabelText(/^Skill name/)).toHaveValue('unsaved'); - save(); - await waitFor(() => expect(posts()).toHaveLength(1)); - view.unmount(); - await act(async () => resolvePost(response(receipt, 202))); - expect(history.replace).not.toHaveBeenCalled(); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 2, - ); -}); - -it('updates only the label using the exact NyxID bot ID and refreshes the safe identity cache', async () => { - fetchMock.mockImplementation(async (input, init) => { - if (init?.method === 'PATCH') - return response({ - ...bot, - label: 'New label', - access_token: 'TEST_ONLY_SECRET', - }); - return identityResponse(input) ?? response(fixture); - }); - const view = renderEditor(); - await screen.findByLabelText('Label'); - editLabel(' New label '); - save(); - await screen.findByText('Channel changes saved.'); - expect(patches()).toHaveLength(1); - expect(patches()[0][0]).toBe(`${botsPath}/bot-nyx-alpha`); - expect(JSON.parse(String(patches()[0][1]?.body))).toEqual({ - label: 'New label', - }); - expect(posts()).toHaveLength(0); - expect(fetchMock.mock.calls.filter(([input]) => input === path)).toHaveLength( - 1, - ); - expect( - view.queryClient.getQueryData(channelKeys.bots('scope-alpha')), - ).toEqual([{ ...bot, label: 'New label' }]); - expect( - JSON.stringify( - view.queryClient.getQueryData(channelKeys.bots('scope-alpha')), - ), - ).not.toContain('TEST_ONLY_SECRET'); - expect(history.replace).toHaveBeenCalledWith( - '/scopes/scope-alpha/workflow-activity-vnext/channels/registration-alpha', - ); -}); - -it('reports a partial save and retries only the failed skill update without changing authorization', async () => { - let attempts = 0; - fetchMock.mockImplementation(async (input, init) => { - if (init?.method === 'PATCH') - return response({ ...bot, label: 'New label' }); - if (init?.method === 'POST') - return ++attempts === 1 ? response({}, 503) : response(receipt, 202); - return identityResponse(input) ?? response(fixture); - }); - renderEditor(); - await screen.findByLabelText('Label'); - editLabel('New label'); - editSkill('new-skill'); - save(); - await screen.findByText( - 'Label saved, but the skill name could not be updated. Try saving again.', - ); - expect(history.replace).not.toHaveBeenCalled(); - expect(screen.getByLabelText('Label')).toHaveValue('New label'); - expect(screen.getByLabelText(/^Skill name/)).toHaveValue('new-skill'); - save(); - await screen.findByText( - 'Changes submitted. They may take a moment to appear in channel details.', - ); - expect(patches()).toHaveLength(1); - expect(posts()).toHaveLength(2); - expect(postBody()).toEqual({ - runtime_config: { - ...fixture.runtime_config, - default_skill: { name: 'new-skill', version: '2.1' }, - }, - }); -}); - -it('validates the label before either write and keeps both values when the label request fails', async () => { - fetchMock.mockImplementation(async (input, init) => { - if (init?.method === 'PATCH') - return response({ token: 'TEST_ONLY_SECRET' }, 503); - return identityResponse(input) ?? response(fixture); - }); - renderEditor(); - await screen.findByLabelText('Label'); - editSkill('new-skill'); - for (const value of [' ', '界'.repeat(43)]) { - editLabel(value); - save(); - expect(screen.getByLabelText('Label')).toHaveAttribute( - 'aria-invalid', - 'true', + ...row, + label: 'reg-alpha', + skill_name: committed ? 'new-support' : 'support', + state_version: committed ? 13 : 12, + }) + ); + }); + window.history.replaceState( + {}, + '', + '/scopes/scope-alpha/workflow-activity-vnext/channels/reg-alpha/edit', + ); + renderWithQueryClient(); + expect(await screen.findByLabelText('Label')).toHaveValue('Support bot'); + fireEvent.change(await screen.findByLabelText('Label'), { + target: { value: 'Renamed bot' }, + }); + await chooseSkill('new-support'); + fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); + await screen.findByText('Confirming your changes...'); + expect(posts()[0][0]).toBe('/api/channels/registrations/reg-alpha'); + expect(JSON.parse(String(posts()[0][1]?.body))).toEqual({ + skill_name: 'new-support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-github'], + }); + expect(mockToast.success).not.toHaveBeenCalled(); + committed = true; + await act(async () => { + await jest.advanceTimersByTimeAsync(1500); + }); + await waitFor(() => + expect(mockToast.success).toHaveBeenCalledWith('Channel changes saved.'), ); - expect(patches()).toHaveLength(0); - expect(posts()).toHaveLength(0); + expect(history.replace).toHaveBeenCalledWith( + '/scopes/scope-alpha/workflow-activity-vnext/channels/reg-alpha', + ); + expect(posts()).toHaveLength(1); + } finally { + cleanup(); + jest.useRealTimers(); } - editLabel('Valid label'); - save(); - await screen.findByText('Could not save the label. Check it and try again.'); - expect(screen.getByLabelText('Label')).toHaveValue('Valid label'); - expect(screen.getByLabelText(/^Skill name/)).toHaveValue('new-skill'); - expect(posts()).toHaveLength(0); - expect(history.replace).not.toHaveBeenCalled(); - expect(document.body).not.toHaveTextContent('TEST_ONLY_SECRET'); }); -it.each([ - { ...registration, owned: false }, - { ...registration, scope_id: 'other-scope' }, - { ...registration, nyx_channel_bot_id: 'unrelated-bot' }, - { ...registration, platform: 'lark' }, -])('does not expose an editor without an exact owned bot mapping: %j', async (row) => { - fetchMock.mockImplementation(async (input) => - input === '/api/channels/registrations' - ? response([row]) - : (identityResponse(input) ?? response(fixture)), - ); - renderEditor(); - await screen.findByText( - 'This channel is unavailable or you do not have access.', - ); - expect(screen.queryByRole('form')).not.toBeInTheDocument(); - expect(patches()).toHaveLength(0); - expect(posts()).toHaveLength(0); +it('does not repeat an adoption after an uncertain transport result', async () => { + jest.useFakeTimers(); + try { + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') throw new Error('TEST_SECRET'); + return catalogue(input) ?? response([unbound]); + }); + renderWithQueryClient( + , + ); + const bind = await screen.findByRole('button', { name: 'Bind bot' }); + await waitFor(() => expect(bind).toBeEnabled()); + fireEvent.click(bind); + expect(await screen.findByRole('alert')).toHaveTextContent( + 'Return to Channels', + ); + expect(bind).toBeDisabled(); + fireEvent.click(bind); + expect(posts()).toHaveLength(1); + expect(document.body).not.toHaveTextContent('TEST_SECRET'); + } finally { + cleanup(); + jest.useRealTimers(); + } }); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx deleted file mode 100644 index 89a2cbe1d9..0000000000 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.tsx +++ /dev/null @@ -1,458 +0,0 @@ -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { Button, Input, Modal } from 'antd'; -import * as React from 'react'; -import { - type ChannelBotIdentity, - isValidChannelBotLabel, - updateChannelBotLabel, -} from '@/shared/api/channelBotsApi'; -import { - type ChannelConfigDetail, - ChannelConfigError, - type ChannelConfigField, - type ChannelConfigUpdate, - channelConfigMatches, - channelRuntimeConfigApi, -} from '@/shared/api/channelRuntimeConfigApi'; -import { ChannelApiError } from '@/shared/api/channelsApi'; -import { t } from '@/shared/i18n/messages'; -import { history } from '@/shared/navigation/history'; -import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; -import { useConsoleToast } from '@/shared/ui/ConsoleToast'; -import { - buildChannelDetailsHref, - buildWorkflowActivitySectionHref, -} from '../navigation'; -import WorkflowActivityVNextShell from '../WorkflowActivityVNextShell'; -import ChannelSkillField from './ChannelSkillField'; -import { channelConnectionCss } from './connectionStyles'; -import { ChannelLoadError, platformName } from './presentation'; -import { - channelKeys, - useChannelBotIdentities, - useChannelRegistrations, -} from './queries'; -import { channelsCss } from './styles'; - -export default function ChannelEditPage({ - scopeId, - registrationId, -}: { - readonly scopeId: string; - readonly registrationId: string; -}) { - const [initial, setInitial] = React.useState<{ - config: ChannelConfigDetail; - bot: ChannelBotIdentity; - } | null>(null); - const config = useQuery({ - queryKey: channelKeys.config(scopeId, registrationId), - queryFn: ({ signal }) => - channelRuntimeConfigApi.get(registrationId, scopeId, signal), - enabled: Boolean(scopeId && registrationId), - retry: false, - staleTime: 0, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - }); - const [navigate, setNavigate] = React.useState<(target: string) => void>( - () => history.push, - ); - const registrations = useChannelRegistrations(scopeId); - const bots = useChannelBotIdentities(scopeId, Boolean(registrationId)); - const queries = [config, registrations, bots]; - const pending = queries.some((query) => query.isFetching); - const fresh = queries.every( - (query) => - query.isSuccess && query.isFetchedAfterMount && !query.isFetching, - ); - const registration = registrations.data?.find( - (row) => row.id === registrationId && row.scopeId === scopeId && row.owned, - ); - const bot = bots.data?.find( - (row) => - row.id === registration?.botId && row.platform === config.data?.platform, - ); - const loadError = - queries.find((query) => query.isError)?.error ?? - (fresh && (!bot || registration?.platform !== config.data?.platform) - ? new ChannelApiError(404) - : null); - const error = pending ? null : loadError; - React.useEffect(() => { - if (!initial && fresh && !error && config.data && bot) - setInitial({ config: config.data, bot }); - }, [initial, fresh, error, config.data, bot]); - const title = initial - ? t('channels.edit.title', 'Edit {platform}', { - platform: platformName(initial.config.platform), - }) - : t('channels.edit.loadingTitle', 'Edit channel'); - const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); - return ( - - - - {!initial && !error ? ( - - ) : !initial && error ? ( - { - void config.refetch(); - void registrations.refetch(); - void bots.refetch(); - }} - /> - ) : initial ? ( - { - const result = await config.refetch(); - if (result.isError || !result.data) throw result.error; - return result.data; - }} - /> - ) : null} - - ); -} - -function ChannelEditForm({ - initial, - initialBot, - title, - setNavigate, - readBack, -}: { - readonly initial: ChannelConfigDetail; - readonly initialBot: ChannelBotIdentity; - readonly title: string; - readonly setNavigate: React.Dispatch< - React.SetStateAction<(target: string) => void> - >; - readonly readBack: () => Promise; -}) { - const baseline = initial; - const [skillName, setSkillName] = React.useState( - baseline.runtimeConfig.defaultSkill.name, - ); - const [label, setLabel] = React.useState(initialBot.label ?? ''); - const [savedLabel, setSavedLabel] = React.useState(initialBot.label ?? ''); - const labelId = React.useId(); - const [errors, setErrors] = React.useState< - readonly (ChannelConfigField | 'label')[] - >([]); - const [submitting, setSubmitting] = React.useState(false); - const [leaveTarget, setLeaveTarget] = React.useState(null); - const inFlight = React.useRef(false); - const mounted = React.useRef(true); - const completed = React.useRef(false); - const toast = useConsoleToast(); - const queryClient = useQueryClient(); - const detailsHref = buildChannelDetailsHref( - baseline.scopeId, - baseline.registrationId, - ); - const update: ChannelConfigUpdate = { - ...baseline, - runtimeConfig: { - ...baseline.runtimeConfig, - defaultSkill: { - name: skillName, - version: skillName.trim() - ? baseline.runtimeConfig.defaultSkill.version - : '', - }, - }, - }; - const labelChanged = label.trim() !== savedLabel; - const skillChanged = !channelConfigMatches(update, baseline); - const dirty = labelChanged || skillChanged; - - React.useEffect(() => { - mounted.current = true; - return () => { - mounted.current = false; - }; - }, []); - React.useEffect(() => { - if ((!dirty && !submitting) || completed.current) return; - const warn = (event: BeforeUnloadEvent) => { - event.preventDefault(); - event.returnValue = ''; - }; - window.addEventListener('beforeunload', warn); - return () => window.removeEventListener('beforeunload', warn); - }, [dirty, submitting]); - React.useEffect(() => { - setNavigate(() => (target: string) => { - if (inFlight.current) return; - if (dirty && !completed.current) setLeaveTarget(target); - else history.push(target); - }); - }, [dirty, setNavigate]); - - async function finishSave(expected?: ChannelConfigUpdate) { - let confirmed = !expected; - let readFailed = false; - try { - if (expected) { - const actual = await readBack(); - confirmed = - actual.stateVersion > baseline.stateVersion && - channelConfigMatches(actual, expected); - } - } catch { - readFailed = true; - } - if (!mounted.current) return; - completed.current = true; - void queryClient.invalidateQueries({ - queryKey: channelKeys.list(baseline.scopeId), - }); - void queryClient.invalidateQueries({ - queryKey: channelKeys.status(baseline.scopeId, baseline.registrationId), - }); - if (confirmed) { - toast.success(t('channels.edit.saved', 'Channel changes saved.')); - } else if (readFailed) { - toast.warning( - t( - 'channels.edit.readbackUnavailable', - 'Changes submitted, but the latest configuration could not be loaded. Refresh the channel details to view it.', - ), - ); - } else { - toast.info( - t( - 'channels.edit.submitted', - 'Changes submitted. They may take a moment to appear in channel details.', - ), - ); - } - history.replace(detailsHref); - } - - async function save(event: React.FormEvent) { - event.preventDefault(); - if (inFlight.current || submitting || !dirty || completed.current) return; - const invalid: (ChannelConfigField | 'label')[] = []; - if (labelChanged && !isValidChannelBotLabel(label)) invalid.push('label'); - if (skillName.trim().length > 128) invalid.push('skill'); - setErrors(invalid); - if (invalid.length) return; - inFlight.current = true; - setSubmitting(true); - let labelSaved = false; - let updatingLabel = labelChanged; - try { - if (labelChanged) { - const updated = await updateChannelBotLabel(initialBot, label); - queryClient.setQueryData( - channelKeys.bots(baseline.scopeId), - (current) => - current?.map((bot) => (bot.id === updated.id ? updated : bot)), - ); - void queryClient.invalidateQueries({ - queryKey: channelKeys.bots(baseline.scopeId), - refetchType: 'none', - }); - if (!mounted.current) return; - setSavedLabel(updated.label ?? ''); - labelSaved = true; - updatingLabel = false; - } - if (skillChanged) - await channelRuntimeConfigApi.update( - baseline.registrationId, - update.runtimeConfig, - ); - if (!mounted.current) return; - // Read once for truthful feedback, then leave the editor even if the - // accepted change is not visible yet. Never poll or resubmit to confirm. - await finishSave(skillChanged ? update : undefined); - } catch (error) { - if (!mounted.current) return; - if (error instanceof ChannelConfigError) { - setErrors(error.fields); - } - if ( - updatingLabel && - error instanceof ChannelApiError && - error.status === 400 - ) - setErrors(['label']); - toast.error( - !updatingLabel && - (labelSaved || savedLabel !== (initialBot.label ?? '')) - ? t( - 'channels.edit.partialSave', - 'Label saved, but the skill name could not be updated. Try saving again.', - ) - : (error instanceof ChannelApiError && - [401, 403].includes(error.status)) || - (error instanceof ChannelConfigError && - error.fields.includes('services')) - ? t( - 'channels.connect.error.authorization', - 'Your session or service access needs attention. Sign in again and review your NyxID access.', - ) - : error instanceof ChannelApiError && - error.status === 404 && - !( - error instanceof ChannelConfigError && - error.fields.includes('services') - ) - ? t( - 'channels.error.unavailable', - 'This channel is unavailable or you do not have access.', - ) - : updatingLabel - ? t( - 'channels.edit.labelFailed', - 'Could not save the label. Check it and try again.', - ) - : t( - 'channels.edit.failed', - 'Could not save channel changes. Review your choices and try again.', - ), - ); - return; - } finally { - inFlight.current = false; - if (mounted.current) setSubmitting(false); - } - } - - const labelError = errors.includes('label') - ? t( - 'channels.edit.labelError', - 'Enter a non-empty label. If it is too long, shorten it and try again.', - ) - : undefined; - const skillError = errors.includes('skill') - ? t( - 'channels.edit.skillError', - 'Check the skill name. Use no more than 128 characters.', - ) - : undefined; - - return ( - <> -
    void save(event)} - noValidate - aria-label={title} - > -

    - {t('channels.edit.configuration', 'Configuration')} -

    -
    -
    - -
    - setLabel(event.target.value)} - /> - {labelError ? ( - - ) : null} -
    - -
    - - -
    - - setLeaveTarget(null)} - onOk={() => { - if (leaveTarget) history.push(leaveTarget); - setLeaveTarget(null); - }} - okText={t('channels.connect.discard', 'Discard')} - cancelText={t('channels.connect.stay', 'Stay')} - > - {t( - 'channels.edit.discardHelp', - 'Your unsaved channel changes will be lost.', - )} - - - ); -} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx index a57df01f6b..198edb7bc2 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelIdentity.test.tsx @@ -3,40 +3,33 @@ import * as React from 'react'; import type { ChannelRegistration } from '@/shared/api/channelsApi'; import { ChannelIdentity } from './presentation'; -describe('Channel identifier disclosure', () => { - it.each([ - 'bot', - 'registration', - ] as const)('reveals the complete %s ID from the compact identifier', async (identityKind) => { - const fullId = - identityKind === 'bot' - ? 'bot-alpha-1234567890-1234567890-complete' - : 'registration-alpha-1234567890-1234567890-complete'; - const registration: ChannelRegistration = { - id: identityKind === 'registration' ? fullId : 'registration-alpha', - botId: identityKind === 'bot' ? fullId : null, - platform: 'telegram', - scopeId: 'scope-alpha', - providerSlug: null, - agentKeyId: null, - skill: null, - workflowDeliveryStatus: null, - owned: true, - serviceAuthorization: { kind: 'unavailable' }, - }; - const identityProps = { - registration, - label: 'Channel for ID test', - pending: false, - }; - render(); - const identifier = screen.getByRole('button', { name: 'Show full ID' }); - expect(identifier).not.toHaveTextContent(fullId); - expect(screen.queryByRole('tooltip')).not.toBeInTheDocument(); - - if (identityKind === 'bot') fireEvent.mouseEnter(identifier); - else fireEvent.focus(identifier); - - expect(await screen.findByRole('tooltip')).toHaveTextContent(fullId); - }); +it('reveals the complete NyxID bot identity even before a local registration exists', async () => { + const fullId = 'bot-alpha-1234567890-1234567890-complete'; + const registration: ChannelRegistration = { + id: null, + botId: fullId, + label: 'Support', + platform: 'discord', + bindingStatus: 'unbound', + availabilityStatus: 'available', + nyxStatus: 'active', + providerSlug: null, + agentKeyId: null, + skill: null, + workflowDeliveryStatus: 'unbound', + stateVersion: null, + owned: true, + serviceAuthorization: { kind: 'unavailable' }, + }; + render( + , + ); + const identifier = screen.getByRole('button', { name: 'Show full ID' }); + expect(identifier).not.toHaveTextContent(fullId); + fireEvent.focus(identifier); + expect(await screen.findByRole('tooltip')).toHaveTextContent(fullId); }); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx index e4aa77c76f..db888b707f 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx @@ -1,41 +1,153 @@ -import { Input } from 'antd'; +import { + ExportOutlined, + PlusOutlined, + ReloadOutlined, +} from '@ant-design/icons'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { Button, Select } from 'antd'; import * as React from 'react'; +import { searchChannelSkills } from '@/shared/api/channelSkillsApi'; import { t } from '@/shared/i18n/messages'; +import { getOrnnRuntimeConfig } from '@/shared/studio/ornnConfig'; +import { AevatarLoadingDots } from '@/shared/ui/AevatarLoading'; +import AevatarTooltip from '@/shared/ui/AevatarTooltip'; +import { channelKeys } from './queries'; export default function ChannelSkillField({ + scopeId, value, onChange, disabled, - placeholder, error, }: { + readonly scopeId: string; readonly value: string; readonly onChange: (value: string) => void; readonly disabled: boolean; - readonly placeholder?: string; readonly error?: string; }) { const id = React.useId(); + const [search, setSearch] = React.useState(''); + const [term, setTerm] = React.useState(''); + React.useEffect(() => { + const timer = window.setTimeout(() => setTerm(search.trim()), 250); + return () => window.clearTimeout(timer); + }, [search]); + const skills = useInfiniteQuery({ + queryKey: channelKeys.skills(scopeId, term), + initialPageParam: undefined as string | undefined, + queryFn: ({ pageParam, signal }) => + searchChannelSkills(term, pageParam, signal), + getNextPageParam: (page) => page.nextCursor, + retry: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + enabled: Boolean(scopeId), + }); + // The registration contract accepts a name, not an Ornn GUID. + const names = [ + ...new Set( + skills.data?.pages.flatMap((page) => + page.items.map((skill) => skill.name), + ) ?? [], + ), + ]; + const config = getOrnnRuntimeConfig(); + const createHref = config.configurationError + ? null + : `${config.baseUrl}/skills/new/generate`; return (
    - onChange(event.target.value)} - /> - {error ? ( +
    + setChannelName(event.target.value)} - /> -
    - -
    - void services.refetch()} - /> - {invalidSelection && !locked ? ( -

    - {t( - 'channels.connect.selectionChanged', - 'Some selected services are no longer available. Deselect them before connecting.', - )} -

    - ) : null} - {!webhookBaseUrl ? ( -

    - {registrationErrorMessage('configuration')} -

    - ) : null} - {submittedId ? ( -
    -

    - {t( - 'channels.connect.pending', - 'Your request was submitted. Check again to open your channel when it is ready.', - )} -

    - -
    - ) : null} -
    - - -
    - - setLeaveTarget(null)} - onOk={() => { - if (leaveTarget) history.push(leaveTarget); - setLeaveTarget(null); - }} - okText={t('channels.connect.discard', 'Discard')} - cancelText={t('channels.connect.stay', 'Stay')} - > - {t( - 'channels.connect.discardHelp', - 'Your bot token and unsaved choices will be cleared.', - )} - - - ); -} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts deleted file mode 100644 index 26be2b7cce..0000000000 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/config.ts +++ /dev/null @@ -1,20 +0,0 @@ -const DEFAULT_WEBHOOK_BASE_URL = - 'https://aevatar-console-backend-api.aevatar.ai'; - -export function getChannelWebhookBaseUrl(): string | null { - const configured = process.env.AEVATAR_CHANNEL_WEBHOOK_BASE_URL; - try { - const url = new URL( - configured === undefined ? DEFAULT_WEBHOOK_BASE_URL : configured, - ); - return url.protocol === 'https:' && - !url.username && - !url.password && - !url.search && - !url.hash - ? url.href.replace(/\/+$/, '') - : null; - } catch { - return null; - } -} diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx index a5c7dc3fc4..86ad746914 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/presentation.tsx @@ -193,6 +193,10 @@ export function InboundStatus({ {t('channels.status.checking', 'Checking…')} ); switch (value?.toLowerCase()) { + case 'disabled': + return ( + {t('channels.status.disabled', 'Disabled')} + ); case 'active': return ( @@ -228,6 +232,12 @@ export function DeliveryStatus({ readonly platform: string; }) { switch (value) { + case 'unbound': + return ( + + {t('channels.binding.unbound', 'Not bound')} + + ); case 'enabled': return ( diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts index afb950e13d..dd3f1a7d26 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/queries.ts @@ -1,66 +1,66 @@ import { useQuery } from '@tanstack/react-query'; -import { listChannelBotIdentities } from '@/shared/api/channelBotsApi'; import { listChannelServices } from '@/shared/api/channelServicesApi'; -import { channelsApi } from '@/shared/api/channelsApi'; +import { ChannelApiError, channelsApi } from '@/shared/api/channelsApi'; export const channelKeys = { - config: (scopeId: string, registrationId: string) => - ['channels', scopeId, 'runtime-config', registrationId] as const, + detail: (scopeId: string, id: string) => + ['channels', scopeId, 'detail', id] as const, services: (scopeId: string) => ['channels', scopeId, 'service-choices'] as const, + skills: (scopeId: string, search: string) => + ['channels', scopeId, 'skills', search] as const, list: (scopeId: string) => ['channels', scopeId, 'registrations'] as const, - bots: (scopeId: string) => ['channels', scopeId, 'bot-identities'] as const, - status: (scopeId: string, registrationId: string) => - ['channels', scopeId, 'status', registrationId] as const, }; +const queryOptions = { + retry: false, + staleTime: 0, + refetchOnWindowFocus: false, + refetchOnReconnect: false, +} as const; + export function useChannelServiceChoices(scopeId: string) { return useQuery({ + ...queryOptions, queryKey: channelKeys.services(scopeId), queryFn: ({ signal }) => listChannelServices(signal), enabled: Boolean(scopeId), - retry: false, - staleTime: 0, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - }); -} - -export function useChannelBotIdentities(scopeId: string, enabled: boolean) { - return useQuery({ - queryKey: channelKeys.bots(scopeId), - queryFn: ({ signal }) => listChannelBotIdentities(signal), - enabled: Boolean(scopeId) && enabled, - retry: false, - staleTime: 0, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchInterval: false, }); } export function useChannelRegistrations(scopeId: string, enabled = true) { return useQuery({ + ...queryOptions, queryKey: channelKeys.list(scopeId), queryFn: ({ signal }) => channelsApi.list(signal), enabled: Boolean(scopeId) && enabled, - retry: false, - staleTime: 0, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchInterval: false, }); } -export function useChannelStatus(scopeId: string, registrationId: string) { +export function useChannelDetail(scopeId: string, id: string) { return useQuery({ - queryKey: channelKeys.status(scopeId, registrationId), - queryFn: ({ signal }) => channelsApi.status(registrationId, signal), - enabled: Boolean(scopeId && registrationId), - retry: false, - staleTime: 15_000, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchInterval: false, + ...queryOptions, + queryKey: channelKeys.detail(scopeId, id), + queryFn: async ({ signal }) => { + const detail = await channelsApi.get(id, signal); + const inventory = await channelsApi.list(signal); + const bot = inventory.find( + (row) => + row.id === id && + row.botId === detail.botId && + row.platform === detail.platform && + row.bindingStatus === 'bound', + ); + if (!bot) throw new ChannelApiError(404); + // Detail may use the registration ID as its label; inventory owns the + // current NyxID display name and inbound availability. + return { + ...detail, + label: bot.label, + nyxStatus: bot.nyxStatus, + availabilityStatus: bot.availabilityStatus, + }; + }, + enabled: Boolean(scopeId && id), }); } diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts index bcf1c73c02..665845867b 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/styles.ts @@ -10,17 +10,7 @@ export const channelsCss = ` .channels__main .wa-vnext__header { padding-top: 40px; } .channels__main .wa-vnext__header h1 { font-size: 24px; line-height: 32px; } .channels__content { padding-top: 0; } -.channels__intro { color: var(--wa-muted); line-height: 20px; margin: 4px 0 28px; } -.channels__section-label { color: var(--wa-muted); font-size: 12px; font-weight: 500; margin: 0 0 12px; } -.channels__platforms { display: grid; gap: 16px; grid-template-columns: repeat(4, minmax(0, 1fr)); } -.channels__platform { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); display: flex; flex-direction: column; min-height: 224px; min-width: 0; padding: 20px; } -.channels__platform-top { align-items: flex-start; display: flex; gap: 8px; justify-content: space-between; } .channels__icon { align-items: center; background: var(--wa-blue); border-radius: var(--wa-radius); color: white; display: inline-flex; flex: 0 0 auto; font-size: 22px; height: 42px; justify-content: center; width: 42px; } -.channels__platform h3 { font-size: 15px; font-weight: 600; line-height: 22px; margin: 16px 0 8px; } -.channels__platform p { color: var(--wa-muted); flex: 1; font-size: 12px; line-height: 19px; margin: 0 0 24px; } -.channels__platform--soon { background: transparent; } -.channels__platform--soon .channels__icon { background: var(--wa-faint); } -.channels__platform--soon h3 { color: var(--wa-muted); } .channels__connect { align-items: center; color: var(--wa-blue); display: inline-flex; font-weight: 500; gap: 8px; min-height: 28px; width: fit-content; } .channels__connections { margin-top: 32px; } .channels__section-heading { align-items: center; display: flex; gap: 16px; justify-content: space-between; margin-bottom: 16px; } @@ -28,15 +18,34 @@ export const channelsCss = ` .channels__section-heading p { color: var(--wa-muted); line-height: 20px; margin: 4px 0 0; } .channels__count { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: 5px; color: var(--wa-muted); font-size: 11px; font-weight: 500; line-height: 20px; min-width: 24px; padding: 0 6px; text-align: center; } .channels__table-wrap { background: var(--wa-surface); border: 1px solid var(--channels-border); border-radius: var(--wa-radius); overflow: hidden; position: relative; } -.channels__table { border-collapse: collapse; table-layout: fixed; width: 100%; } +.channels__table-scroll { overflow-x: auto; } +.channels__table { border-collapse: collapse; table-layout: fixed; min-width: 960px; width: 100%; } +.channels__table th:nth-child(1) { width: 23%; } +.channels__table th:nth-child(2) { width: 10%; } +.channels__table th:nth-child(3) { width: 18%; } +.channels__table th:nth-child(4) { width: 12%; } +.channels__table th:nth-child(5) { width: 14%; } +.channels__table th:nth-child(6) { width: 10%; } +.channels__table th:last-child { text-align: right; width: 13%; } +.channels__guide { background: var(--wa-surface); border-bottom: 1px solid var(--channels-border); padding: 24px 0; } +.channels__guide-steps { display: grid; gap: 24px; grid-template-columns: repeat(3, minmax(0, 1fr)); list-style: none; margin: 24px 0 0; padding: 0; } +.channels__guide-steps li { align-items: center; display: flex; gap: 14px; min-width: 0; padding-right: 22px; position: relative; } +.channels__guide-icon { align-items: center; background: var(--wa-blue-bg); border-radius: 8px; color: var(--wa-blue); display: flex; flex: 0 0 60px; font-size: 24px; height: 60px; justify-content: center; } +.channels__guide-number { color: var(--wa-muted); font-size: 10px; font-weight: 600; } +.channels__guide-steps h3 { font-size: 13px; line-height: 20px; margin: 3px 0; } +.channels__guide-steps p { color: var(--wa-muted); font-size: 11px; line-height: 18px; margin: 0; } +.channels__guide-arrow { color: var(--wa-muted); position: absolute; right: 0; } +.channels__bind { align-items: center; background: var(--wa-blue); border: 1px solid var(--wa-blue); border-radius: var(--wa-radius); color: white; display: inline-flex; justify-content: center; min-height: 32px; padding: 0 12px; } +.channels__bind:hover { color: white; filter: brightness(.92); } +.channels__bot-summary { align-items: center; border-bottom: 1px solid var(--channels-border); display: flex; gap: 16px; justify-content: space-between; margin-bottom: 24px; padding-bottom: 24px; } +.channels__bot-summary .channels__identity { min-width: 0; } +.channels__skill-controls { align-items: center; display: flex; gap: 8px; } +.channels__skill-controls .ant-select { flex: 1; min-width: 0; height: 40px; } +.channels__skill-controls > .ant-btn { flex: 0 0 36px; height: 36px; } +.channels__skill-create { align-items: center; border-top: 1px solid #e4e7ec; display: flex; gap: 8px; padding: 12px 8px; } +.channels__skill-create:focus-visible { outline: 2px solid #2563eb; outline-offset: -2px; } .channels__table th { background: var(--wa-subtle); border-bottom: 1px solid var(--channels-border); color: var(--wa-muted); font-size: 10px; font-weight: 600; line-height: 16px; padding: 12px 16px; text-align: left; text-transform: uppercase; } -.channels__table th:nth-child(1) { width: auto; } -.channels__table th:nth-child(2) { width: 12%; } -.channels__table th:nth-child(3) { width: 22%; } -.channels__table th:nth-child(4) { width: 13%; } -.channels__table th:nth-child(5) { width: 15%; } -.channels__table th:last-child { text-align: right; width: 128px; } -.channels__table td { border-bottom: 1px solid var(--channels-border); height: 92px; overflow-wrap: anywhere; padding: 16px; vertical-align: middle; } +.channels__table td { border-bottom: 1px solid var(--channels-border); height: 64px; overflow-wrap: anywhere; padding: 12px; vertical-align: middle; } .channels__table tr:last-child td { border-bottom: 0; } .channels__table tbody tr:hover { background: var(--wa-subtle); } .channels__identity { align-items: center; display: flex; gap: 12px; min-width: 0; } @@ -95,37 +104,22 @@ export const channelsCss = ` .channels__removal p { color: var(--wa-muted); line-height: 20px; margin: 0; } .channels__content a:focus-visible { border-radius: 3px; outline: 2px solid var(--wa-blue); outline-offset: 4px; } @media (max-width: 1199px) { - .channels__platforms { grid-template-columns: repeat(2, minmax(0, 1fr)); } .channels__table th, .channels__table td { padding-left: 12px; padding-right: 12px; } - .channels__table th:nth-child(1) { width: auto; } - .channels__table th:nth-child(2) { width: 12%; } - .channels__table th:nth-child(3) { width: 20%; } - .channels__table th:nth-child(4) { width: 14%; } - .channels__table th:nth-child(5) { width: 16%; } - .channels__table th:last-child { width: 120px; } } @media (max-width: 767px) { + .channels__guide .channels__section-heading { align-items: flex-start; flex-wrap: wrap; } + .channels__guide-steps { gap: 32px; grid-template-columns: minmax(0, 1fr); } + .channels__guide-steps li { padding: 0; } + .channels__guide-arrow { bottom: -24px; left: 23px; right: auto; transform: rotate(90deg); } + .channels__bot-summary { flex-wrap: wrap; } .channels__main { --channels-space: 20px; --channels-action-height: 44px; } .channels__main .wa-vnext__header { padding-top: 24px; } .channels__main .wa-vnext__header h1 { font-size: 22px; line-height: 30px; } - .channels__platforms { gap: 12px; } - .channels__platform { min-height: 210px; padding: 16px; } .channels__platform-top { flex-wrap: wrap; } - .channels__table, .channels__table tbody, .channels__table tr, .channels__table td { display: block; width: 100%; } - .channels__table thead { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; width: 1px; } - .channels__table tr { border-bottom: 1px solid var(--channels-border); padding: 16px; } - .channels__table tr:last-child { border-bottom: none; } - .channels__table td { border: 0; height: auto; padding: 6px 0; } - .channels__table td[data-label]:not(:first-child) { align-items: baseline; display: grid; gap: 12px; grid-template-columns: 110px minmax(0, 1fr); } - .channels__table td[data-label]:not(:first-child)::before { color: var(--wa-muted); content: attr(data-label); font-size: 11px; } - .channels__table .channels__row-action { padding-top: 8px; } .channels__main--detail .channels__content { padding-top: 20px; } .channels__detail-heading { align-items: flex-start; flex-wrap: wrap; } .channels__detail-heading .channels__badge { margin-left: 56px; } .channels__facts > div { grid-template-columns: 108px minmax(0, 1fr); } .channels__detail-heading h1 { font-size: 14px; } } -@media (max-width: 359px) { - .channels__platforms { grid-template-columns: minmax(0, 1fr); } -} `; diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx index 77a6d38274..f765048484 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/index.tsx @@ -2,10 +2,9 @@ import React from 'react'; import { t } from '@/shared/i18n/messages'; import ActivityPage from './activity/ActivityPage'; import RunDetailPage from './activity/RunDetailPage'; +import ChannelConfigurationPage from './channels/ChannelConfigurationPage'; import ChannelDetailsPage from './channels/ChannelDetailsPage'; -import ChannelEditPage from './channels/ChannelEditPage'; import ChannelsPage from './channels/ChannelsPage'; -import TelegramConnectionPage from './channels/TelegramConnectionPage'; import { useConsoleLocation } from './hooks/useConsoleLocation'; import SettingsPage from './settings/SettingsPage'; import WorkflowActivityVNextShell from './WorkflowActivityVNextShell'; @@ -58,13 +57,20 @@ const WorkflowActivityVNextPage: React.FC = () => { return ; } - if (pathname.endsWith('/channels/connect/telegram')) { - return ; + const channelBindMatch = /\/channels\/bind\/([^/]+)$/.exec(pathname); + if (channelBindMatch) { + return ( + + ); } const channelEditMatch = /\/channels\/([^/]+)\/edit$/.exec(pathname); if (channelEditMatch) { return ( - ({ authFetch: jest.fn() })); -jest.mock('@/shared/auth/config', () => ({ - getNyxIDRuntimeConfig: () => ({ baseUrl: 'https://nyx.example.test' }), -})); -const fetchMock = jest.mocked(authFetch); -const originalFetch = global.fetch; -const telegramFetch = jest.fn(); -const response = (value: unknown, status = 200) => - ({ - ok: status >= 200 && status < 300, - status, - json: async () => value, - }) as Response; - -beforeEach(() => { - telegramFetch.mockReset(); - global.fetch = telegramFetch; -}); -afterEach(() => { - fetchMock.mockReset(); - global.fetch = originalFetch; - jest.restoreAllMocks(); -}); - -it('preserves an explicit empty allowlist and retains only the accepted registration identity', async () => { - const random = jest.spyOn(Math, 'random'); - fetchMock.mockResolvedValue( - response( - { - status: 'accepted', - registration_id: 'registration-new', - webhook_url: 'TEST_ONLY_SECRET', - }, - 202, - ), - ); - expect( - await channelsApi.registerTelegram({ - botToken: ' TEST_ONLY_TOKEN ', - label: ' Label ', - skillName: ' my-bot-skill ', - serviceIds: [], - webhookBaseUrl: 'https://api.example.test', - }), - ).toEqual({ registrationId: 'registration-new' }); - expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); - expect(telegramFetch).not.toHaveBeenCalled(); - expect(random).not.toHaveBeenCalled(); - expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toEqual({ - platform: 'telegram', - bot_token: 'TEST_ONLY_TOKEN', - label: 'Label', - default_skill_name: 'my-bot-skill', - authorization_mode: 'explicit_service_allowlist', - service_ids: [], - webhook_base_url: 'https://api.example.test', - }); - fetchMock.mockResolvedValue( - response( - { status: 'accepted', registration_id: '', note: 'TEST_ONLY_SECRET' }, - 202, - ), - ); - await expect( - channelsApi.registerTelegram({ - botToken: 'TEST_ONLY_TOKEN', - label: 'Label', - skillName: 'my-bot-skill', - serviceIds: [], - webhookBaseUrl: 'https://api.example.test', - }), - ).rejects.toEqual(new ChannelRegistrationError('uncertain')); -}); - -it.each([ - { - label: undefined, - skillName: undefined, - randomValue: 0, - expectedName: 'My Bot-100000', - expectedSkillName: 'My Bot', - }, - { - label: ' ', - skillName: ' ', - randomValue: 0.99999999, - expectedName: 'My Bot-999999', - expectedSkillName: 'My Bot', - }, - { - label: ' Custom channel ', - skillName: '', - randomValue: 0, - expectedName: 'Custom channel', - expectedSkillName: 'My Bot', - }, - { - label: '', - skillName: ' custom-skill ', - randomValue: 0, - expectedName: 'My Bot-100000', - expectedSkillName: 'custom-skill', - }, -])('resolves label $label and skill $skillName independently with one bot lookup', async ({ - label, - skillName, - randomValue, - expectedName, - expectedSkillName, -}) => { - const random = jest.spyOn(Math, 'random').mockReturnValue(randomValue); - const token = '123456:TEST_ONLY_TOKEN'; - telegramFetch.mockResolvedValue( - response({ - ok: true, - result: { - is_bot: true, - first_name: ' My Bot ', - username: 'different_username', - }, - }), - ); - fetchMock.mockResolvedValue( - response( - { status: 'accepted', registration_id: 'registration-defaults' }, - 202, - ), - ); - await channelsApi.registerTelegram({ - botToken: ` ${token} `, - label, - skillName, - serviceIds: ['user-service-a'], - webhookBaseUrl: 'https://api.example.test', - }); - expect(telegramFetch).toHaveBeenCalledTimes(1); - expect(telegramFetch).toHaveBeenCalledWith( - `https://api.telegram.org/bot${token}/getMe`, - { - method: 'POST', - credentials: 'omit', - cache: 'no-store', - referrerPolicy: 'no-referrer', - redirect: 'error', - signal: expect.any(AbortSignal), - }, - ); - expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toMatchObject({ - label: expectedName, - default_skill_name: expectedSkillName, - bot_token: token, - service_ids: ['user-service-a'], - }); - expect(random).toHaveBeenCalledTimes(label?.trim() ? 0 : 1); -}); - -it('does not register using a username when Telegram omits a usable bot name', async () => { - telegramFetch.mockResolvedValue( - response({ - ok: true, - result: { is_bot: true, first_name: ' ', username: 'not_the_bot_name' }, - }), - ); - await expect( - channelsApi.registerTelegram({ - botToken: '123456:TEST_ONLY_TOKEN', - serviceIds: [], - webhookBaseUrl: 'https://api.example.test', - }), - ).rejects.toEqual(new ChannelRegistrationError('botName')); - expect(fetchMock).not.toHaveBeenCalled(); -}); - -it('aborts a stalled name lookup without submitting a registration', async () => { - jest.useFakeTimers(); - try { - telegramFetch.mockImplementation( - (_input: string, init: RequestInit) => - new Promise((_resolve, reject) => { - init.signal?.addEventListener('abort', () => - reject(new DOMException('Lookup aborted', 'AbortError')), - ); - }), - ); - const registration = channelsApi.registerTelegram({ - botToken: '123456:TEST_ONLY_TOKEN', - serviceIds: [], - webhookBaseUrl: 'https://api.example.test', - }); - const rejection = expect(registration).rejects.toEqual( - new ChannelRegistrationError('botName'), - ); - await jest.advanceTimersByTimeAsync(15_000); - await rejection; - expect(fetchMock).not.toHaveBeenCalled(); - } finally { - jest.useRealTimers(); - } -}); diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts deleted file mode 100644 index 55b0d6d51d..0000000000 --- a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { authFetch } from '@/shared/auth/fetch'; -import { channelRuntimeConfigFixture } from '../../../tests/fixtures/channelRuntimeConfig'; -import { channelRuntimeConfigApi } from './channelRuntimeConfigApi'; -import { ChannelApiError } from './channelsApi'; - -jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); -const fetchMock = jest.mocked(authFetch); -const response = (value: unknown) => - ({ ok: true, status: 200, json: async () => value }) as Response; - -it('reads the exact identity and legacy skill while discarding unexpected credential fields', async () => { - fetchMock.mockResolvedValue( - response({ - ...channelRuntimeConfigFixture, - registration_id: 'registration/a', - runtime_config: { - ...channelRuntimeConfigFixture.runtime_config, - default_skill: { name: '', version: '' }, - access_token: 'TEST_ONLY_SECRET', - }, - agent_key: { raw_key: 'TEST_ONLY_SECRET' }, - webhook_url: 'TEST_ONLY_SECRET', - }), - ); - const detail = await channelRuntimeConfigApi.get( - 'registration/a', - 'scope-alpha', - ); - expect(fetchMock.mock.calls[0][0]).toBe( - '/api/channels/registrations/registration%2Fa/runtime-config', - ); - expect(detail.runtimeConfig.defaultSkill).toEqual({ - name: 'team-helper', - version: '2.1', - }); - expect(JSON.stringify(detail)).not.toContain('TEST_ONLY_SECRET'); - fetchMock.mockResolvedValue( - response({ ...channelRuntimeConfigFixture, scope_id: 'scope-other' }), - ); - await expect( - channelRuntimeConfigApi.get('registration-alpha', 'scope-alpha'), - ).rejects.toEqual(new ChannelApiError(404)); -}); - -it('rejects an unrecognized config or receipt instead of replacing it with defaults', async () => { - fetchMock.mockResolvedValue(response(channelRuntimeConfigFixture)); - const detail = await channelRuntimeConfigApi.get( - 'registration-alpha', - 'scope-alpha', - ); - fetchMock.mockResolvedValue( - response({ - ...channelRuntimeConfigFixture, - runtime_config: { - ...channelRuntimeConfigFixture.runtime_config, - credential_source_mode: 'future_mode', - }, - }), - ); - await expect( - channelRuntimeConfigApi.get('registration-alpha', 'scope-alpha'), - ).rejects.toThrow('Unsupported channel credential source.'); - fetchMock.mockResolvedValue( - response({ - status: 'accepted', - registration_id: 'registration-other', - command_id: 'command-alpha', - }), - ); - await expect( - channelRuntimeConfigApi.update('registration-alpha', detail.runtimeConfig), - ).rejects.toThrow('not acknowledged'); -}); diff --git a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts b/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts deleted file mode 100644 index 1c64721e82..0000000000 --- a/apps/aevatar-console-web/src/shared/api/channelRuntimeConfigApi.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { authFetch } from '@/shared/auth/fetch'; -import { ChannelApiError, registrationPath } from './channelsApi'; -import { expectArray, expectRecord, readString } from './http/decoders'; - -export interface ChannelRuntimeConfig { - readonly instructions: string; - readonly defaultSkill: { readonly name: string; readonly version: string }; - readonly toolSetRefs: readonly string[]; - readonly extraToolNames: readonly string[]; - readonly serviceSelectors: readonly { - readonly serviceSlug: string; - readonly endpointNames: readonly string[]; - }[]; - readonly credentialSourceMode: - | 'registration_agent_key' - | 'sender_binding' - | 'unspecified'; -} - -export interface ChannelConfigUpdate { - readonly authorizationMode: 'nyxid_default' | 'explicit_service_allowlist'; - readonly serviceIds: readonly string[]; - readonly runtimeConfig: ChannelRuntimeConfig; -} - -export interface ChannelConfigDetail extends ChannelConfigUpdate { - readonly registrationId: string; - readonly scopeId: string; - readonly platform: string; - readonly stateVersion: number; -} - -export interface ChannelConfigReceipt { - readonly registrationId: string; - readonly commandId: string; -} - -export type ChannelConfigField = - | 'skill' - | 'version' - | 'instructions' - | 'services'; -export class ChannelConfigError extends ChannelApiError { - constructor( - status: number, - readonly fields: readonly ChannelConfigField[], - ) { - super(status); - this.name = 'ChannelConfigError'; - } -} - -function strings(value: unknown, label: string): string[] { - return expectArray(value, label, (item) => { - if (typeof item !== 'string') throw new Error(`Invalid ${label}.`); - return item; - }); -} - -function decodeDetail( - value: unknown, - registrationId: string, - scopeId: string, -): ChannelConfigDetail { - const row = expectRecord(value, 'Channel configuration'); - if (row.registration_id !== registrationId || row.scope_id !== scopeId) - throw new ChannelApiError(404); - if ( - row.authorization_mode !== 'nyxid_default' && - row.authorization_mode !== 'explicit_service_allowlist' - ) - throw new Error('Unsupported channel authorization.'); - if ( - typeof row.state_version !== 'number' || - !Number.isSafeInteger(row.state_version) || - row.state_version < 0 - ) - throw new Error('Invalid channel configuration version.'); - const config = expectRecord( - row.runtime_config, - 'Channel runtime configuration', - ); - // The top-level skill includes the backend's legacy registration fallback. - const skill = expectRecord( - row.default_skill ?? config.default_skill, - 'Channel skill', - ); - const mode = config.credential_source_mode; - if ( - mode !== 'registration_agent_key' && - mode !== 'sender_binding' && - mode !== 'unspecified' - ) - throw new Error('Unsupported channel credential source.'); - return { - registrationId, - scopeId, - platform: readString(row, 'platform', 'Channel platform'), - stateVersion: row.state_version, - authorizationMode: row.authorization_mode, - serviceIds: - row.authorization_mode === 'nyxid_default' - ? [] - : strings(row.service_ids, 'Service IDs'), - // Project only the editable contract; never cache the transport object. - runtimeConfig: { - instructions: readString(config, 'instructions', 'Bot instructions'), - defaultSkill: { - name: readString(skill, 'name', 'Skill name'), - version: readString(skill, 'version', 'Skill version'), - }, - toolSetRefs: strings(config.tool_set_refs, 'Tool set references'), - extraToolNames: strings(config.extra_tool_names, 'Extra tool names'), - serviceSelectors: expectArray( - config.nyxid_service_selectors, - 'Service selectors', - (value) => { - const selector = expectRecord(value, 'Service selector'); - return { - serviceSlug: readString(selector, 'service_slug', 'Service slug'), - endpointNames: strings(selector.endpoint_names, 'Endpoint names'), - }; - }, - ), - credentialSourceMode: mode, - }, - }; -} - -export function channelConfigPayload(input: ChannelConfigUpdate) { - const config = input.runtimeConfig; - return { - authorization_mode: input.authorizationMode, - service_ids: [...input.serviceIds].sort(), - runtime_config: runtimeConfigPayload(config), - }; -} - -function runtimeConfigPayload(config: ChannelRuntimeConfig) { - return { - instructions: config.instructions, - default_skill: { - name: config.defaultSkill.name.trim(), - version: config.defaultSkill.version, - }, - tool_set_refs: [...config.toolSetRefs], - extra_tool_names: [...config.extraToolNames], - nyxid_service_selectors: config.serviceSelectors.map((selector) => ({ - service_slug: selector.serviceSlug, - endpoint_names: [...selector.endpointNames], - })), - credential_source_mode: config.credentialSourceMode, - }; -} - -export function channelConfigMatches( - actual: ChannelConfigUpdate, - expected: ChannelConfigUpdate, -): boolean { - return ( - JSON.stringify(channelConfigPayload(actual)) === - JSON.stringify(channelConfigPayload(expected)) - ); -} - -function decodeFieldErrors(value: unknown): ChannelConfigField[] { - if (!value || typeof value !== 'object') return []; - const fields = new Set(); - if ( - 'error' in value && - typeof value.error === 'string' && - [ - 'invalid_service_ids', - 'nyxid_user_service_not_accessible', - 'service_owner_forbidden', - 'channel_authorization_contract_invalid', - ].includes(value.error) - ) - fields.add('services'); - if ('field_errors' in value && Array.isArray(value.field_errors)) { - for (const error of value.field_errors) { - if (!error || typeof error !== 'object' || !('field' in error)) continue; - const field = error.field; - if (field === 'runtime_config.instructions') fields.add('instructions'); - else if (field === 'runtime_config.default_skill.name') - fields.add('skill'); - else if (field === 'runtime_config.default_skill.version') - fields.add('version'); - else if ( - typeof field === 'string' && - (field === 'service_ids' || - field.startsWith('runtime_config.nyxid_service_selectors')) - ) - fields.add('services'); - } - } - return [...fields]; -} - -export const channelRuntimeConfigApi = { - async get( - registrationId: string, - scopeId: string, - signal?: AbortSignal, - ): Promise { - const response = await authFetch( - `${registrationPath(registrationId)}/runtime-config`, - { - signal, - cache: 'no-store', - headers: { Accept: 'application/json' }, - }, - ); - if (!response.ok) throw new ChannelApiError(response.status); - return decodeDetail(await response.json(), registrationId, scopeId); - }, - async update( - registrationId: string, - input: ChannelRuntimeConfig, - ): Promise { - const response = await authFetch( - `${registrationPath(registrationId)}/runtime-config`, - { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - // Omitting service selection preserves the current backend authorization. - body: JSON.stringify({ runtime_config: runtimeConfigPayload(input) }), - }, - ); - const value: unknown = await response.json().catch(() => null); - if (!response.ok) - throw new ChannelConfigError(response.status, decodeFieldErrors(value)); - const receipt = expectRecord(value, 'Channel configuration update'); - if ( - receipt.status !== 'accepted' || - receipt.registration_id !== registrationId - ) - throw new Error('Channel configuration update was not acknowledged.'); - const commandId = readString(receipt, 'command_id', 'Channel command'); - if (!commandId.trim()) throw new Error('Missing channel command.'); - return { registrationId, commandId }; - }, -}; diff --git a/apps/aevatar-console-web/src/shared/api/channelSkillsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelSkillsApi.test.ts new file mode 100644 index 0000000000..de4241c37c --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelSkillsApi.test.ts @@ -0,0 +1,70 @@ +import { authFetch } from '@/shared/auth/fetch'; +import { persistAuthSession } from '@/shared/auth/session'; +import { createNyxIDServiceSession } from '../../../tests/fixtures/nyxidServiceSession'; +import { searchChannelSkills } from './channelSkillsApi'; + +jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); +jest.mock('@/shared/auth/config', () => ({ + getNyxIDRuntimeConfig: () => ({ + baseUrl: 'https://nyx.example.test', + enabled: true, + }), +})); +const fetchMock = jest.mocked(authFetch); +it('uses the current user token and private proxy search, preserving shared skills and cursor pagination', async () => { + const session = createNyxIDServiceSession(); + persistAuthSession(session); + fetchMock.mockResolvedValue({ + ok: true, + json: async () => ({ + data: { + items: [ + { + guid: 'skill-one', + name: 'shared-skill', + description: 'Shared with the team', + myAccessReason: 'shared-via-org', + createdByEmail: 'private@example.test', + }, + ], + meta: { hasMore: true, nextCursor: 'cursor-two' }, + }, + error: null, + }), + } as Response); + const signal = new AbortController().signal; + expect( + await searchChannelSkills('team & docs', 'cursor-one', signal), + ).toEqual({ + items: [ + { + id: 'skill-one', + name: 'shared-skill', + description: 'Shared with the team', + }, + ], + nextCursor: 'cursor-two', + }); + const [url, init] = fetchMock.mock.calls[0]; + expect(new URL(String(url)).pathname).toBe( + '/api/v1/proxy/s/ornn-api/api/v1/skill-search', + ); + expect(Object.fromEntries(new URL(String(url)).searchParams)).toEqual({ + scope: 'private', + mode: 'keyword', + q: 'team & docs', + limit: '50', + cursor: 'cursor-one', + }); + expect(init).toMatchObject({ + credentials: 'omit', + cache: 'no-store', + headers: { Authorization: `Bearer ${session.tokens.accessToken}` }, + }); + expect(init?.signal).toBe(signal); + fetchMock.mockResolvedValue({ + ok: true, + json: async () => ({ error: { message: 'TEST_SECRET' }, data: null }), + } as Response); + await expect(searchChannelSkills('')).rejects.toThrow('Skill search failed.'); +}); diff --git a/apps/aevatar-console-web/src/shared/api/channelSkillsApi.ts b/apps/aevatar-console-web/src/shared/api/channelSkillsApi.ts new file mode 100644 index 0000000000..b86de46452 --- /dev/null +++ b/apps/aevatar-console-web/src/shared/api/channelSkillsApi.ts @@ -0,0 +1,76 @@ +import { ensureActiveAuthSession } from '@/shared/auth/client'; +import { getNyxIDRuntimeConfig } from '@/shared/auth/config'; +import { authFetch } from '@/shared/auth/fetch'; +import { ChannelApiError } from './channelsApi'; +import { + expectArray, + expectBoolean, + expectRecord, + readOptionalString, + readString, +} from './http/decoders'; + +export interface ChannelSkillChoice { + readonly id: string; + readonly name: string; + readonly description: string; +} + +export async function searchChannelSkills( + search: string, + cursor?: string, + signal?: AbortSignal, +) { + const config = getNyxIDRuntimeConfig(); + if (config.configurationError || !config.baseUrl) + throw new Error('NyxID is unavailable.'); + const session = await ensureActiveAuthSession(); + if (!session) throw new ChannelApiError(401); + const url = new URL( + `${config.baseUrl}/api/v1/proxy/s/ornn-api/api/v1/skill-search`, + ); + url.search = new URLSearchParams({ + scope: 'private', + mode: 'keyword', + q: search, + limit: '50', + ...(cursor ? { cursor } : {}), + }).toString(); + const response = await authFetch(url.href, { + signal, + credentials: 'omit', + cache: 'no-store', + headers: { + Accept: 'application/json', + Authorization: `Bearer ${session.tokens.accessToken}`, + }, + }); + if (!response.ok) throw new ChannelApiError(response.status); + const body = expectRecord(await response.json(), 'Skill search'); + if (body.error != null) throw new Error('Skill search failed.'); + const data = expectRecord(body.data, 'Skill results'); + const meta = expectRecord(data.meta, 'Skill pagination'); + const hasMore = expectBoolean(meta.hasMore, 'More skills'); + const nextCursor = + readOptionalString(meta, 'nextCursor', 'Skill cursor') || undefined; + if (hasMore && (!nextCursor || nextCursor === cursor)) + throw new Error('Invalid skill pagination.'); + const items = expectArray( + data.items, + 'Skills', + (value): ChannelSkillChoice => { + const row = expectRecord(value, 'Skill'); + const id = readString(row, 'guid', 'Skill ID'); + const name = readString(row, 'name', 'Skill name'); + if (!id.trim() || !name.trim()) + throw new Error('Missing skill identity.'); + return { + id, + name, + description: + readOptionalString(row, 'description', 'Skill description') || '', + }; + }, + ); + return { items, nextCursor: hasMore ? nextCursor : undefined }; +} diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts index c71380d57f..09eb3b00f8 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.test.ts @@ -1,5 +1,5 @@ import { authFetch } from '@/shared/auth/fetch'; -import { ChannelApiError, channelsApi } from './channelsApi'; +import { channelsApi } from './channelsApi'; jest.mock('@/shared/auth/fetch', () => ({ authFetch: jest.fn() })); const fetchMock = jest.mocked(authFetch); @@ -9,175 +9,142 @@ const response = (value: unknown, status = 200) => status, json: async () => value, }) as Response; - -const registration = { - id: 'registration-alpha', - platform: 'telegram', - scope_id: 'scope-alpha', +const bound = { + id: 'reg:one/a', + nyx_channel_bot_id: 'bot-one', + platform: 'discord', + label: 'Support', + binding_status: 'bound', + availability_status: 'available', + nyx_status: 'active', owned: true, - nyx_channel_bot_id: 'bot-alpha', - nyx_provider_slug: 'telegram-provider', - nyx_agent_api_key_id: 'key-alpha', - workflow_result_delivery_status: 'enabled', + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work'], + state_version: 12, }; - afterEach(() => fetchMock.mockReset()); -describe('channel API boundary', () => { - it('reads the owner list and keeps only safe display fields with the exact typed skill', async () => { - fetchMock.mockResolvedValue( - response([ - { - ...registration, - authorization_mode: 'explicit_service_allowlist', - service_ids: ['us-work', 'us-model'], - default_skill: { name: 'review.skill', version: '2.4' }, - agent_key: { api_key_id: 'key-current', raw_key: 'TEST_ONLY_SECRET' }, - runtime_config: { instructions: 'private instructions' }, - access_token: 'TEST_ONLY_SECRET', - webhook_url: 'https://example.invalid/private', - }, - { ...registration, id: 'registration-other-owner', owned: false }, - ]), - ); - const result = await channelsApi.list(); - expect(fetchMock.mock.calls[0][0]).toBe('/api/channels/registrations'); - expect(result).toEqual([ +it('reads bound and unbound inventory without scope IDs and projects only safe fields', async () => { + fetchMock.mockResolvedValue( + response([ + { + ...bound, + access_token: 'TEST_SECRET', + webhook_url: 'TEST_SECRET', + runtime_config: { secret: 'TEST_SECRET' }, + }, { - id: 'registration-alpha', - platform: 'telegram', - scopeId: 'scope-alpha', - botId: 'bot-alpha', - providerSlug: 'telegram-provider', - agentKeyId: 'key-current', - skill: { name: 'review.skill', version: '2.4' }, - workflowDeliveryStatus: 'enabled', - owned: true, - serviceAuthorization: { - kind: 'explicit', - serviceIds: ['us-work', 'us-model'], - }, + ...bound, + id: null, + nyx_channel_bot_id: 'bot-two', + binding_status: 'unbound', + authorization_mode: null, + skill_name: '', }, - ]); - expect(JSON.stringify(result)).not.toMatch( - /TEST_ONLY_SECRET|instructions|webhook/, - ); + { ...bound, owned: false }, + ]), + ); + const rows = await channelsApi.list(); + expect(rows).toHaveLength(2); + expect(rows[0]).toMatchObject({ + id: 'reg:one/a', + botId: 'bot-one', + label: 'Support', + skill: { name: 'support', version: null }, + stateVersion: 12, }); - - it('accepts the deployed name-only contract without inventing a version', async () => { - fetchMock.mockResolvedValue( - response([ - { ...registration, default_skill_name: 'legacy-skill' }, - { - ...registration, - id: 'registration-unset', - default_skill: { name: '', version: '7' }, - }, - ]), - ); - expect((await channelsApi.list()).map((row) => row.skill)).toEqual([ - { name: 'legacy-skill', version: null }, - null, - ]); + expect(rows[1]).toMatchObject({ + id: null, + bindingStatus: 'unbound', + skill: null, + serviceAuthorization: { kind: 'unavailable' }, }); + expect(JSON.stringify(rows)).not.toMatch( + /TEST_SECRET|webhook|runtime_config|scope/, + ); + fetchMock.mockResolvedValue(response([{ ...bound, id: null }])); + await expect(channelsApi.list()).rejects.toThrow(); + fetchMock.mockResolvedValue(response({ registrations: [] })); + await expect(channelsApi.list()).rejects.toThrow(); + fetchMock.mockResolvedValue( + response({ error: 'nyxid_channel_bots_unavailable' }, 502), + ); + await expect(channelsApi.list()).rejects.toMatchObject({ status: 502 }); +}); - it('rejects malformed list responses instead of presenting an empty collection', async () => { - fetchMock.mockResolvedValue(response({ registrations: [] })); - await expect(channelsApi.list()).rejects.toThrow(); +it('adopts and updates through the narrow contract and keeps admission separate from completion', async () => { + fetchMock.mockResolvedValue( + response( + { + status: 'accepted', + registration_id: bound.id, + command_id: 'command-one', + relay_callback_url: 'TEST_SECRET', + nyx_agent_api_key: 'TEST_SECRET', + }, + 202, + ), + ); + const config = { + skillName: ' /Support ', + authorizationMode: 'explicit_service_allowlist' as const, + serviceIds: ['us-work'], + }; + expect(await channelsApi.adopt('bot-one', config)).toEqual({ + registrationId: bound.id, + commandId: 'command-one', }); - - it('distinguishes explicit empty, NyxID default, missing and unknown service authorization', async () => { - fetchMock.mockResolvedValue( - response([ - { - ...registration, - authorization_mode: 'explicit_service_allowlist', - service_ids: [], - }, - { ...registration, authorization_mode: 'nyxid_default' }, - { ...registration, authorization_mode: 'explicit_service_allowlist' }, - { - ...registration, - authorization_mode: 'future_mode', - service_ids: ['us-work'], - }, - ]), - ); - expect( - (await channelsApi.list()).map((row) => row.serviceAuthorization), - ).toEqual([ - { kind: 'explicit', serviceIds: [] }, - { kind: 'nyxidDefault' }, - { kind: 'unavailable' }, - { kind: 'unavailable' }, - ]); + expect(JSON.parse(String(fetchMock.mock.calls[0][1]?.body))).toEqual({ + nyx_channel_bot_id: 'bot-one', + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work'], }); - - it('rejects malformed saved service IDs rather than inventing an authorization list', async () => { - fetchMock.mockResolvedValue( - response([ - { - ...registration, - authorization_mode: 'explicit_service_allowlist', - service_ids: ['us-work', null], - }, - ]), - ); - await expect(channelsApi.list()).rejects.toThrow( - 'Invalid authorized service identity.', - ); + await channelsApi.update(bound.id, config); + expect(fetchMock.mock.calls[1][0]).toBe( + '/api/channels/registrations/reg%3Aone%2Fa', + ); + expect(JSON.parse(String(fetchMock.mock.calls[1][1]?.body))).toEqual({ + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['us-work'], }); - - it('encodes opaque registration IDs and rejects a status for a different registration', async () => { - const id = 'registration:alpha/one + two'; - fetchMock.mockResolvedValue( - response({ - registration_id: id, - status: 'new_backend_state', - workflow_result_delivery_status: 'enabled', - }), - ); - expect(await channelsApi.status(id)).toEqual({ - registrationId: id, - status: 'new_backend_state', - workflowDeliveryStatus: 'enabled', - }); - expect(fetchMock.mock.calls[0][0]).toBe( - '/api/channels/registrations/registration%3Aalpha%2Fone%20%2B%20two/status', - ); - fetchMock.mockResolvedValue( - response({ registration_id: 'registration-other', status: 'active' }), - ); - await expect(channelsApi.status(id)).rejects.toThrow('identity mismatch'); + fetchMock.mockResolvedValue( + response( + { error: 'ambiguous_channel_bot_route', secret: 'TEST_SECRET' }, + 409, + ), + ); + await expect(channelsApi.adopt('bot-one', config)).rejects.toMatchObject({ + reason: 'conflict', }); - - it('uses the registration identity for deletion and discards cleanup diagnostics', async () => { - fetchMock.mockResolvedValue( - response({ status: 'deleted', warnings: ['TEST_ONLY_SECRET'] }), - ); - await expect(channelsApi.remove('registration:alpha/one')).resolves.toEqual( - { hasWarnings: true }, - ); - expect(fetchMock).toHaveBeenCalledWith( - '/api/channels/registrations/registration%3Aalpha%2Fone', - expect.objectContaining({ method: 'DELETE' }), - ); - fetchMock.mockResolvedValue(response({ status: 'accepted' }, 202)); - await expect(channelsApi.remove('registration-alpha')).rejects.toThrow( - 'not confirmed', - ); + fetchMock.mockRejectedValue(new Error('TEST_SECRET')); + await expect(channelsApi.adopt('bot-one', config)).rejects.toMatchObject({ + reason: 'uncertain', }); +}); - it('preserves HTTP failure classification without retaining backend secret material', async () => { - const json = jest.fn().mockResolvedValue({ message: 'TEST_ONLY_SECRET' }); - fetchMock.mockResolvedValue({ - ok: false, - status: 404, - json, - } as unknown as Response); - await expect(channelsApi.status('registration-alpha')).rejects.toEqual( - new ChannelApiError(404), - ); - expect(json).not.toHaveBeenCalled(); +it('requires exact owned detail identity and strips removal diagnostics', async () => { + fetchMock.mockResolvedValue(response(bound)); + expect(await channelsApi.get(bound.id)).toMatchObject({ + id: bound.id, + stateVersion: 12, + }); + fetchMock.mockResolvedValue(response({ ...bound, id: 'reg-other' })); + await expect(channelsApi.get(bound.id)).rejects.toMatchObject({ + status: 404, + }); + fetchMock.mockResolvedValue(response({ ...bound, owned: false })); + await expect(channelsApi.get(bound.id)).rejects.toMatchObject({ + status: 404, }); + fetchMock.mockResolvedValue( + response({ status: 'deleted', warnings: ['TEST_SECRET'] }), + ); + expect(await channelsApi.remove(bound.id)).toEqual({ hasWarnings: true }); + expect(fetchMock.mock.calls.at(-1)).toEqual([ + '/api/channels/registrations/reg%3Aone%2Fa', + expect.objectContaining({ method: 'DELETE' }), + ]); }); diff --git a/apps/aevatar-console-web/src/shared/api/channelsApi.ts b/apps/aevatar-console-web/src/shared/api/channelsApi.ts index 040a17dd9d..8111c52153 100644 --- a/apps/aevatar-console-web/src/shared/api/channelsApi.ts +++ b/apps/aevatar-console-web/src/shared/api/channelsApi.ts @@ -13,10 +13,13 @@ export type ChannelServiceAuthorization = | { readonly kind: 'unavailable' }; export interface ChannelRegistration { - readonly id: string; + readonly id: string | null; + readonly botId: string; + readonly label: string | null; readonly platform: string; - readonly scopeId: string; - readonly botId: string | null; + readonly bindingStatus: 'bound' | 'unbound'; + readonly availabilityStatus: string | null; + readonly nyxStatus: string | null; readonly providerSlug: string | null; readonly agentKeyId: string | null; readonly skill: { @@ -24,28 +27,31 @@ export interface ChannelRegistration { readonly version: string | null; } | null; readonly workflowDeliveryStatus: string | null; + readonly stateVersion: number | null; readonly owned: boolean; readonly serviceAuthorization: ChannelServiceAuthorization; } -export interface ChannelStatus { +export interface ChannelConfiguration { + readonly skillName: string; + readonly authorizationMode: 'nyxid_default' | 'explicit_service_allowlist'; + readonly serviceIds: readonly string[]; +} + +export interface ChannelReceipt { readonly registrationId: string; - readonly status: string; - readonly workflowDeliveryStatus: string | null; + readonly commandId: string; } export class ChannelApiError extends Error { constructor(readonly status: number) { - // Backend error bodies can contain provisioning diagnostics. Keep them out - // of the query cache and use localized recovery copy in the page. + // Never retain provisioning diagnostics or credential material. super(`Channel request failed (${status}).`); this.name = 'ChannelApiError'; } } export type ChannelRegistrationFailure = - | 'token' - | 'botName' | 'services' | 'skill' | 'authorization' @@ -53,107 +59,51 @@ export type ChannelRegistrationFailure = | 'configuration' | 'rejected' | 'uncertain'; - -export class ChannelRegistrationError extends Error { - constructor(readonly reason: ChannelRegistrationFailure) { - super('Could not connect Telegram.'); +export class ChannelRegistrationError extends ChannelApiError { + constructor( + status: number, + readonly reason: ChannelRegistrationFailure, + ) { + super(status); this.name = 'ChannelRegistrationError'; } } -export interface TelegramRegistrationInput { - readonly botToken: string; - readonly label?: string; - readonly skillName?: string; - readonly serviceIds: readonly string[]; - readonly webhookBaseUrl: string; -} - -async function readTelegramBotName(botToken: string): Promise { - if (!/^[0-9]+:[A-Za-z0-9_-]+$/.test(botToken)) - throw new ChannelRegistrationError('token'); - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), 15_000); - try { - // Telegram requires the token in its API path. Use a plain, uncached - // request without Aevatar auth; never retain or log upstream errors/URLs. - const response = await fetch( - `https://api.telegram.org/bot${botToken}/getMe`, - { - method: 'POST', - credentials: 'omit', - cache: 'no-store', - referrerPolicy: 'no-referrer', - redirect: 'error', - signal: controller.signal, - }, - ); - if ([401, 404].includes(response.status)) - throw new ChannelRegistrationError('token'); - if (!response.ok) throw new ChannelRegistrationError('botName'); - const payload = expectRecord(await response.json(), 'Telegram response'); - if (payload.ok !== true) { - throw new ChannelRegistrationError( - [401, 404].includes(Number(payload.error_code)) ? 'token' : 'botName', - ); - } - const bot = expectRecord(payload.result, 'Telegram bot'); - const botName = readString(bot, 'first_name', 'Telegram bot name').trim(); - if (bot.is_bot !== true || !botName) - throw new ChannelRegistrationError('botName'); - return botName; - } catch (error) { - throw error instanceof ChannelRegistrationError - ? error - : new ChannelRegistrationError('botName'); - } finally { - clearTimeout(timeout); - } -} - -function registrationFailure(status: number, value: unknown) { - const code = - value && typeof value === 'object' && 'error' in value - ? value.error - : undefined; - if (code === 'missing_bot_token' || code === 'invalid_bot_token') - return 'token'; - if (code === 'invalid_service_ids') return 'services'; - if (code === 'invalid_default_skill' || code === 'skill_not_found') - return 'skill'; - if (status === 401 || status === 403) return 'authorization'; - if (status === 409) return 'conflict'; - if ( - code === 'missing_webhook_base_url' || - code === 'insecure_webhook_base_url' || - code === 'nyx_base_url_not_configured' - ) - return 'configuration'; - return status >= 500 ? 'uncertain' : 'rejected'; +export function normalizeChannelSkill(value: string): string { + return value.trim().replace(/^\//, '').toLowerCase(); } function decodeRegistration(value: unknown): ChannelRegistration { const row = expectRecord(value, 'Channel registration'); - const skill = - row.default_skill == null - ? null - : expectRecord(row.default_skill, 'Channel default skill'); + const id = readOptionalString(row, 'id', 'Registration ID') || null; + const botId = readString(row, 'nyx_channel_bot_id', 'Bot ID'); + const bindingStatus = row.binding_status; + if ( + !botId.trim() || + !['bound', 'unbound'].includes(String(bindingStatus)) || + (bindingStatus === 'bound' && !id?.trim()) + ) + throw new Error('Invalid channel binding identity.'); + const skillName = readOptionalString(row, 'skill_name', 'Skill name')?.trim(); const key = - row.agent_key == null - ? null - : expectRecord(row.agent_key, 'Channel agent key'); - const skillName = skill - ? readOptionalString(skill, 'name', 'Skill name')?.trim() - : readOptionalString(row, 'default_skill_name', 'Skill name')?.trim(); - const id = readString(row, 'id', 'Registration ID'); - if (!id.trim()) throw new Error('Missing channel registration ID.'); - // Explicitly project safe display fields. Never retain the transport object, - // webhook URL, runtime configuration, or unexpected credential fields. + row.agent_key == null ? null : expectRecord(row.agent_key, 'Agent key'); + const version = row.state_version; + if ( + version != null && + (typeof version !== 'number' || + !Number.isSafeInteger(version) || + version < 0) + ) + throw new Error('Invalid channel state version.'); return { id, + botId, platform: readString(row, 'platform', 'Channel platform'), - scopeId: readString(row, 'scope_id', 'Channel scope'), - botId: readOptionalString(row, 'nyx_channel_bot_id', 'Bot ID') || null, + label: readOptionalString(row, 'label', 'Channel label')?.trim() || null, + bindingStatus: bindingStatus === 'bound' ? 'bound' : 'unbound', + availabilityStatus: + readOptionalString(row, 'availability_status', 'Availability') || null, + nyxStatus: readOptionalString(row, 'nyx_status', 'NyxID status') || null, providerSlug: readOptionalString(row, 'nyx_provider_slug', 'Provider') || null, agentKeyId: @@ -161,27 +111,20 @@ function decodeRegistration(value: unknown): ChannelRegistration { ? readOptionalString(key, 'api_key_id', 'Agent key ID') : readOptionalString(row, 'nyx_agent_api_key_id', 'Agent key ID')) || null, - skill: skillName - ? { - name: skillName, - version: skill - ? readOptionalString(skill, 'version', 'Skill version')?.trim() || - null - : null, - } - : null, + skill: skillName ? { name: skillName, version: null } : null, workflowDeliveryStatus: readOptionalString( row, 'workflow_result_delivery_status', 'Workflow delivery status', ) || null, + stateVersion: typeof version === 'number' ? version : null, owned: expectBoolean(row.owned, 'Channel ownership'), - serviceAuthorization: decodeServiceAuthorization(row), + serviceAuthorization: decodeAuthorization(row), }; } -function decodeServiceAuthorization( +function decodeAuthorization( row: Record, ): ChannelServiceAuthorization { if (row.authorization_mode === 'nyxid_default') @@ -191,16 +134,54 @@ function decodeServiceAuthorization( row.service_ids == null ) return { kind: 'unavailable' }; - const serviceIds = expectArray( - row.service_ids, - 'Authorized services', - (id) => { - if (typeof id !== 'string' || !id.trim()) - throw new Error('Invalid authorized service identity.'); - return id; - }, + const serviceIds = expectArray(row.service_ids, 'Service IDs', (id) => { + if (typeof id !== 'string' || !id.trim()) + throw new Error('Invalid service identity.'); + return id; + }); + if (new Set(serviceIds).size !== serviceIds.length) + throw new Error('Duplicate service identity.'); + return { kind: 'explicit', serviceIds }; +} + +export function channelConfiguration( + row: ChannelRegistration, +): ChannelConfiguration | null { + if (row.serviceAuthorization.kind === 'unavailable') return null; + return { + skillName: row.skill?.name ?? '', + authorizationMode: + row.serviceAuthorization.kind === 'explicit' + ? 'explicit_service_allowlist' + : 'nyxid_default', + serviceIds: + row.serviceAuthorization.kind === 'explicit' + ? row.serviceAuthorization.serviceIds + : [], + }; +} + +export function channelConfigPayload(input: ChannelConfiguration) { + return { + skill_name: normalizeChannelSkill(input.skillName), + authorization_mode: input.authorizationMode, + service_ids: + input.authorizationMode === 'nyxid_default' + ? [] + : [...input.serviceIds].sort(), + }; +} + +export function channelConfigMatches( + actual: ChannelRegistration, + expected: ChannelConfiguration, +): boolean { + const config = channelConfiguration(actual); + return ( + config !== null && + JSON.stringify(channelConfigPayload(config)) === + JSON.stringify(channelConfigPayload(expected)) ); - return { kind: 'explicit', serviceIds: [...new Set(serviceIds)] }; } async function request( @@ -210,6 +191,7 @@ async function request( ): Promise { const response = await authFetch(path, { ...init, + cache: 'no-store', headers: { Accept: 'application/json' }, }); if (!response.ok) throw new ChannelApiError(response.status); @@ -222,90 +204,117 @@ export function registrationPath(registrationId: string): string { return `/api/channels/registrations/${encodeURIComponent(registrationId)}`; } +async function submit( + path: string, + body: object, + registrationId?: string, +): Promise { + let response: Response; + try { + response = await authFetch(path, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + } catch { + throw new ChannelRegistrationError(0, 'uncertain'); + } + const value: unknown = await response.json().catch(() => null); + if (!response.ok) { + const code = + value && typeof value === 'object' && 'error' in value + ? value.error + : null; + const reason = + code === 'invalid_service_ids' || + code === 'service_owner_forbidden' || + code === 'channel_authorization_contract_invalid' + ? 'services' + : code === 'invalid_runtime_config' || code === 'skill_not_found' + ? 'skill' + : [401, 403].includes(response.status) + ? 'authorization' + : response.status === 409 + ? 'conflict' + : code === 'insecure_webhook_base_url' + ? 'configuration' + : response.status >= 500 + ? 'uncertain' + : 'rejected'; + throw new ChannelRegistrationError(response.status, reason); + } + if (!value || typeof value !== 'object' || Array.isArray(value)) + throw new ChannelRegistrationError(response.status, 'uncertain'); + const receipt = expectRecord(value, 'Channel receipt'); + if ( + receipt.status !== 'accepted' || + typeof receipt.registration_id !== 'string' || + !receipt.registration_id.trim() || + typeof receipt.command_id !== 'string' || + !receipt.command_id.trim() || + (registrationId && receipt.registration_id !== registrationId) + ) + throw new ChannelRegistrationError(response.status, 'uncertain'); + return { + registrationId: receipt.registration_id, + commandId: receipt.command_id, + }; +} + export const channelsApi = { - async registerTelegram( - input: TelegramRegistrationInput, - ): Promise<{ readonly registrationId: string }> { - const botToken = input.botToken.trim(); - const label = input.label?.trim() ?? ''; - const skillName = input.skillName?.trim() ?? ''; - const botName = - !label || !skillName ? await readTelegramBotName(botToken) : ''; - const channelName = - label || `${botName}-${Math.floor(100_000 + Math.random() * 900_000)}`; - // This request carries the token only in the authenticated POST body. - // Do not use a mutation cache, navigation state, or persistent draft. - let response: Response; - try { - response = await authFetch('/api/channels/registrations', { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - platform: 'telegram', - bot_token: botToken, - label: channelName, - default_skill_name: skillName || botName, - webhook_base_url: input.webhookBaseUrl, - authorization_mode: 'explicit_service_allowlist', - service_ids: [...input.serviceIds], - }), - }); - } catch { - throw new ChannelRegistrationError('uncertain'); - } - const body: unknown = await response.json().catch(() => null); - if (!response.ok) - throw new ChannelRegistrationError( - registrationFailure(response.status, body), - ); - if ( - !body || - typeof body !== 'object' || - !('status' in body) || - body.status !== 'accepted' || - !('registration_id' in body) || - typeof body.registration_id !== 'string' || - !body.registration_id.trim() - ) - throw new ChannelRegistrationError('uncertain'); - // Retain the identity needed for readback, not raw provisioning output. - return { registrationId: body.registration_id }; + adopt(botId: string, input: ChannelConfiguration): Promise { + if (!botId.trim()) throw new Error('Missing bot identity.'); + return submit('/api/channels/registrations', { + nyx_channel_bot_id: botId, + ...channelConfigPayload(input), + }); + }, + update( + registrationId: string, + input: ChannelConfiguration, + ): Promise { + return submit( + registrationPath(registrationId), + channelConfigPayload(input), + registrationId, + ); }, list(signal?: AbortSignal): Promise { return request( '/api/channels/registrations', - (value) => - expectArray(value, 'Channel registrations', decodeRegistration).filter( - (row) => row.owned, - ), + (value) => { + const rows = expectArray( + value, + 'Channel registrations', + decodeRegistration, + ).filter((row) => row.owned); + if (new Set(rows.map((row) => row.botId)).size !== rows.length) + throw new Error('Ambiguous channel bot inventory.'); + return rows; + }, { signal }, ); }, - status(registrationId: string, signal?: AbortSignal): Promise { + get( + registrationId: string, + signal?: AbortSignal, + ): Promise { return request( - `${registrationPath(registrationId)}/status`, + registrationPath(registrationId), (value) => { - const row = expectRecord(value, 'Channel status'); - const returnedId = readString( - row, - 'registration_id', - 'Registration ID', - ); - if (returnedId !== registrationId) - throw new Error('Channel status identity mismatch.'); - return { - registrationId: returnedId, - status: readString(row, 'status', 'Inbound status'), - workflowDeliveryStatus: - readOptionalString( - row, - 'workflow_result_delivery_status', - 'Workflow delivery status', - ) || null, - }; + const row = decodeRegistration(value); + if ( + row.id !== registrationId || + !row.owned || + row.bindingStatus !== 'bound' + ) + throw new ChannelApiError(404); + if (row.stateVersion === null) + throw new Error('Missing channel detail state version.'); + return row; }, { signal }, ); diff --git a/apps/aevatar-console-web/src/shared/auth/routeAccess.ts b/apps/aevatar-console-web/src/shared/auth/routeAccess.ts new file mode 100644 index 0000000000..7cd76f050e --- /dev/null +++ b/apps/aevatar-console-web/src/shared/auth/routeAccess.ts @@ -0,0 +1,10 @@ +const benchmarkEnabled = process.env.AEVATAR_WORKFLOW_CANVAS_BENCHMARK === '1'; +export const PUBLIC_ROUTES = new Set([ + '/login', + '/auth/callback', + ...(benchmarkEnabled ? ['/workflow-canvas-benchmark'] : []), +]); + +export function requiresGlobalAuthGate(pathname: string): boolean { + return !PUBLIC_ROUTES.has(pathname) && pathname !== '/studio'; +} diff --git a/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts b/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts deleted file mode 100644 index 945574aa8a..0000000000 --- a/apps/aevatar-console-web/tests/fixtures/channelRuntimeConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -export const channelRuntimeConfigFixture = { - registration_id: 'registration-alpha', - scope_id: 'scope-alpha', - platform: 'telegram', - state_version: 12, - authorization_mode: 'explicit_service_allowlist', - service_ids: ['user-service-github'], - default_skill: { name: 'team-helper', version: '2.1' }, - runtime_config: { - instructions: 'Keep replies concise.', - default_skill: { name: 'team-helper', version: '2.1' }, - tool_set_refs: ['approved-tools'], - extra_tool_names: ['calendar_lookup'], - nyxid_service_selectors: [ - { service_slug: 'api-github', endpoint_names: ['read_issue'] }, - ], - credential_source_mode: 'registration_agent_key', - }, -}; From 4ffa44015dba3b6f495daf4aa3d7389511ae8c8f Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:18:38 +0800 Subject: [PATCH 160/167] Fix channel binding design and live contract feedback --- .../specs/2026-09-17-channel-bot-adoption.md | 28 ++++- apps/aevatar-console-web/package.json | 1 + apps/aevatar-console-web/pnpm-lock.yaml | 8 ++ .../src/locales/channelMessages.en-US.ts | 11 +- .../src/locales/channelMessages.zh-CN.ts | 8 +- .../channels/ChannelConfigurationPage.tsx | 18 +++- .../channels/ChannelEditPage.test.tsx | 22 +++- .../channels/ChannelSkillField.tsx | 68 ++++++++++-- .../channels/ChannelsPage.test.tsx | 19 ++++ .../channels/ChannelsPage.tsx | 43 ++++++-- .../channels/connectionStyles.ts | 9 +- .../channels/presentation.tsx | 46 +++++--- .../channels/styles.ts | 101 ++++++++++++------ .../src/shared/api/channelsApi.test.ts | 12 ++- .../src/shared/api/channelsApi.ts | 11 +- 15 files changed, 321 insertions(+), 84 deletions(-) diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md index 9d333e8488..5b7884ed33 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md @@ -20,6 +20,14 @@ Telegram token creation flow. - Skill creation opens https://ornn.chrono-ai.fun/skills/new/generate in a new tab. Its tooltip explains the return-and-refresh action. Refresh is always visible beside the selector and preserves the current selection. +- Match Figma's DM Sans typography, continuous white surface, restrained blue + actions, 60px guide icons and neutral connecting arrows. At a 1440px viewport, + the inventory content is 1080px wide; the table has a 46px header and 88px + rows, horizontal separators and no enclosing card border. +- The binding form identifies the bot by label and platform and shows Not + bound. The Skill popup contains a search input, skill names and descriptions, + and the external creation footer. Keyboard users can move from search into + the options with Arrow Down and dismiss the popup with Escape. ## API Contract @@ -60,10 +68,22 @@ are supported. ## Deployment And Verification -The reference `origin/feature/integrate` still exposes the old channel contract -at implementation time. Deploy the #3652 backend contract before enabling this -frontend. There is no token-provisioning fallback, synthetic inventory, or -local backend substitute. +The #3652 contract was merged into `feature/integrate` by +[backend PR #3653](https://github.com/aevatarAI/aevatar/pull/3653). +During live verification on 2026-09-17, the remote deployment initially returned +HTTP 200 with the old inventory shape, which the new decoder rejected. The UI +now distinguishes an unavailable binding contract from a generic load failure. +It never infers a binding state from legacy fields or treats failed decoding as +an empty inventory. + +The remote deployment subsequently exposed the new exact-detail GET/POST +routes and the new inventory shape. Verification against that real deployment +displayed nine bots (eight bound and one unbound), loaded NyxID services and +Ornn skills, and confirmed server search, keyboard selection and retention +after refreshing skills. Desktop (1440px) and mobile (375px) screenshots were +inspected in the existing Chrome session. No production bot or service grant +was mutated during this verification; write confirmation and uncertain-write +behavior are covered by focused tests. Focused tests cover inventory/null identities, request shapes and safe decoding, private Ornn proxy authorization/pagination, static guidance, diff --git a/apps/aevatar-console-web/package.json b/apps/aevatar-console-web/package.json index 3162f229b6..eb46f4bbea 100644 --- a/apps/aevatar-console-web/package.json +++ b/apps/aevatar-console-web/package.json @@ -31,6 +31,7 @@ "@aevatar-react-sdk/types": "0.5.0", "@ant-design/icons": "^6.1.0", "@ant-design/pro-components": "3.1.2-0", + "@fontsource-variable/dm-sans": "5.3.0", "@monaco-editor/react": "^4.7.0", "@tanstack/react-query": "^5.90.21", "@xyflow/react": "^12.10.1", diff --git a/apps/aevatar-console-web/pnpm-lock.yaml b/apps/aevatar-console-web/pnpm-lock.yaml index 53738768db..1ce67a82bf 100644 --- a/apps/aevatar-console-web/pnpm-lock.yaml +++ b/apps/aevatar-console-web/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@ant-design/pro-components': specifier: 3.1.2-0 version: 3.1.2-0(antd@6.3.3(date-fns@2.30.0)(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fontsource-variable/dm-sans': + specifier: 5.3.0 + version: 5.3.0 '@monaco-editor/react': specifier: ^4.7.0 version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -1841,6 +1844,9 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@fontsource-variable/dm-sans@5.3.0': + resolution: {integrity: sha512-BpUG5bqePiDFMBM4ZtLSlPdAIOM990uB1dlXzIf4aw21yQR7BmykedLXXXMqGWsR18aMLOsbWccwJNh3ztTQaA==} + '@formatjs/ecma402-abstract@1.11.4': resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==} @@ -13999,6 +14005,8 @@ snapshots: transitivePeerDependencies: - '@types/react' + '@fontsource-variable/dm-sans@5.3.0': {} + '@formatjs/ecma402-abstract@1.11.4': dependencies: '@formatjs/intl-localematcher': 0.2.25 diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 0d08a73a8c..0ce6a7123a 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -1,4 +1,6 @@ export default { + 'channels.error.contract': + 'Bot binding is not available yet. Please try again later.', 'channels.guide.title': 'How to connect a bot', 'channels.guide.add': 'Add bot in NyxID', 'channels.guide.addHelp': 'Create your bot in NyxID.', @@ -18,6 +20,8 @@ export default { 'channels.binding.unavailable': 'Unavailable', 'channels.status.disabled': 'Disabled', 'channels.skills.placeholder': 'Select a skill', + 'channels.skills.search': 'Search skills', + 'channels.skills.available': 'Available in Ornn', 'channels.skills.loading': 'Loading skills', 'channels.skills.failed': 'Could not load skills. Use Refresh to try again.', 'channels.skills.empty': 'No skills found', @@ -27,6 +31,8 @@ export default { 'Opens Ornn in a new tab. Create your skill, then return here and refresh the list.', 'channels.skills.refresh': 'Refresh skills', 'channels.bind.title': 'Bind bot', + 'channels.bind.description': + 'Choose a skill and the services this bot can use.', 'channels.bind.success': 'Bot bound successfully.', 'channels.bind.confirming': 'Confirming your changes...', 'channels.bind.delayed': @@ -76,8 +82,7 @@ export default { 'channels.edit.discardHelp': 'Your unsaved channel changes will be lost.', 'workflowActivityVNext.nav.channels': 'Channels', 'channels.title': 'Connect your channels to your agent', - 'channels.description': - 'Choose a channel to connect your bot and select the services it can use.', + 'channels.description': 'Bring your NyxID bots into Aevatar.', 'channels.intro': 'Once connected, talk to your Aevatar bot directly from the channel.', 'channels.available': 'Available channels', @@ -169,7 +174,7 @@ export default { 'channels.connect.hideToken': 'Hide bot token', 'channels.connect.botFather': 'Get token from BotFather', 'channels.connect.name': 'Channel name', - 'channels.connect.skillName': 'Skill name', + 'channels.connect.skillName': 'Skill', 'channels.connect.skillNameDefault': 'Defaults to the Telegram bot name', 'channels.connect.optional': '(optional)', 'channels.connect.botNameDefault': diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index 51bf26d4bf..58e1b5b809 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -1,4 +1,5 @@ export default { + 'channels.error.contract': 'Bot 绑定功能暂未开放,请稍后重试。', 'channels.guide.title': '如何连接 Bot', 'channels.guide.add': '前往 NyxID 添加 Bot', 'channels.guide.addHelp': '在 NyxID 中添加 Bot。', @@ -17,6 +18,8 @@ export default { 'channels.binding.unavailable': '不可用', 'channels.status.disabled': '已停用', 'channels.skills.placeholder': '选择 Skill', + 'channels.skills.search': '搜索 Skill', + 'channels.skills.available': 'Ornn 中可用的 Skill', 'channels.skills.loading': '正在加载 Skill', 'channels.skills.failed': '无法加载 Skill,请点击刷新重试。', 'channels.skills.empty': '未找到 Skill', @@ -26,6 +29,7 @@ export default { '将在新标签页打开 Ornn。创建 Skill 后,请返回此处刷新列表。', 'channels.skills.refresh': '刷新 Skill', 'channels.bind.title': '绑定 Bot', + 'channels.bind.description': '选择此 Bot 使用的 Skill 和可访问的服务。', 'channels.bind.success': 'Bot 绑定成功。', 'channels.bind.confirming': '正在确认更改…', 'channels.bind.delayed': @@ -67,7 +71,7 @@ export default { 'channels.edit.discardHelp': '未保存的渠道更改将会丢失。', 'workflowActivityVNext.nav.channels': '渠道', 'channels.title': '将渠道连接到你的 Agent', - 'channels.description': '选择渠道,连接你的机器人并选择它可以使用的服务。', + 'channels.description': '将你的 NyxID Bot 连接到 Aevatar。', 'channels.intro': '连接后,即可直接在渠道中与你的 Aevatar 机器人对话。', 'channels.available': '可用渠道', 'channels.availableNow': '可用', @@ -150,7 +154,7 @@ export default { 'channels.connect.hideToken': '隐藏机器人令牌', 'channels.connect.botFather': '从 BotFather 获取令牌', 'channels.connect.name': 'Channel 名字', - 'channels.connect.skillName': 'Skill 名称', + 'channels.connect.skillName': 'Skill', 'channels.connect.skillNameDefault': '默认使用 Telegram 机器人名字', 'channels.connect.optional': '(可选)', 'channels.connect.botNameDefault': '默认使用机器人名字 + 6 位随机数字', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx index 81ac9dc141..d9698edf07 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx @@ -30,6 +30,7 @@ import ChannelServicePicker from './ChannelServicePicker'; import ChannelSkillField from './ChannelSkillField'; import { channelConnectionCss } from './connectionStyles'; import { + ChannelBadge, ChannelIdentity, ChannelLoadError, InboundStatus, @@ -124,6 +125,14 @@ export default function ChannelConfigurationPage({

    {title}

    + {!editing ? ( +

    + {t( + 'channels.bind.description', + 'Choose a skill and the services this bot can use.', + )} +

    + ) : null}
    {initial ? ( - + {editing ? ( + + ) : ( + + {t('channels.binding.unbound', 'Not bound')} + + )}
    {editing ? (
    diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx index 9d49b89b6d..1088fe042e 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx @@ -78,7 +78,7 @@ function catalogue(input: RequestInfo | URL) { { guid: 'skill-guid', name: 'support', - description: '', + description: 'Help customers with support requests.', myAccessReason: 'shared-via-org', }, { guid: 'skill-guid-new', name: 'new-support', description: '' }, @@ -116,7 +116,7 @@ async function chooseSkill(name: string) { fireEvent.mouseDown(await screen.findByRole('combobox')); fireEvent.click( await screen.findByText(name, { - selector: '.ant-select-item-option-content', + selector: '.channels__skill-option strong', }), ); } @@ -135,6 +135,24 @@ it('binds an existing bot with shared Ornn skills, keeps selection on refresh an '/scopes/scope-alpha/workflow-activity-vnext/channels/bind/bot-alpha', ); renderWithQueryClient(); + fireEvent.mouseDown(await screen.findByRole('combobox')); + expect( + await screen.findByText('Help customers with support requests.'), + ).toBeInTheDocument(); + fireEvent.change(screen.getByRole('textbox', { name: 'Search skills' }), { + target: { value: 'support' }, + }); + await act(async () => { + await jest.advanceTimersByTimeAsync(250); + }); + await waitFor(() => + expect( + fetchMock.mock.calls.some(([url]) => String(url).includes('q=support')), + ).toBe(true), + ); + fireEvent.keyDown(screen.getByRole('textbox', { name: 'Search skills' }), { + key: 'Escape', + }); await chooseSkill('support'); expect( screen.queryByLabelText(/Bot token|Channel name|Label/), diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx index db888b707f..483a61ddae 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelSkillField.tsx @@ -2,9 +2,10 @@ import { ExportOutlined, PlusOutlined, ReloadOutlined, + SearchOutlined, } from '@ant-design/icons'; import { useInfiniteQuery } from '@tanstack/react-query'; -import { Button, Select } from 'antd'; +import { Button, Input, type InputRef, Select } from 'antd'; import * as React from 'react'; import { searchChannelSkills } from '@/shared/api/channelSkillsApi'; import { t } from '@/shared/i18n/messages'; @@ -29,6 +30,16 @@ export default function ChannelSkillField({ const id = React.useId(); const [search, setSearch] = React.useState(''); const [term, setTerm] = React.useState(''); + const [open, setOpen] = React.useState(false); + const selectRef = React.useRef>(null); + const searchRef = React.useRef(null); + React.useEffect(() => { + if (!open) return; + const frame = window.requestAnimationFrame(() => + searchRef.current?.focus(), + ); + return () => window.cancelAnimationFrame(frame); + }, [open]); React.useEffect(() => { const timer = window.setTimeout(() => setTerm(search.trim()), 250); return () => window.clearTimeout(timer); @@ -45,12 +56,12 @@ export default function ChannelSkillField({ enabled: Boolean(scopeId), }); // The registration contract accepts a name, not an Ornn GUID. - const names = [ - ...new Set( + const choices = [ + ...new Map( skills.data?.pages.flatMap((page) => - page.items.map((skill) => skill.name), + page.items.map((skill) => [skill.name, skill] as const), ) ?? [], - ), + ).values(), ]; const config = getOrnnRuntimeConfig(); const createHref = config.configurationError @@ -67,20 +78,40 @@ export default function ChannelSkillField({
    } + value={search} + aria-label={t('channels.skills.search', 'Search skills')} + placeholder={t('channels.skills.search', 'Search skills')} + onChange={(event) => setSearch(event.target.value)} + onKeyDown={(event) => { + if (event.key === 'ArrowDown' || event.key === 'Escape') { + event.preventDefault(); + selectRef.current?.focus(); + if (event.key === 'Escape') setOpen(false); + } + event.stopPropagation(); + }} + /> +

    {t('channels.skills.available', 'Available in Ornn')}

    +
    {menu} {skills.hasNextPage ? ( @@ -56,7 +58,7 @@ function ConnectionGuide() { ), }, { - icon: , + icon: , title: t('channels.guide.bind', 'Refresh and bind'), description: t( 'channels.guide.bindHelp', @@ -127,6 +129,10 @@ export default function ChannelsPage({ activeSection="channels" scopeId={scopeId} title={t('workflowActivityVNext.nav.channels', 'Channels')} + description={t( + 'channels.description', + 'Bring your NyxID bots into Aevatar.', + )} mainClassName="channels__main" contentClassName="channels__content" > @@ -147,6 +153,8 @@ export default function ChannelsPage({ - + {secondary === 'platform' ? ( + + {platformName(registration.platform)} + + ) : ( + + + + )}
  • ); @@ -294,15 +303,20 @@ export function ChannelLoadError({ return (

    - {unavailable + {error instanceof ChannelContractUnavailableError ? t( - 'channels.error.unavailable', - 'This channel is unavailable or you do not have access.', + 'channels.error.contract', + 'Bot binding is not available yet. Please try again later.', ) - : t( - 'channels.error.load', - 'Could not load channels. Please try again.', - )} + : unavailable + ? t( + 'channels.error.unavailable', + 'This channel is unavailable or you do not have access.', + ) + : t( + 'channels.error.load', + 'Could not load channels. Please try again.', + )}

    {menu} {skills.hasNextPage ? ( @@ -181,15 +198,6 @@ export default function ChannelSkillField({ )} /> - -
    {error || skills.isError ? (
    - - {workOrder.memberId} - - - {workOrder.publishedServiceId} - + + + {workOrder.memberId} + + + + + {workOrder.publishedServiceId} + +
    { `${namespace}/activity`, `${namespace}/activity/:runId`, `${namespace}/channels`, - `${namespace}/channels/connect/telegram`, + `${namespace}/channels/bind/:botId`, `${namespace}/channels/:registrationId`, + `${namespace}/channels/:registrationId/edit`, `${namespace}/settings`, ]; for (const path of expectedRoutes) { expect(findRoute(routes, path).hideInMenu).toBe(true); } + expect( + routes.some( + (route) => route.path === `${namespace}/channels/connect/telegram`, + ), + ).toBe(false); expect(findRoute(routes, namespace).redirect).toBe( `${namespace}/workflows`, From 92d9c6c487144b07d7734fb810bb00636f49f4db Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:52:23 +0800 Subject: [PATCH 165/167] Render channel empty values without HTML text entities --- .../workflow-activity-vnext/channels/ChannelsPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx index 410133c673..fda5af4af9 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelsPage.tsx @@ -220,19 +220,19 @@ export default function ChannelsPage({ {row.skill ? ( ) : ( - + {'\u2013'} )} {row.bindingStatus === 'unbound' ? ( - + {'\u2013'} ) : ( )} {row.bindingStatus === 'unbound' ? ( - + {'\u2013'} ) : ( ) : ( - + {'\u2013'} )} From fba2ce180c32be833c035aa45b11cbee5330708d Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Thu, 17 Sep 2026 14:14:32 +0800 Subject: [PATCH 166/167] Narrow service access review options before reading resources --- apps/aevatar-console-web/src/shared/auth/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/aevatar-console-web/src/shared/auth/client.ts b/apps/aevatar-console-web/src/shared/auth/client.ts index b2faacc452..3693e2618c 100644 --- a/apps/aevatar-console-web/src/shared/auth/client.ts +++ b/apps/aevatar-console-web/src/shared/auth/client.ts @@ -213,7 +213,7 @@ export class NyxIDAuthClient { if (prompt) { url.searchParams.set('prompt', prompt); } - if (flow === "serviceAccessReview") { + if (options.flow === 'serviceAccessReview') { for (const resource of options.resources ?? []) { if (resource.trim()) url.searchParams.append('resource', resource); } From ab00a814b1fc51c090f82458967d2306f3e4d12f Mon Sep 17 00:00:00 2001 From: AbigailDeng <108705114+AbigailDeng@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:32:28 +0800 Subject: [PATCH 167/167] fix(console): require channel Ornn and LLM services --- .../specs/2026-09-17-channel-bot-adoption.md | 9 + .../src/locales/channelMessages.en-US.ts | 7 +- .../src/locales/channelMessages.zh-CN.ts | 8 +- .../channels/ChannelConfigurationPage.tsx | 50 ++-- .../channels/ChannelEditPage.test.tsx | 225 ++++++++++++++++-- .../channels/ChannelServicePicker.tsx | 62 +++-- 6 files changed, 304 insertions(+), 57 deletions(-) diff --git a/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md b/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md index 66c3872a6a..fd7ad9f450 100644 --- a/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md +++ b/apps/aevatar-console-web/docs/superpowers/specs/2026-09-17-channel-bot-adoption.md @@ -17,6 +17,15 @@ Telegram token creation flow. - Bind uses the existing Skill and Services form controls. It contains no platform credentials or editable channel name. Edit retains its existing NyxID Label action and shares the Skill and Services controls. +- Bind and Edit preselect and lock services whose exact slugs are `ornn-api` + and `chrono-llm-public`. Individual and bulk deselection retain both; other + services remain optional. Resolve their real UserService IDs from the current + authorized, active NyxID inventory, never from labels or slug-as-ID fallbacks. + Missing required access blocks submission and offers retry after correcting + NyxID access. Save uses an explicit allowlist containing both required IDs and + the optional selections. Editing an older NyxID-default configuration explains + that saving replaces defaults with these selected services; the UI does not + infer which services were included in those defaults. - Skill creation opens https://ornn.chrono-ai.fun/skills/new/generate in a new tab. Its tooltip explains the return-and-refresh action. The refresh icon sits at the right of the popup's Available in Ornn heading. Refresh preserves diff --git a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts index 9290153b96..399fa53550 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.en-US.ts @@ -192,7 +192,12 @@ export default { 'channels.connect.servicesError': 'Could not load your services. Retry before connecting.', 'channels.connect.servicesEmpty': - 'No services are available with your current NyxID authorization. You can connect this bot without service access.', + 'No services are available with your current NyxID authorization.', + 'channels.connect.serviceRequired': 'Required', + 'channels.connect.requiredServicesMissing': + 'Required services unavailable: {services}. Check your NyxID access, then retry.', + 'channels.edit.replaceDefaults': + 'Saving replaces NyxID default access with the selected services.', 'channels.connect.searchServices': 'Search services by name or slug', 'channels.connect.serviceUnavailable': 'Unavailable', 'channels.connect.personal': 'Personal', diff --git a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts index d413631306..841214d0a9 100644 --- a/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts +++ b/apps/aevatar-console-web/src/locales/channelMessages.zh-CN.ts @@ -168,8 +168,12 @@ export default { 'channels.connect.servicesHelp': '仅显示当前 NyxID 授权范围内可用的服务。', 'channels.connect.servicesLoading': '正在加载服务', 'channels.connect.servicesError': '无法加载服务,请重试后再连接。', - 'channels.connect.servicesEmpty': - '当前 NyxID 授权范围内暂无可用服务。可以连接机器人,但不授予服务访问权限。', + 'channels.connect.servicesEmpty': '当前 NyxID 授权范围内暂无可用服务。', + 'channels.connect.serviceRequired': '必选', + 'channels.connect.requiredServicesMissing': + '必选服务不可用:{services}。请检查 NyxID 访问权限后重试。', + 'channels.edit.replaceDefaults': + '保存后将使用所选服务替代 NyxID 默认授权范围。', 'channels.connect.searchServices': '按名称或标识搜索服务', 'channels.connect.serviceUnavailable': '不可用', 'channels.connect.personal': '个人', diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx index 3e995cf780..99e30de056 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelConfigurationPage.tsx @@ -47,6 +47,8 @@ type Target = | { readonly botId: string; readonly registrationId?: never } | { readonly registrationId: string; readonly botId?: never }; +const requiredServiceSlugs = ['ornn-api', 'chrono-llm-public'] as const; + export default function ChannelConfigurationPage({ scopeId, ...target @@ -176,12 +178,9 @@ function ConfigurationForm({ }) { const baseline = channelConfiguration(initial); const [skillName, setSkillName] = React.useState(initial.skill?.name ?? ''); - const [serviceIds, setServiceIds] = React.useState( + const [chosenServiceIds, setServiceIds] = React.useState( baseline?.serviceIds ?? [], ); - const [authorizationMode, setAuthorizationMode] = React.useState< - ChannelConfiguration['authorizationMode'] - >(baseline?.authorizationMode ?? 'explicit_service_allowlist'); const [label, setLabel] = React.useState(initial.label ?? ''); const [savedLabel, setSavedLabel] = React.useState(initial.label ?? ''); const [receipt, setReceipt] = React.useState(null); @@ -198,6 +197,21 @@ function ConfigurationForm({ const mounted = React.useRef(true); const labelId = React.useId(); const services = useChannelServiceChoices(scopeId); + const requiredServices = (services.data ?? []).filter( + (service) => + service.active && + service.allowed && + requiredServiceSlugs.some((slug) => service.slug === slug), + ); + const requiredIds = requiredServices.map((service) => service.id); + const missingRequiredSlugs = requiredServiceSlugs.filter( + (slug) => !requiredServices.some((service) => service.slug === slug), + ); + const serviceIds = [...new Set([...chosenServiceIds, ...requiredIds])]; + const servicesReady = + !services.isPending && + !services.isError && + missingRequiredSlugs.length === 0; const toast = useConsoleToast(); const client = useQueryClient(); const listHref = buildWorkflowActivitySectionHref(scopeId, 'channels'); @@ -208,13 +222,13 @@ function ConfigurationForm({ const config: ChannelConfiguration = { skillName, serviceIds, - authorizationMode, + authorizationMode: 'explicit_service_allowlist', }; const configDirty = !editing || !channelConfigMatches(initial, config); const labelDirty = editing && label.trim() !== savedLabel; const dirty = editing ? configDirty || labelDirty - : Boolean(skillName || serviceIds.length); + : Boolean(skillName || chosenServiceIds.length); const busy = submitting || Boolean(receipt) || uncertain; const options = [ ...(services.data ?? []), @@ -319,7 +333,13 @@ function ConfigurationForm({ async function save(event: React.FormEvent) { event.preventDefault(); - if (lock.current || busy || completed.current || (editing && !dirty)) + if ( + lock.current || + busy || + completed.current || + !servicesReady || + (editing && !dirty) + ) return; setFailure(null); if (labelDirty && !isValidChannelBotLabel(label)) { @@ -463,17 +483,16 @@ function ConfigurationForm({ { - setServiceIds(ids); - setAuthorizationMode('explicit_service_allowlist'); - }} + requiredIds={requiredIds} + missingRequiredSlugs={missingRequiredSlugs} + onChange={setServiceIds} loading={services.isPending} failed={services.isError} refreshing={services.isFetching} disabled={busy} retry={() => void services.refetch()} editing={editing} - usesDefaults={authorizationMode === 'nyxid_default'} + replacesDefaults={baseline?.authorizationMode === 'nyxid_default'} /> {failure ? (

    @@ -515,12 +534,7 @@ function ConfigurationForm({ submitting || (Boolean(receipt) && !delayed && !observation.isError) } - disabled={ - busy || - (editing && !dirty) || - services.isPending || - services.isError - } + disabled={busy || (editing && !dirty) || !servicesReady} > {editing ? t('channels.edit.save', 'Save changes') diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx index e84f3d8276..c8dfcdd0a4 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelEditPage.test.tsx @@ -70,7 +70,35 @@ const receipt = { registration_id: 'reg-alpha', command_id: 'cmd-one', }; -function catalogue(input: RequestInfo | URL) { +const serviceCatalogue = [ + { + id: 'user-service-github', + slug: 'api-github', + label: 'GitHub', + is_active: true, + credential_source: { type: 'personal' }, + }, + { + id: 'user-service-ornn', + slug: 'ornn-api', + label: 'Ornn', + is_active: true, + credential_source: { type: 'personal' }, + }, + { + id: 'user-service-llm', + slug: 'chrono-llm-public', + label: 'Chrono LLM', + is_active: true, + credential_source: { type: 'personal' }, + }, +]; +const selectedServiceIds = [ + 'user-service-github', + 'user-service-llm', + 'user-service-ornn', +]; +function catalogue(input: RequestInfo | URL, services = serviceCatalogue) { if (String(input).includes('/skill-search')) return response({ data: { @@ -87,18 +115,7 @@ function catalogue(input: RequestInfo | URL) { }, error: null, }); - if (String(input).endsWith('/user-services')) - return response({ - services: [ - { - id: 'user-service-github', - slug: 'api-github', - label: 'GitHub', - is_active: true, - credential_source: { type: 'personal' }, - }, - ], - }); + if (String(input).endsWith('/user-services')) return response({ services }); return null; } const posts = () => @@ -108,7 +125,9 @@ beforeEach(() => { Object.values(mockToast).forEach((mock) => { mock.mockReset(); }); - persistAuthSession(createNyxIDServiceSession()); + persistAuthSession( + createNyxIDServiceSession({ allowed_service_ids: selectedServiceIds }), + ); jest.spyOn(history, 'push').mockImplementation(() => {}); jest.spyOn(history, 'replace').mockImplementation(() => {}); }); @@ -127,7 +146,12 @@ it('binds with shared Ornn skills, retains refresh selection and opens the regis let committed = false; fetchMock.mockImplementation(async (input, init) => { if (init?.method === 'POST') return response(receipt, 202); - return catalogue(input) ?? response([committed ? row : unbound]); + return ( + catalogue(input) ?? + response([ + committed ? { ...row, service_ids: selectedServiceIds } : unbound, + ]) + ); }); window.history.replaceState( {}, @@ -199,7 +223,7 @@ it('binds with shared Ornn skills, retains refresh selection and opens the regis nyx_channel_bot_id: 'bot-alpha', skill_name: 'support', authorization_mode: 'explicit_service_allowlist', - service_ids: ['user-service-github'], + service_ids: selectedServiceIds, }); expect(mockToast.success).not.toHaveBeenCalled(); expect(history.replace).not.toHaveBeenCalled(); @@ -243,6 +267,7 @@ it('preserves Label editing and requires a newer matching configuration before r ...row, label: 'reg-alpha', skill_name: committed ? 'new-support' : 'support', + service_ids: committed ? selectedServiceIds : row.service_ids, state_version: committed ? 13 : 12, }) ); @@ -264,7 +289,7 @@ it('preserves Label editing and requires a newer matching configuration before r expect(JSON.parse(String(posts()[0][1]?.body))).toEqual({ skill_name: 'new-support', authorization_mode: 'explicit_service_allowlist', - service_ids: ['user-service-github'], + service_ids: selectedServiceIds, }); expect(mockToast.success).not.toHaveBeenCalled(); committed = true; @@ -315,7 +340,7 @@ it('attributes an insecure callback rejection to the server and retains the bind nyx_channel_bot_id: 'bot-alpha', skill_name: 'support', authorization_mode: 'explicit_service_allowlist', - service_ids: ['user-service-github'], + service_ids: selectedServiceIds, }); expect(posts()).toHaveLength(1); expect(mockToast.success).not.toHaveBeenCalled(); @@ -351,3 +376,167 @@ it('does not repeat an adoption after an uncertain transport result', async () = jest.useRealTimers(); } }); + +it('locks required services through individual, bulk and filtered deselection and submits their real IDs', async () => { + jest.useFakeTimers(); + try { + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') return response(receipt, 202); + return catalogue(input) ?? response([unbound]); + }); + renderWithQueryClient( + , + ); + const ornn = await screen.findByRole('checkbox', { name: /ornn-api/ }); + const llm = screen.getByRole('checkbox', { name: /chrono-llm-public/ }); + for (const required of [ornn, llm]) { + expect(required).toBeChecked(); + expect(required).toBeDisabled(); + fireEvent.click(required); + expect(required).toBeChecked(); + } + expect(screen.getByText('2 selected')).toBeInTheDocument(); + const github = screen.getByRole('checkbox', { name: /GitHub/ }); + expect(github).toBeEnabled(); + expect(github).not.toBeChecked(); + const selectAll = screen.getByRole('checkbox', { name: 'Select all' }); + fireEvent.click(selectAll); + expect(github).toBeChecked(); + expect(screen.getByText('3 selected')).toBeInTheDocument(); + fireEvent.click(selectAll); + expect(github).not.toBeChecked(); + expect(ornn).toBeChecked(); + expect(llm).toBeChecked(); + const search = screen.getByRole('textbox', { + name: 'Search services by name or slug', + }); + fireEvent.change(search, { target: { value: 'ornn-api' } }); + expect( + screen.getByRole('checkbox', { name: 'Select all results' }), + ).toBeDisabled(); + fireEvent.change(search, { target: { value: 'GitHub' } }); + fireEvent.click( + screen.getByRole('checkbox', { name: 'Select all results' }), + ); + expect(screen.getByRole('checkbox', { name: /GitHub/ })).toBeChecked(); + fireEvent.click( + screen.getByRole('checkbox', { name: 'Select all results' }), + ); + fireEvent.change(search, { target: { value: '' } }); + expect(screen.getByText('2 selected')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Bind bot' })); + await screen.findByText('Confirming your changes...'); + expect(JSON.parse(String(posts()[0][1]?.body))).toEqual({ + nyx_channel_bot_id: 'bot-alpha', + skill_name: '', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-llm', 'user-service-ornn'], + }); + } finally { + cleanup(); + jest.useRealTimers(); + } +}); + +it.each([ + ['missing', 'ornn-api'], + ['inactive', 'chrono-llm-public'], + ['unauthorized', 'ornn-api'], + ['similar slug', 'chrono-llm-public'], +])('blocks submission with a %s required service and recovers on retry', async (condition, slug) => { + jest.useFakeTimers(); + try { + let restored = false; + const unavailableId = serviceCatalogue.find( + (service) => service.slug === slug, + )?.id; + if (condition === 'unauthorized') + persistAuthSession( + createNyxIDServiceSession({ + allowed_service_ids: selectedServiceIds.filter( + (id) => id !== unavailableId, + ), + }), + ); + const unavailableServices = serviceCatalogue + .filter((service) => condition !== 'missing' || service.slug !== slug) + .map((service) => + service.slug !== slug + ? service + : { + ...service, + is_active: condition !== 'inactive', + slug: condition === 'similar slug' ? `${slug}-other` : slug, + label: slug, + }, + ); + fetchMock.mockImplementation( + async (input) => + catalogue(input, restored ? serviceCatalogue : unavailableServices) ?? + response([unbound]), + ); + renderWithQueryClient( + , + ); + expect(await screen.findByRole('alert')).toHaveTextContent( + `Required services unavailable: ${slug}.`, + ); + expect(screen.getByRole('button', { name: 'Bind bot' })).toBeDisabled(); + fireEvent.submit(screen.getByRole('form', { name: 'Bind bot' })); + expect(posts()).toHaveLength(0); + restored = true; + persistAuthSession( + createNyxIDServiceSession({ allowed_service_ids: selectedServiceIds }), + ); + fireEvent.click(screen.getByRole('button', { name: 'Try again' })); + await waitFor(() => + expect(screen.queryByRole('alert')).not.toBeInTheDocument(), + ); + expect(screen.getByRole('button', { name: 'Bind bot' })).toBeEnabled(); + expect(screen.getByRole('checkbox', { name: /ornn-api/ })).toBeChecked(); + expect( + screen.getByRole('checkbox', { name: /chrono-llm-public/ }), + ).toBeChecked(); + } finally { + cleanup(); + jest.useRealTimers(); + } +}); + +it('explains replacing legacy NyxID defaults and saves the required explicit allowlist', async () => { + jest.useFakeTimers(); + try { + const defaults = { + ...row, + authorization_mode: 'nyxid_default', + service_ids: [], + }; + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'POST') return response(receipt, 202); + if (input === '/api/channels/registrations') return response([defaults]); + return catalogue(input) ?? response(defaults); + }); + renderWithQueryClient( + , + ); + await screen.findByRole('checkbox', { name: /ornn-api/ }); + expect( + screen.getByText( + 'Saving replaces NyxID default access with the selected services.', + ), + ).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: 'Save changes' })); + await screen.findByText('Confirming your changes...'); + expect(JSON.parse(String(posts()[0][1]?.body))).toEqual({ + skill_name: 'support', + authorization_mode: 'explicit_service_allowlist', + service_ids: ['user-service-llm', 'user-service-ornn'], + }); + } finally { + cleanup(); + jest.useRealTimers(); + } +}); diff --git a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx index 7123c0798c..bac5008604 100644 --- a/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx +++ b/apps/aevatar-console-web/src/pages/workflow-activity-vnext/channels/ChannelServicePicker.tsx @@ -8,6 +8,8 @@ import { AevatarContentSkeleton } from '@/shared/ui/AevatarContentSkeleton'; export default function ChannelServicePicker({ services, selectedIds, + requiredIds, + missingRequiredSlugs, onChange, loading, failed, @@ -15,10 +17,12 @@ export default function ChannelServicePicker({ disabled, retry, editing = false, - usesDefaults = false, + replacesDefaults = false, }: { readonly services: readonly ChannelServiceChoice[]; readonly selectedIds: readonly string[]; + readonly requiredIds: readonly string[]; + readonly missingRequiredSlugs: readonly string[]; readonly onChange: (ids: string[]) => void; readonly loading: boolean; readonly failed: boolean; @@ -26,7 +30,7 @@ export default function ChannelServicePicker({ readonly disabled: boolean; readonly retry: () => void; readonly editing?: boolean; - readonly usesDefaults?: boolean; + readonly replacesDefaults?: boolean; }) { const [search, setSearch] = React.useState(''); const term = search.trim().toLocaleLowerCase(); @@ -36,6 +40,7 @@ export default function ChannelServicePicker({ const selectableIds = visible .filter((service) => service.active && service.allowed) .map((service) => service.id); + const optionalIds = selectableIds.filter((id) => !requiredIds.includes(id)); const selectedCount = selectableIds.filter((id) => selectedIds.includes(id), ).length; @@ -51,11 +56,9 @@ export default function ChannelServicePicker({ {t('channels.connect.services', 'Services')} - {usesDefaults - ? t('channels.edit.defaults', 'NyxID defaults') - : t('channels.connect.selected', '{count} selected', { - count: selectedIds.length, - })} + {t('channels.connect.selected', '{count} selected', { + count: selectedIds.length, + })}