v2026.06.18#461
Merged
Merged
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
fix(log): restructure log directory retention
Persist and resolve TDP service origins without /config/api or /api/v1 segments. Add handler and secrets normalization with tests, fix TDP plugin test paths, and remove obsolete docs.
* feat(workspace): treat jsonl as text and stabilize Files tab loading Add .jsonl to workspace text extensions with API/UI coverage, and fix racey directory loads plus unstable toast deps that re-triggered list fetches. * fix(workspace): cap text preview size and disable edit when truncated Limit workspace/memory file reads via FLOCKS_WORKSPACE_MAX_READ_BYTES (default 2MB), return truncation metadata, and show a read-only preview banner in the WebUI.
* fix(session): recover orphaned running tools after server restart Mark persisted running tool parts as interrupted after unexpected server exits so session history no longer shows stale in-progress tools. Add startup, session-read, and session-loop recovery coverage with regression tests for idle and busy sessions. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): avoid duplicate orphan tool scans in message listing Reuse preloaded message parts during orphan tool recovery so GET /message no longer performs a redundant scan before returning results.
Extract shared graph layout for branch/loop routing, add edge property panel and join conflict settings, and refresh node/edge styling in the editor.
* feat(workflow): unify trigger integrations and simplify setup Make workflow.json the source of truth for trigger definitions and consolidate the workflow integration UI around API publishing plus four trigger entrypoints. This removes legacy trigger resurrection and streamlines the trigger configuration experience. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): route ingest triggers through unified dispatcher Wire Kafka and Syslog managers through EventDispatcher so filter, mapping, and trigger IDs match unified definitions. Add webhook HMAC verification, singleton trigger validation, public webhook auth bypass, and runtime adapter reload when custom trigger config changes. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: move contributing guide to root and ignore docs/ Add docs/ to gitignore, stop tracking local documentation, and point README links at CONTRIBUTING.md. Simplify workflow trigger editor UI by removing preview/test mapping panels and unused adapter controls.
- Enable MULTI_GROUP_ENABLED by default in backend (env var to opt out) - Refactor DeviceIntegration page with left sidebar for room navigation - GroupSidebar with inline create / rename / delete for rooms - "All Rooms" aggregate view with collapsible per-room sections - Collapse-all / expand-all toggle when multiple rooms are present - Fix sidebar height: route /devices as fullscreen to bypass min-h-full wrapper that breaks flex height propagation chain - Room assignment in DeviceConfigPanel: dropdown (editable) when viewing "All Rooms", read-only field when viewing a specific room or editing - Add i18n (zh-CN / en-US) for all hardcoded strings in device page; register "device" namespace in i18n.ts - Align page header icon with other pages (ServerCog w-8 h-8) - Update index.test.tsx mocks to cover createGroup / deleteGroup / listDeviceTools / updateDeviceTool Co-authored-by: Cursor <cursoragent@cursor.com>
feat(device): add multi-room support and i18n for device integration
When opening a tool's test panel from within a specific device's configuration panel, the device UUID (generated at onboarding time) is now pre-filled as `device_id` in the test parameters JSON. This resolves the error "当前存在多台同类型设备,调用前必须显式传入 `device_id`" that appeared whenever multiple instances of the same device type were registered, because the registry's `_resolve_device_target` could not auto-select a target without an explicit `device_id`. Changes: - ToolDetailModal: add optional `deviceId` prop; inject it as the first key in the template produced by `buildParamsTemplate`, skipping any duplicate `device_id` param entry from the tool's parameter list only when the prop is already provided (avoids regression for tools whose YAML explicitly declares a required `device_id` parameter) - ToolDetailModal: show a contextual hint below the params label when `deviceId` is pre-filled, so the user knows where the value came from - DeviceConfigPanel: pass `device?.id` as `deviceId` when opening ToolDetailModal from the per-device tools tab - buildParamsTemplate: fix number/boolean default values to emit actual `0` / `false` instead of the string literals `"0"` / `"false"` Co-authored-by: Cursor <cursoragent@cursor.com>
…evice-id feat(device): auto-inject device_id into tool test params
Clicking a fixture called setTestParams with the fixture's raw params, silently overwriting the pre-filled device_id injected by the deviceId prop. Merge device_id as the first key so it is never lost regardless of which fixture the user selects. Co-authored-by: Cursor <cursoragent@cursor.com>
setToolModal(tool) passed the raw tool object whose enabled field
reflects the initial load, ignoring any per-device toggle the user
applied in the same session. Replace with { ...tool, enabled: isOn }
so the modal always receives the current effective enabled state.
Co-authored-by: Cursor <cursoragent@cursor.com>
…l reveal (#349) * feat(webui): add custom device access wizard (API/WebCLI/Syslog) Extend Device Integration with custom device onboarding for API, WebCLI, and syslog modes, including types, panel UI, and tests. Document a third web2cli capture path when CLI requirements are already specified. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(webui,web2cli): align WebCLI custom device with device plugin flow Require skill integration first, then optional device plugin packaging for security devices. Add cli-in-device reference, tighten session prompts and provider description guidance, and update custom device access UI/tests. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(device,web2cli): reveal persisted secrets and streamline CLI docs Add an explicit credentials endpoint so the device UI can show full masked secrets on demand. Update web2cli to document cookie/auth-state defaults and replace the spec-generation step with cli-requirements. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(web2cli,webui): document optional auth recovery credentials Allow optional username/password for browser-based cookie recovery after auth-state expires, and align custom device session prompts with the updated cli-in-device guidance. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(device,webui): scope credential reveal and add audit trail Switch credentials reveal to POST with per-field requests, emit device.credentials_reveal audit events without logging secrets, and localize the custom device access UI.
…E_ID handlers _config_override_service was set to the bare service_id (e.g. "sangfor_af") derived by storage_key_to_service_id(), but several device handlers declare SERVICE_ID as the full versioned storage key (e.g. "sangfor_af_v8_0_48"). get_config_override() performed an exact match, so these handlers always got None and silently fell back to the global default config — using the plugin's hardcoded DEFAULT_BASE_URL (192.168.1.1) instead of the device's configured IP (e.g. 10.201.255.17). Fix: - _build_overrides() now returns a 4-tuple including storage_key - activate_device_credentials() stores storage_key in a new ContextVar _config_override_storage_key alongside the existing service_id var - get_config_override() accepts a match on either bare service_id OR full storage_key, so all handler SERVICE_ID conventions work correctly Affected handlers (non-exhaustive): sangfor_af_v8_0_48, sangfor_af_v8_0_85, sangfor_af_v8_0_106 Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(session): add write file links and concise workflow tool output Append clickable local-file Markdown links to write tool results in session output, omit verbose workflow execution history from default run_workflow text while keeping it in metadata, and strip reasoning block whitespace on both ends. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(session): drop runner-side write tool file link formatting Remove clickable Markdown link injection from session runner write output; workflow concise output and reasoning strip changes remain unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): show live stage in WebUI and forward session cancel Expose workflow_name and total_nodes in run_workflow metadata, forward the session abort flag to the workflow runtime, and render a compact running-stage summary in the session tool header. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): propagate abort to tools and guard late metadata updates Forward the session abort event into StreamProcessor tool execution, mark metadata callbacks finished in a finally block, ignore stale running updates after completion, and persist interrupted tool state on cancel. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): cancel inflight metadata tasks and localize workflow header Cancel pending running-metadata publish/persist tasks when a tool completes, and use i18n labels for run_workflow stage summaries in the WebUI header. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): make llm.ask cancellable and show cancelling UI state Propagate workflow cancel checks through LLM nodes, lazy llm helpers, and provider calls, then surface a cancelling phase in workflow detail run/history views with localized status messaging. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(workflow): add cancel_checker to sandbox python runtime Declare cancel_checker on SandboxPythonExecRuntime and update sandbox tests to match the keyword-aware get_lazy_llm factory.
B1 (ToolDetailModal): { device_id: deviceId, ...fx.params } had the spread
order backwards — a fixture that already carries a device_id key would
overwrite the prop-injected value, exactly undoing the fix. Swap to
{ ...fx.params, device_id: deviceId } so the parent-supplied deviceId
always takes precedence regardless of fixture contents.
S1 (credential_context): 'and' binds tighter than 'or', so the guard
if secret_ovr is None and config_ovr is None or service_id is None
was already evaluated correctly, but was easy to misread as
if secret_ovr is None and (config_ovr is None or service_id is None).
Add explicit parentheses to match the intended semantics at a glance.
Co-authored-by: Cursor <cursoragent@cursor.com>
S2 — Add unit tests for get_config_override dual-key matching New test file tests/tool/test_credential_context_config_override.py covers 7 scenarios without touching the DB or ToolRegistry: · bare service_id match (existing behaviour) · versioned storage_key match (new behaviour, regression target) · unrelated service_id → None · no active override → None · storage_key=None does not match empty string (falsy trap) · service_id=None does not match empty string (falsy trap) · identical service_id and storage_key (no version suffix) S3 — Improve readability of the dual-key match in get_config_override Replace 'service_id in (expected_service, expected_storage)' with explicit named booleans (matches_service / matches_storage) and an expanded docstring that explains the two naming conventions handlers use, so future readers do not mistake either branch as redundant. S4 — _build_overrides return type → _DeviceOverrides NamedTuple Positional 4-tuples are fragile: a caller adding or reordering fields silently shifts every unpack site. Introducing _DeviceOverrides makes field access self-documenting (.service_id, .storage_key, …) and lets type-checkers catch missing fields at import time. Co-authored-by: Cursor <cursoragent@cursor.com>
- Add autouse fixture _reset_context_vars that clears the three ContextVars before and after every test; without it a test that sets a var leaks state into the next test in the same thread - Remove bare 'import pytest' that was unused before the fixture was added (would have triggered a lint warning) Co-authored-by: Cursor <cursoragent@cursor.com>
…-drops-device-id fix/device tool test fixture drops device
…ingtalk, telegram Apply PR #190 (wecom inbound + outbound file attachments) and extend the same pattern to dingtalk and telegram. Refactor the inbound dispatcher into a per-channel hook registry so new channels no longer need to modify dispatcher.py. Inbound (download to local FilePart): - wecom: AES-256-CBC decrypt via wecom_aibot_sdk, 30MB cap, nested mixed-message aeskey, Content-Disposition filename extraction - dingtalk: exchange download_code via OAPI /v1.0/robot/messageFiles/download, 20MB cap, separate exchange/download error classification - telegram: resolve telegram://<kind>/<file_id> via getFile + https://api.telegram.org/file/bot<token>/<file_path> download Outbound (send_media per channel): - wecom: SDK upload_media → send_media_message / reply_media; companion text sent as a follow-up markdown message - dingtalk: OAPI multipart upload → msgKey=file (downloadCode+fileName) for any type; msgKey=image (photoURL) for remote image URLs - telegram: route to sendPhoto / sendDocument / sendVideo / sendAudio / sendVoice / sendAnimation based on inferred kind; agent can force document via telegram:document:<url> prefix Dispatcher: - register_inbound_media_downloader() + _DOWNLOADERS table - dynamic per-channel lookup so test monkeypatches on the channel's inbound_media module still apply - SSE message.part.updated events for both FilePart and the rewritten text part; placeholder text replaced with 'Attached files: <path>' Tests: - wecom: +14 (send_media, inbound_media, content-disposition, mixed file) - dingtalk: +9 (download_code exchange, oversized guard, send_media routing, text-after-file, image URL inline) - telegram: +15 (file_id resolution, kind inference for image/pdf/gif/ogg, endpoint routing, kind override prefix, error path) - dispatcher: +9 (per-channel routing, placeholder detection, end-to-end per-channel pipeline) - test_e2e_file_roundtrip.py: 7 new tests covering real PNG byte round-trips for all five channels with in-process fake servers All 354 channel tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documents the bidirectional file/image contract for all built-in channels, the dispatcher refactor, the per-channel downloader hook pattern, and the channel-specific outbound quirks reviewers need to know before approving changes to the media path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…escription) The channel file/image review notes are better kept in the PR description itself (where reviewers see them first) than in a root-level doc that the gitignore policy excludes from the docs/ folder. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ge points, impact scope, and review focus Rewrite the "Pull Request Guidelines" section in CONTRIBUTING.md so the required PR description structure is explicit: - Key Changes (改动点) — concrete deltas grouped by area. - Impact Scope (影响范围) — user-visible behavior, compatibility, configuration, dependencies, performance, security. - Business Logic to Focus On During Review (需重点 Review 的业务逻辑) — the parts of the change that deserve extra reviewer attention. The previous section listed five reviewer-facing questions but did not constrain the order or depth of the description, which led to PRs that mentioned the impact but omitted the logic that needed a careful read. Also update the PR description template to match the new structure and add a Why-This-Approach section plus an explicit Compatibility, Migration & Rollback section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR #380 added flex flex-col to the shared content wrapper for the /devices page height chain, which unintentionally changed home and other standard page layout. Keep /devices on the fullscreen route and restore the v2026.6.4 wrapper for all other pages.
Add admin-managed user-defined pages with backend build/watch/runtime support and a WebUI host for published pages.
Add backend config-store authentication guidance to workflow chat and publish guide prompts so Rex uses the server_api_token bearer flow against the backend API. Cover the workflow detail and integration prompt paths with assertions for server_api_token and Authorization headers.
Fix device plugin loading and config defaults
Keep API service driver switching available after publish and guard async operations against stale UI state. Avoid duplicate dynamic ports by reserving persisted and in-flight service ports, and release reservations on failed local or Docker publishes. Render stale API service records as stopped without persisting stoppedAt so autostart can still recover them.
fix/workbench session collapse
fix(workflow): prevent workbench interaction timeouts
Pin LiteLLM to 1.83.7 to include the fix for the MCP stdio command injection vulnerability in the test connection and tools list endpoints.
fix(deps): upgrade litellm for CVE-2026-42271
fix(workflow): stabilize docker publish startup
…op-timeout fix: prevent DingTalk stream from blocking event loop
Fix/resolve dev main conflicts
fix(cli): constrain typer below incompatible release
…-jsx Fix workflow integration tab JSX closure
Remove bundled Hub catalog entries that reference missing Anthropic skill manifests. Fix the IntegrationTab test mock object so eslint can parse the suite.
fix(ci): restore FlocksHub validation
* perf: optimize session page loading * fix: avoid restoring last session on initial load * fix: gate last session restore by entrypoint * fix: preserve optimistic sessions during list refresh * fix: address session pagination review issues
* fix(log): restructure log directory retention Co-authored-by: Cursor <cursoragent@cursor.com> * fix(log): address retention review feedback Co-authored-by: Cursor <cursoragent@cursor.com> * fix(updater): avoid Windows project install during dependency sync (#373) Skip installing the current project during Windows self-upgrade dependency sync to avoid transient hatchling file traversal failures, and keep flockshub resources out of wheel build metadata. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tdp): normalize base_url by stripping UI/API path suffixes (#376) Persist and resolve TDP service origins without /config/api or /api/v1 segments. Add handler and secrets normalization with tests, fix TDP plugin test paths, and remove obsolete docs. * feat(workspace): jsonl text preview and Files tab load stability (#367) * feat(workspace): treat jsonl as text and stabilize Files tab loading Add .jsonl to workspace text extensions with API/UI coverage, and fix racey directory loads plus unstable toast deps that re-triggered list fetches. * fix(workspace): cap text preview size and disable edit when truncated Limit workspace/memory file reads via FLOCKS_WORKSPACE_MAX_READ_BYTES (default 2MB), return truncation metadata, and show a read-only preview banner in the WebUI. * fix(session): recover orphaned running tools after server restart (#370) * fix(session): recover orphaned running tools after server restart Mark persisted running tool parts as interrupted after unexpected server exits so session history no longer shows stale in-progress tools. Add startup, session-read, and session-loop recovery coverage with regression tests for idle and busy sessions. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): avoid duplicate orphan tool scans in message listing Reuse preloaded message parts during orphan tool recovery so GET /message no longer performs a redundant scan before returning results. * feat(webui): improve workflow graph layout and editor edge UX (#379) Extract shared graph layout for branch/loop routing, add edge property panel and join conflict settings, and refresh node/edge styling in the editor. * Feat/workflow trigger integration (#375) * feat(workflow): unify trigger integrations and simplify setup Make workflow.json the source of truth for trigger definitions and consolidate the workflow integration UI around API publishing plus four trigger entrypoints. This removes legacy trigger resurrection and streamlines the trigger configuration experience. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): route ingest triggers through unified dispatcher Wire Kafka and Syslog managers through EventDispatcher so filter, mapping, and trigger IDs match unified definitions. Add webhook HMAC verification, singleton trigger validation, public webhook auth bypass, and runtime adapter reload when custom trigger config changes. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: move contributing guide to root and ignore docs/ Add docs/ to gitignore, stop tracking local documentation, and point README links at CONTRIBUTING.md. Simplify workflow trigger editor UI by removing preview/test mapping panels and unused adapter controls. * feat(device): add multi-room support and i18n for device integration - Enable MULTI_GROUP_ENABLED by default in backend (env var to opt out) - Refactor DeviceIntegration page with left sidebar for room navigation - GroupSidebar with inline create / rename / delete for rooms - "All Rooms" aggregate view with collapsible per-room sections - Collapse-all / expand-all toggle when multiple rooms are present - Fix sidebar height: route /devices as fullscreen to bypass min-h-full wrapper that breaks flex height propagation chain - Room assignment in DeviceConfigPanel: dropdown (editable) when viewing "All Rooms", read-only field when viewing a specific room or editing - Add i18n (zh-CN / en-US) for all hardcoded strings in device page; register "device" namespace in i18n.ts - Align page header icon with other pages (ServerCog w-8 h-8) - Update index.test.tsx mocks to cover createGroup / deleteGroup / listDeviceTools / updateDeviceTool Co-authored-by: Cursor <cursoragent@cursor.com> * feat(device): auto-inject device_id into tool test params When opening a tool's test panel from within a specific device's configuration panel, the device UUID (generated at onboarding time) is now pre-filled as `device_id` in the test parameters JSON. This resolves the error "当前存在多台同类型设备,调用前必须显式传入 `device_id`" that appeared whenever multiple instances of the same device type were registered, because the registry's `_resolve_device_target` could not auto-select a target without an explicit `device_id`. Changes: - ToolDetailModal: add optional `deviceId` prop; inject it as the first key in the template produced by `buildParamsTemplate`, skipping any duplicate `device_id` param entry from the tool's parameter list only when the prop is already provided (avoids regression for tools whose YAML explicitly declares a required `device_id` parameter) - ToolDetailModal: show a contextual hint below the params label when `deviceId` is pre-filled, so the user knows where the value came from - DeviceConfigPanel: pass `device?.id` as `deviceId` when opening ToolDetailModal from the per-device tools tab - buildParamsTemplate: fix number/boolean default values to emit actual `0` / `false` instead of the string literals `"0"` / `"false"` Co-authored-by: Cursor <cursoragent@cursor.com> * fix(device): preserve device_id when applying a test fixture Clicking a fixture called setTestParams with the fixture's raw params, silently overwriting the pre-filled device_id injected by the deviceId prop. Merge device_id as the first key so it is never lost regardless of which fixture the user selects. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(device): sync toolEnabled state into ToolDetailModal on open setToolModal(tool) passed the raw tool object whose enabled field reflects the initial load, ignoring any per-device toggle the user applied in the same session. Replace with { ...tool, enabled: isOn } so the modal always receives the current effective enabled state. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(webui,device,web2cli): custom device access wizard and credential reveal (#349) * feat(webui): add custom device access wizard (API/WebCLI/Syslog) Extend Device Integration with custom device onboarding for API, WebCLI, and syslog modes, including types, panel UI, and tests. Document a third web2cli capture path when CLI requirements are already specified. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(webui,web2cli): align WebCLI custom device with device plugin flow Require skill integration first, then optional device plugin packaging for security devices. Add cli-in-device reference, tighten session prompts and provider description guidance, and update custom device access UI/tests. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(device,web2cli): reveal persisted secrets and streamline CLI docs Add an explicit credentials endpoint so the device UI can show full masked secrets on demand. Update web2cli to document cookie/auth-state defaults and replace the spec-generation step with cli-requirements. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(web2cli,webui): document optional auth recovery credentials Allow optional username/password for browser-based cookie recovery after auth-state expires, and align custom device session prompts with the updated cli-in-device guidance. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(device,webui): scope credential reveal and add audit trail Switch credentials reveal to POST with per-field requests, emit device.credentials_reveal audit events without logging secrets, and localize the custom device access UI. * fix(credential-context): resolve config override for versioned SERVICE_ID handlers _config_override_service was set to the bare service_id (e.g. "sangfor_af") derived by storage_key_to_service_id(), but several device handlers declare SERVICE_ID as the full versioned storage key (e.g. "sangfor_af_v8_0_48"). get_config_override() performed an exact match, so these handlers always got None and silently fell back to the global default config — using the plugin's hardcoded DEFAULT_BASE_URL (192.168.1.1) instead of the device's configured IP (e.g. 10.201.255.17). Fix: - _build_overrides() now returns a 4-tuple including storage_key - activate_device_credentials() stores storage_key in a new ContextVar _config_override_storage_key alongside the existing service_id var - get_config_override() accepts a match on either bare service_id OR full storage_key, so all handler SERVICE_ID conventions work correctly Affected handlers (non-exhaustive): sangfor_af_v8_0_48, sangfor_af_v8_0_85, sangfor_af_v8_0_106 Co-authored-by: Cursor <cursoragent@cursor.com> * feat(session): add concise workflow tool output (#381) * feat(session): add write file links and concise workflow tool output Append clickable local-file Markdown links to write tool results in session output, omit verbose workflow execution history from default run_workflow text while keeping it in metadata, and strip reasoning block whitespace on both ends. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(session): drop runner-side write tool file link formatting Remove clickable Markdown link injection from session runner write output; workflow concise output and reasoning strip changes remain unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): show live stage in WebUI and forward session cancel Expose workflow_name and total_nodes in run_workflow metadata, forward the session abort flag to the workflow runtime, and render a compact running-stage summary in the session tool header. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): propagate abort to tools and guard late metadata updates Forward the session abort event into StreamProcessor tool execution, mark metadata callbacks finished in a finally block, ignore stale running updates after completion, and persist interrupted tool state on cancel. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(session): cancel inflight metadata tasks and localize workflow header Cancel pending running-metadata publish/persist tasks when a tool completes, and use i18n labels for run_workflow stage summaries in the WebUI header. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(workflow): make llm.ask cancellable and show cancelling UI state Propagate workflow cancel checks through LLM nodes, lazy llm helpers, and provider calls, then surface a cancelling phase in workflow detail run/history views with localized status messaging. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(workflow): add cancel_checker to sandbox python runtime Declare cancel_checker on SandboxPythonExecRuntime and update sandbox tests to match the keyword-aware get_lazy_llm factory. * fix: correct merge order and operator precedence from review B1 (ToolDetailModal): { device_id: deviceId, ...fx.params } had the spread order backwards — a fixture that already carries a device_id key would overwrite the prop-injected value, exactly undoing the fix. Swap to { ...fx.params, device_id: deviceId } so the parent-supplied deviceId always takes precedence regardless of fixture contents. S1 (credential_context): 'and' binds tighter than 'or', so the guard if secret_ovr is None and config_ovr is None or service_id is None was already evaluated correctly, but was easy to misread as if secret_ovr is None and (config_ovr is None or service_id is None). Add explicit parentheses to match the intended semantics at a glance. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(credential-context): address S2/S3/S4 from PR review S2 — Add unit tests for get_config_override dual-key matching New test file tests/tool/test_credential_context_config_override.py covers 7 scenarios without touching the DB or ToolRegistry: · bare service_id match (existing behaviour) · versioned storage_key match (new behaviour, regression target) · unrelated service_id → None · no active override → None · storage_key=None does not match empty string (falsy trap) · service_id=None does not match empty string (falsy trap) · identical service_id and storage_key (no version suffix) S3 — Improve readability of the dual-key match in get_config_override Replace 'service_id in (expected_service, expected_storage)' with explicit named booleans (matches_service / matches_storage) and an expanded docstring that explains the two naming conventions handlers use, so future readers do not mistake either branch as redundant. S4 — _build_overrides return type → _DeviceOverrides NamedTuple Positional 4-tuples are fragile: a caller adding or reordering fields silently shifts every unpack site. Introducing _DeviceOverrides makes field access self-documenting (.service_id, .storage_key, …) and lets type-checkers catch missing fields at import time. Co-authored-by: Cursor <cursoragent@cursor.com> * test: fix ContextVar isolation and remove unused import - Add autouse fixture _reset_context_vars that clears the three ContextVars before and after every test; without it a test that sets a var leaks state into the next test in the same thread - Remove bare 'import pytest' that was unused before the fixture was added (would have triggered a lint warning) Co-authored-by: Cursor <cursoragent@cursor.com> * feat(channel): complete bidirectional file/image support for wecom, dingtalk, telegram Apply PR #190 (wecom inbound + outbound file attachments) and extend the same pattern to dingtalk and telegram. Refactor the inbound dispatcher into a per-channel hook registry so new channels no longer need to modify dispatcher.py. Inbound (download to local FilePart): - wecom: AES-256-CBC decrypt via wecom_aibot_sdk, 30MB cap, nested mixed-message aeskey, Content-Disposition filename extraction - dingtalk: exchange download_code via OAPI /v1.0/robot/messageFiles/download, 20MB cap, separate exchange/download error classification - telegram: resolve telegram://<kind>/<file_id> via getFile + https://api.telegram.org/file/bot<token>/<file_path> download Outbound (send_media per channel): - wecom: SDK upload_media → send_media_message / reply_media; companion text sent as a follow-up markdown message - dingtalk: OAPI multipart upload → msgKey=file (downloadCode+fileName) for any type; msgKey=image (photoURL) for remote image URLs - telegram: route to sendPhoto / sendDocument / sendVideo / sendAudio / sendVoice / sendAnimation based on inferred kind; agent can force document via telegram:document:<url> prefix Dispatcher: - register_inbound_media_downloader() + _DOWNLOADERS table - dynamic per-channel lookup so test monkeypatches on the channel's inbound_media module still apply - SSE message.part.updated events for both FilePart and the rewritten text part; placeholder text replaced with 'Attached files: <path>' Tests: - wecom: +14 (send_media, inbound_media, content-disposition, mixed file) - dingtalk: +9 (download_code exchange, oversized guard, send_media routing, text-after-file, image URL inline) - telegram: +15 (file_id resolution, kind inference for image/pdf/gif/ogg, endpoint routing, kind override prefix, error path) - dispatcher: +9 (per-channel routing, placeholder detection, end-to-end per-channel pipeline) - test_e2e_file_roundtrip.py: 7 new tests covering real PNG byte round-trips for all five channels with in-process fake servers All 354 channel tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(channel): add review guide for file/image attachment support Documents the bidirectional file/image contract for all built-in channels, the dispatcher refactor, the per-channel downloader hook pattern, and the channel-specific outbound quirks reviewers need to know before approving changes to the media path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(channel): remove standalone review guide (move content into PR description) The channel file/image review notes are better kept in the PR description itself (where reviewers see them first) than in a root-level doc that the gitignore policy excludes from the docs/ folder. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(contributing): restructure PR description guidelines around change points, impact scope, and review focus Rewrite the "Pull Request Guidelines" section in CONTRIBUTING.md so the required PR description structure is explicit: - Key Changes (改动点) — concrete deltas grouped by area. - Impact Scope (影响范围) — user-visible behavior, compatibility, configuration, dependencies, performance, security. - Business Logic to Focus On During Review (需重点 Review 的业务逻辑) — the parts of the change that deserve extra reviewer attention. The previous section listed five reviewer-facing questions but did not constrain the order or depth of the description, which led to PRs that mentioned the impact but omitted the logic that needed a careful read. Also update the PR description template to match the new structure and add a Why-This-Approach section plus an explicit Compatibility, Migration & Rollback section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * build(deps): bump starlette to >=1.0.1 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(webui): remove global flex-col wrapper from standard pages (#384) PR #380 added flex flex-col to the shared content wrapper for the /devices page height chain, which unintentionally changed home and other standard page layout. Keep /devices on the fullscreen route and restore the v2026.6.4 wrapper for all other pages. * feat(user-defined-pages): add custom page runtime (#389) Add admin-managed user-defined pages with backend build/watch/runtime support and a WebUI host for published pages. * Fix Telegram file roundtrip media upload * Fix channel media filename and caption handling * fix(provider): thinking params (#387) * fix(provider): send enable_thinking for catalog-declared interleaved models Trace ses_1628dfe6cffe1i5xZY9lv1u20m showed GLM-5 on alibaba returning finishReason=stop with empty content + zero tool calls on every turn, so the agent loop handed the turn back to the user after every step. Root cause: the request-side dispatch in options.py was a hard-coded substring whitelist (qwen3 / kimi / mimo / qwq / qwen-max). GLM-5, minimax-m2.* / m3, deepseek-reasoner and step-3.5-flash all declare interleaved in the catalog but their model names match none of those substrings, so enable_thinking: true was never sent. Without the flag, DashScope / Moonshot / Zhipu / MiniMax / Stepfun / DeepSeek stream their thinking into content instead of reasoning_content and stop mid-tool-call. Replace the token-list dispatch with _THINKING_REQUEST_SHAPES, a provider-id-keyed dict of callables gated on the catalog interleaved capability. The catalog becomes the single source of truth for "this model wants thinking-aware streaming"; adding a new provider is one line. Also fix openai_compatible.py chat() and chat_stream(): both were silently dropping caller-supplied extra_body whenever kwargs.thinking was None. This made the user-configured openai-compatible provider ignore default_parameters.enable_thinking in flocks.json. Now mirrors openai_base.py:905-913. Adds tests/provider/test_thinking_params.py with 44 tests: - Property test: every catalog entry with interleaved != null resolves to a thinking flag (regression net for this class of bug) - Specific GLM-5 step-50 trace replay across alibaba / threatbook-cn / threatbook-io / zhipu - All previously-dropped models (minimax-m2.* / m3 × 3 providers, deepseek-reasoner, step-3.5-flash) - Shape-registry structural checks (no legacy token constant, every reasoning provider has a shape entry) - openai_compatible extra_body propagation smoke test Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(provider): drop _deepseek_thinking_shape V3 model-name branching Follow-up to 2299b33. The catalog is the single source of truth for "this model wants thinking-aware streaming" — the V3 string checks (re)introduced the exact fragility the catalog-driven dispatch was meant to eliminate. Catalog already says the right thing: ``deepseek-chat`` (family=deepseek-v3) has no ``interleaved`` capability, so ``interleaved_enabled`` is False and the shape function is never called for it. A future catalog change that adds ``interleaved`` to V3 would have been silently stripped by the V3 branch; deleting the branch makes the catalog gate honest. Add TestShapeRegistry.test_deepseek_v3_is_not_a_thinking_model which pins both directions: - Sanity: every catalog entry with family starting ``deepseek-v3`` has no ``interleaved`` capability. If someone changes that, the test fails and forces a re-evaluation. - Runtime: with ``_resolve_interleaved_capability`` returning None, the dispatcher emits no ``enable_thinking`` flag for ``deepseek-chat``. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(provider): drop shape registry, use transport-driven dispatch Follow-up to 699f717. The provider-keyed shape registry introduced in 2299b33 was over-engineered: every entry produced the same dict ({"enable_thinking": bool(reasoning_enabled)}), and the gate was already correctly computed upstream by ``resolve_interleaved_capability`` in ``interleaved.py`` (catalog explicit declaration → series-token inference fallback). Replacing it with a transport-based branch: reasoning_transport == anthropic_messages → thinking={type: "enabled", budget_tokens:...} (unchanged) reasoning_transport == generic_chat → extra_body.enable_thinking = bool(reasoning_enabled) This unblocks the natural design goal: read the model series globally, no per-provider dispatch. Concrete consequences: - deepseek-v4-flash and similar models that the catalog forgets to declare now get enable_thinking automatically via the series-token inference in ``infer_interleaved_capability`` (qwen3 / glm-* / kimi-k2* / deepseek-v4* / step-3.5* / minimax-m* tokens). - A user-configured openai-compatible endpoint pointing at a known family Just Works without anyone editing a per-provider registry. - Adding a new provider or a new model from a known family is now zero-touch: no catalog edit, no shape entry, no dispatch change. Removed: - _THINKING_REQUEST_SHAPES dict (9 entries, all identical product) - _openai_base_thinking_shape helper function - TestShapeRegistry.test_all_thinking_providers_have_a_shape (replaced with transport-based and shape-removal assertions) - Property test skip-on-missing-shape (no longer needed; transport dispatch is universal for interleaved models) Added: - TestDispatchShape.test_no_shape_registry — pins the removal - TestDispatchShape.test_anthropic_transport_still_uses_thinking_field — pins the contract that the new generic_chat branch did not regress the anthropic_messages path - TestDispatchShape.test_series_token_fallback_emits_enable_thinking — 5 parametrized cases proving series-token inference closes the "forgot to add to catalog" gap (qwen3, glm-5, kimi-k2.6, minimax, step-3.5-flash) Tests: 51/51 in test_thinking_params.py pass; full tests/provider/ shows the same 7 pre-existing failures as baseline, none introduced by this refactor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(provider): expand deepseek thinking coverage to full series User-driven follow-up to 3335cc7. Aligns the deepseek handling with the "全局读模型系列" design intent: any model in the deepseek family should be treated as thinking-capable unless explicitly overridden. Changes: catalog.json (deepseek provider): - Add ``deepseek-v4-flash`` with explicit ``interleaved`` declaration. Previously only present in the threatbook-cn-llm / threatbook-io-llm providers. - Add ``deepseek-v4-pro`` (new model entry) with explicit ``interleaved``. interleaved.py (_STRICT_REASONING_CONTENT_TOKENS): - Add ``deepseek`` (broad catch-all) and ``deepseek-v3`` (specific) to cover the entire deepseek series. Previously only V4-subset and R1 tokens were listed, so a model named ``deepseek-chat`` (V3) or any future V5 / V6 would not be auto-inferred. - Result: every deepseek model now auto-resolves to the strict reasoning_content policy via series-token inference, including ``deepseek-chat`` (V3). Test impact: - test_thinking_params.py: invert ``test_deepseek_v3_is_not_a_thinking_model`` → ``test_deepseek_v3_is_a_thinking_model`` to assert the new behavior using the real resolution chain (no monkeypatch). - test_chinese_providers.py::test_deepseek_catalog: extend the model set assertion to include deepseek-v4-flash / deepseek-v4-pro; add per-model interleaved assertions for the new entries. - test_provider.py::test_resolve_model_does_not_infer_interleaved_for_non_reasoning_model: switch the test fixture from ``deepseek-chat`` to ``gpt-4-turbo`` because ``deepseek-chat`` is no longer a non-reasoning model under the new design. ``gpt-4-turbo`` doesn't match any series token and remains a true non-reasoning fixture. Net result of this + the previous three commits: - Original bug (2299b33): GLM-5 / minimax / deepseek / stepfun dropped from request-side enable_thinking because of a hard-coded substring whitelist — fixed. - 699f717: removed the model-name V3 carve-out in _deepseek_thinking_shape so catalog becomes the only gate. - 3335cc7: replaced provider-keyed shape registry with transport-driven dispatch, hooking up the series-token inference in interleaved.py as a fallback for any uncatalogued model. - This commit: extend the deepseek series coverage to "all of it" (V3 / R1 / V4 and forward) and make V4-flash / V4-pro explicit in the deepseek provider catalog. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(provider,session): per-model thinking extra_body and queued user cache Route generic-chat reasoning to provider-specific wire formats (DeepSeek/GLM/Kimi/MiMo thinking blocks, MiniMax reasoning_split). Apply queued-user reminders inside _to_chat_messages with chat context cache invalidation, and persist aborted assistant messages when LLM streams are interrupted. * feat(workflow): improve diagram usability Add a Flocks Help entry point to AI editing and replace the default canvas controls with lightweight controls that include tooltips. Document PowerShell encoding requirements for agents. * chore(gitignore): ignore .codex/ directory Local Codex CLI cache / config directory; not meant to be tracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Fix fallback port conflict detection (#394) * Improve chat model picker controls (#391) * refactor: unify subagent delegation under delegate_task (#385) * refactor: unify subagent delegation under delegate_task Consolidate task scheduling into delegate_task (task becomes a compat alias), remove plan mode and standalone background tools, and run independent foreground subagents in parallel. Update WebUI cards, skill installer, and agent configs. Co-authored-by: Cursor <cursoragent@cursor.com> * fix/skill remove * fix/skill install skill.sh * fix(webui): render parallel delegate tasks as separate cards Remove buildParallelDelegateGroupParts which merged multiple sibling delegate_task tool parts into a single 'Parallel Agents' card. Each parallel subagent now renders as its own DelegateTaskCard with its real subagent_type name, matching the user-facing expectation of 'one tool card per agent'. * refactor(delegate_task): remove legacy tasks=[...] batch shape The unified delegate_task tool only needs the single-subagent shape now that parallel work is expressed as multiple sibling tool calls in one assistant turn. Drop the batch path from delegate_task, the task compat alias, the stream-processor branch that aggregated it into one card, and the matching batch compat tests; replace the latter with a small tolerance test for the slimmer schema and adjust model-pinning tests accordingly. Also remove the now-redundant fallback to the load_skills-aware description in the webui DelegateTaskCard since the card already renders the explicit description when provided. * Fix legacy todo permission migration * feat(workflow): support config-driven publish templates * feat(workflow): add editable workflow document flow * feat(session): refine chat selector controls * Fix channel image preview rendering * Fix DingTalk inbound file detection * feat(session): refine agent and model selectors * feat(session): add context usage indicator * fix(session): stabilize selector button widths * fix(session): localize selector control widths * fix(session): cap adaptive agent selector width * fix(session): stabilize streaming status (#396) * Reduce workflow default log noise (#399) * fix: clarify browser doctor and session streaming status (#400) * feat(workflow): add publish config templates Add storage-backed workflow publish config templates with config.json migration, template workflow assets, and the hidden workflow-config-guide skill. Refine workflow detail publish/chat UX with process folding, question follow-up inputs, and UI-hidden workflow/skill filtering. Clean workflow runtime state on deletion and cover the new publish config, visibility, skill, and chat behaviors with tests. * revert(workflow): restore pre-pr398 workflow experience Revert PR #398 changes that touched workflow pages and workflow-local config support. This removes the workflow detail UI tuning, edit markdown/config APIs, generated config templates, and matching test/mock updates while keeping later dev commits intact. * feat(device): unify device plugin intake (#392) * feat(device): unify device plugin intake * refactor(device): streamline custom intake chat * refine device intake layout and webcli guidance * feat(workflows): improve workflow configuration UX Use workflow.md as the editable workflow source, add inline markdown diff review, improve chat process folding and flow canvas controls, and harden workflow publish configuration guidance. * fix(channel): preserve plugin instances across load_all (#402) * Fix session model persistence (#403) * feat(device): Improve device integration auto-sync (#405) * feat: improve device integration auto-sync * fix: harden device refresh auto-provisioning * fix: default model reasoning to enabled (#406) * fix skill install from GitHub blob URLs (#407) * chore/update-version-2026-6-10 (#408) * feat(workflows): improve workbench publishing guidance Add workflow-local guide support, refine publish guidance UI, reuse current workflow chat sessions for launch requests, and share prompt selector controls across session surfaces. Also persist display labels through prompt queue/session APIs so shortcut prompts render as compact instruction tags. * Fix device refresh sync flow (#410) * feat(workflow): refine builder workbench experience * docs: reorganize web2cli reference guides (#411) Co-authored-by: xiami762 <> * feat(tool): Add l IM send message tool (#404) * Add high-level IM send message tool * Add high-level IM send message tool * fix: channel question * fix: honor selected IM session binding * fix: bound SSH connection pool (#415) * feat(device): add 360 FW v5.5 integration * feat(session): refine todo tool rendering * Fix updater cancellation and session statistics (#414) * Support structured ACP command arguments (#412) * fix(sip): preserve plugin result output * Fix Windows updater venv rotation (#413) * Fix Windows updater venv rotation * Improve restart readiness diagnostics * Fix skill install timeouts and session streaming state (#418) * Fix skill install timeouts and tool native defaults * Fix session streaming state during tool calls * Fix unavailable model error display * Handle unavailable model connection errors * Tune OpenAI-compatible provider timeouts * Fix session chat auto-scroll with sticky composer * Address PR review feedback * fix(updater): preserve restart after upgrade disconnect * fix: handle windows image paths and default workflow history * feat(workflow): improve workbench and publishing flows * fix(workflow): show guide info tooltip above panels * fix(updater): restore restart argv reconstruction * chore(updater): remove unused service restart argv helper * Add Windows updater restart handoff * fix(updater): tune restart handoff timeouts * fix(session): correct context usage after compaction * fix(session): handle multimodal image paths across platforms * fix(provider): enable vision for ThreatBook MiniMax M3 * fix(workflow): preserve cron schedules and refine guides Keep cron expressions when applying schedule templates to poller runtime config. Make workflow chat guide actions compact by default, grouped in the expanded panel, and dismissible on outside click. * fix(updater): defer dependency sync to restart handoff * chore(skills): remove unused cybersecurity skills * chore(skills): remove supply chain malware analysis skill * feat(workflow): improve workflow authoring usability * fix(provider): support configured OpenAI-compatible extra_body (#424) * Resolve custom model limits automatically (#421) * fix(session): attribute context usage breakdown * fix(session): exclude delegated tools from usage calls * fix(session): include system prompt usage segment * fix(session): count tool schemas as definitions * fix(session): show zero agent delegation usage * fix(session): split reasoning from conversation usage * fix(session): refine context usage and compaction divider * fix(session): constrain compaction divider width * fix(session): show compacted history in timeline * fix(session): reset context usage after compaction * fix(session): stabilize context usage popover * fix(session): compact only new turns after summary * fix(session): refresh context usage on compaction failure * fix(session): keep context usage during compaction * fix(session): reduce context usage refresh overhead * Add WebUI dark mode support (#430) * Add WebUI dark mode support * feat: darm mode * fix(session): improve dark send button contrast * fix(workflow): collapse invisible chat process markers * fix(workflow): stabilize compact chat bubble width * fix(session): expand grouped process steps by default * fix(session): enforce disabled agent availability (#433) * feat(workflow): improve publish runtime configuration Add runtime API service deletion support and expose frontend workflow endpoint metadata to assistant prompts. Refine publish and trigger cards so configuration, enable/disable, delete, and Flocks assisted setup share the same runtime-card model. * feat: add persistent goal mode (#431) * feat: add persistent goal command * feat: goal mode * Use model-based goal judging and normalize question options * Harden goal judging and wait on judge failures * Support goal clarification and custom question answers * Fix goal clear and verdict handling * feat(webui): add guided Rex creation for capabilities * feat(workflow): refine overview run experience Embed test and history flows inside the overview run section with compact stats. Rename workflow info to info, show workflow file locations, and keep history details inline. Load user-defined page bundles via relative API URLs without download headers. * feat(webui): refine guided capability workbench * fix(webui): handle guided workbench edge cases * fix(webui): load user page bundles with credentials * fix: enforce workflow API keys and restore agent tests Validate published workflow runtime invokes with x-api-key and pass the generated key through local/docker runtime startup and center proxy calls. Restore the Agent edit sheet test hook so the Test tab runs agentAPI.test instead of only offering Rex guide prompts. * Reduce workflow progress storage contention (#435) * Reduce workflow progress storage contention * Preserve workflow step logs during summary writes * Reduce workflow log noise and add debug output logging * Default workflow history retention off * Persist trigger workflow steps without retaining history * fix(user-defined-pages): handle client disconnects in page api * fix: preserve device draft config during tests (#437) * fix(workflow): improve API service recovery * fix(workflow): prevent workbench interaction timeouts * fix: improve device plugin loading and config defaults * fix(webui): collapse entity workbench process details * fix: load package entry points for tool plugins * fix: align device tool switch semantics * fix(webui): align entity Rex workbench with workflow Validate stored Rex sessions before resuming the entity workbench, matching the workflow detail chat behavior for stale sessions. Keep empty conversations on the welcome guide only, move extract-from-Rex into guide actions, and normalize guide button sizing/layout across entity workbenches. * fix(webui): document workflow config API auth Add backend config-store authentication guidance to workflow chat and publish guide prompts so Rex uses the server_api_token bearer flow against the backend API. Cover the workflow detail and integration prompt paths with assertions for server_api_token and Authorization headers. * fix(workflow): stabilize API service publishing Keep API service driver switching available after publish and guard async operations against stale UI state. Avoid duplicate dynamic ports by reserving persisted and in-flight service ports, and release reservations on failed local or Docker publishes. Render stale API service records as stopped without persisting stoppedAt so autostart can still recover them. * fix(deps): upgrade litellm for CVE-2026-42271 Pin LiteLLM to 1.83.7 to include the fix for the MCP stdio command injection vulnerability in the test connection and tools list endpoints. * fix(workflow): stabilize docker publish startup * fix: prevent dingtalk stream from blocking event loop * chore/update-version-2026-6-17 (#446) * fix(cli): constrain typer below incompatible release * Fix workflow integration tab JSX closure * Remove duplicate WebUI declarations * fix(ci): restore FlocksHub validation Remove bundled Hub catalog entries that reference missing Anthropic skill manifests. Fix the IntegrationTab test mock object so eslint can parse the suite.
Merge main into dev and resolve conflicts
duguwanglong
approved these changes
Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.