Skip to content

feat(flow-chat): add quote-select — copy or quote selected message text into composer - #2321

Closed
buddha68 wants to merge 3263 commits into
GCWing:mainfrom
buddha68:feature/quote-select
Closed

buddha68 wants to merge 3263 commits into
GCWing:mainfrom
buddha68:feature/quote-select

Conversation

@buddha68

Copy link
Copy Markdown

Summary

Adds a "select-to-quote" workflow to the chat composer, mirroring the interaction
popularized by qoderwork and dsh's @deepseek-ai/dsh-quote-select:

  1. Select text inside a user or assistant message → a floating menu appears with
    Copy text and Add to conversation.
  2. Add to conversation writes a provenance marker
    【引用自对话第 N 条消息(你的原话/AI 的回答)】「原文」 into the composer
    (quote first, existing draft below) and renders a removable quote chip above
    the input.
  3. Chips stay in sync with the draft: removing the marker text removes the chip;
    sending clears both; clicking ✕ removes the marker from the draft.

Design

  • New component src/web-ui/src/flow_chat/components/QuoteSelect/:
    • QuoteSelect.tsx — selection detection + floating menu (portal to
      getAppearanceOverlayHost()), copy & add handlers.
    • quoteSelectPick.ts — pure selection extraction (separate file to satisfy
      react-refresh/only-export-components).
    • quoteSelectStore.ts — zustand store; composer read/write via the existing
      public event-bus contracts.
    • QuoteDock.tsx — removable quote chips rendered through the new
      composerDock extension point.
    • QuoteSelect.scss, QuoteSelect.test.tsx (12 tests).
  • Minimal host changes (~15 lines total):
    • ChatInput.tsx: new optional render-prop prop composerDock?: (draft: string) => React.ReactNode,
      rendered above the composer inside bitfun-chat-input__container.
    • ChatPane.tsx: mounts <QuoteSelect /> and passes <QuoteDock draft={...} />
      to ChatInput.
  • Reuses existing contracts (no DOM hacks):
    • Message anchors: data-bf-component="user-message-item" (+ data-turn-id) and
      data-bf-component="model-round-item".
    • Composer write: globalEventBus.emit('fill-chat-input', { content, mode }).
    • Composer read: globalEventBus.emit('chat-input:get-state', { getValue }).
  • New dev dependencies: @testing-library/react, @testing-library/dom (test-only).

Verification

  • vitest run QuoteSelect → 12/12 passed.
  • eslint on changed files → 0 errors, 0 warnings.
  • Existing ChatInput* test suites → 83/83 passed (no regressions).
  • vite dev-server compile check on all touched modules → 200 OK.

Notes / follow-ups

  • v1 message ordinal is a DOM-based approximation (visible rows). A follow-up
    could compute global ordinals via absoluteSessionTurnIndexForId and add
    data-turn-id to ModelRoundItem for consistency.
  • Tool-output-only quotes and multi-message quotes are out of scope for v1.

bobleer and others added 30 commits August 7, 2026 12:51
The SSH CLI installer launched its driver over a PTY exec channel. The
driver only spawns a nohup body and exits, which it does about a
millisecond later, and sshd tears the PTY down the moment it does. That
teardown races the body: a body still inside bash's startup has not
reached its own exit trap yet, so losing the race kills it silently.

Nothing survives to explain it. The body writes no log and no exit file,
the driver's cleanup trap removes the `.preparing` marker, and the next
poll reaps the now-stale `.pid`. The controller reads an empty state,
maps it to Failed on its very first poll, and the user gets "could not
fully deploy BitFun" for an install that had already downloaded and
verified the release.

Launch over a plain exec channel instead. Without a controlling terminal
there is no hangup to race, and the installer needs no TTY semantics
anyway since it never uses sudo.

Two diagnostics gaps kept this invisible and are fixed alongside it:

- WebKit, which Tauri embeds on macOS, builds `Error.stack` from frames
  only. The logger preferred `stack` over the message, so the warning
  reached the log file as a bare source location with no reason.
- The install poll's failure discarded the installer's own output, so
  even a populated remote log never reached the error.

Verified against a real Ubuntu aarch64 target. Launching a detached
process from a PTY channel is killed before it writes a line; over a
plain channel it survives and the install completes (running=1 on the
first poll, then marker=1 exit_code=0).
Starting a second Dispatch session on a workspace failed provisioning:

  dispatch __workspace_provision failed (exit 1): Error: dispatch
  workspace provisioning failed: dispatch worktree exists without the
  requested base commit

The target names a job's checkout `<project label>-<short job id>`, and
took the short id by filtering the job id to alphanumerics and slicing
the first eight. Job ids are minted as `dispatch-<uuid>`, so the slice
never reached the uuid: every job of a project produced the same eight
characters — `dispatch` — and therefore the same directory.

The second session landed on the first session's checkout. Provisioning
inspects an occupied directory before it fetches anything, so the second
job's base commit was usually not in the shared clone yet and it bailed
with the message above; when the commit was already cached it bailed on
the branch check instead. Either way one workspace could only ever run
one dispatch at a time, and the error named neither the directory nor
the job it collided with.

Digest the job id instead of slicing it. A digest depends on the whole
id, so no shared prefix, suffix, or length can collapse two jobs onto one
directory. Jobs that already have a checkout keep it: the provision
record pins the path it was first given, so this is inert on upgrade.

The three worktree-rejection errors now name the directory they judged
and the commit or branch they wanted, because an occupied checkout is
exactly the case where "which directory, and whose?" is the question.

Verified by reverting the fix: both new tests fail, the end-to-end one
with the same refusal users hit.
A dispatch target already prefers pulling `base_commit` from the project's
Git remote and only asks the controller to ship a bundle when that fails.
Three things made the preferred path fragile enough that a large project
looked like it had hung.

The fetch was unbounded. `git fetch` ran with no deadline and no stall
detection, so a dead transport parked the whole dispatch on it with the
UI showing one static line. Observed on a target: fifteen minutes inside
a single fetch, and the only reason it ended was that someone killed it.

Git aborts a stalled HTTP transfer itself given `http.lowSpeedLimit` and
`http.lowSpeedTime`, which is the check that actually wants to be tight —
it separates "slow but arriving" from "hung", which a total-time budget
cannot. A 25-minute backstop covers non-HTTP transports and sits under
the controller's own 30-minute ceiling so the target reports the failure.
The backstop is deliberately generous: a first fetch of a large project
legitimately runs for many minutes, and killing one that is still making
progress falls back to shipping the same history over SSH instead —
strictly slower than the fetch it replaced.

A killed fetch stranded its download. Git writes an incoming pack under a
temporary name and only renames it once indexing completes, so an
interrupted fetch leaves the whole thing as `tmp_pack_*`. Nothing ever
collects those: `git gc` ignores them and they appear in no object count.
The same target was holding 207 MiB of them across two dead attempts, and
every retry added another copy. They are now swept before each attempt
and after a failure, and the fetch runs in its own process group so a
timeout stops the transport helper and `index-pack` too, rather than
leaving them downloading into a cache nobody is waiting for.

The fetch asked for every branch. `+refs/heads/*:refs/remotes/origin/*`
pulls all 192 branches of this project when a dispatch only ever checks
out one commit. It now asks for that commit, falling back to the old
refspec on servers that refuse a bare object id. Measured against the
real remote this is a smaller win than it sounds — the branches share
almost all of their history — so it is a saving, not the fix.

Fetched commits are anchored under `refs/dispatch/bases/`. Asking for a
bare object id writes no ref, and the job branch that would hold it goes
away with the job, which would leave the cache holding a whole history
with nothing pointing at it: invisible to `have_tips`, so the next job
bundles everything again, and eligible for `gc` to discard. This also
repairs the state that motivated the change — a cache found with 139 MB
of objects and zero refs, bundling in full every time.

Finally, the target now reports why it fell back. On a cold cache that
fallback re-sends the project's entire history, and "the remote refused
us" is the difference between a slow dispatch and a broken target; it was
previously visible only in the target's own log.

This makes the fetch path bounded, self-cleaning and diagnosable. It does
not make a first dispatch of a large repository fast — that target pulls
from GitHub at about 860 KB/s, so the wait is bandwidth, and what it
really needs next is for the transfer to be visible while it happens.
fix(web-ui): restore MCP deletion and voice input
AppRoot had grown into a single runtime object that owned routing, remote
transport, conversation state and presentation at once, so every feature
change reached across all of them. Split it along explicit boundaries:

- pages/runtime for the composition root and lifecycle
- pages/viewmodel for controllers and view models
- pages/policy for pure decision helpers
- pages/actions for the typed intent/action surface handed to components
- pages/navigation and pages/layout for route and geometry contracts

Components now receive typed action objects instead of reaching into view
models, which lets Local and Remote share one conversation shell
(ConversationRouteSurface on compact, WideConversationHost on wide).

Behaviour changes that came out of the split:

- Creating a chat from the "chat" option binds the desktop's assistant
  workspace first. The desktop ignores workspace_path for Claw sessions and
  always uses its assistant workspace, so the app used to keep showing the
  code workspace it was on while the session was actually created
  elsewhere - the new chat never appeared in the list.
- Picking a workspace in the create sheet now pairs it with the code agent,
  so the picker is honoured instead of being silently dropped.
- Compact remote conversations open the sidebar over the chat from a menu
  button, matching local chats, instead of popping back out of the
  conversation. The system back gesture still leaves the chat and reveals
  the drawer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The MVVM split only holds if the import direction is enforced. Add
`pnpm run harmony:architecture`, which fails when services import pages,
when components import view models, when the page graph gains a cycle, or
when action and hook interfaces are passed as anything but typed object
literals.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eset editor

Replace hand-written provider/model inputs in the reasoning preset editor with
cascading dropdowns backed by the merged provider catalog. Selecting a provider
filters models to those that support reasoning, and an empty selection is valid
for models that are not yet adapted.

Add tests covering provider aggregation, reasoning-capable model filtering,
cascading filter, unknown-provider handling, and value echo.
- use the full models.dev snapshot for reasoning provider/model bindings
- expose catalog source, revision, cache path, timestamp, and model counts
- add manual refresh, cache directory reveal, and offline update guidance
- refresh the UI when the catalog changes
- add Rust, remote transport, and frontend coverage
将外部 AI 应用设置收敛为默认页少量控制:应用行改为可展开树,仅保留总开关、已启用能力图标与单一待确认入口;打开应用开关即应用推荐设置,关闭撤下运行能力,custom 模式重新启用时保留原有覆盖;全局关闭时应用开关保持失效。移除独立 Hooks 一级入口,旧 hooks 深链归一到外部应用设置并复用同一面板;清理 connect 对话框相关死代码与失效 i18n 键。行为由应用模型与设置页测试冻结并逐项审查。
The permission mode behind the chat input control was stored as two global
config knobs (policy preset + auto-approve preference), so switching it in
one conversation moved every other open session. Users who keep a read-only
exploration session next to an editing session had no way to hold different
modes at once.

Make the mode a first-class value resolved per submission as
`turn -> session -> project -> global default`:

- Add `PermissionMode` (ask / auto_approve / full_access) to product-domains,
  with the preset and auto-approve knobs derived from that single value so a
  round can no longer run with a preset from one mode and an approval
  behavior from another. The project layer is reserved, not yet written.
- Persist the session's own selection on `SessionConfig.permission_mode`.
  `None` keeps following the user-level default, so existing sessions behave
  exactly as before and still track later changes to that default.
- Resolve the mode once per submission in the coordinator and read it from a
  single context key downstream. The legacy `auto_approve_ask` metadata key
  is still honored for CLI, dispatch, and persisted submissions.
- Demote the settings-page value to the default for sessions that have not
  chosen their own mode, and surface the session override in the chat input
  with a scope label, an indicator, and a reset action.
- Add an opt-in "next message only" mode that rides on the submission and is
  never persisted.

Also fixes two subagent inheritance gaps found while wiring this up: the
Task tool forwarded only boolean invocation facts, and the external subagent
delegation path read submission metadata without resolving the session
layer. Both dropped a delegated child back to the global default. The parent
runtime ceiling still bounds the child, and a `full_access` mode remains
bounded by project, agent, enforced, and constraint layers.
Make review decisions direct and authoritative across GUI and TUI, clarify disclosure and empty states, and avoid unnecessary snapshot projection work.
chore(i18n): unify copy wording in zh-CN and zh-TW locale
Remove the parallel application connection and batch review surface, keeping the existing source policy and owner-specific permission controls as the single path.

Reduce Web and TUI output, preserve remote and owner guards, and consume retired automatic defaults once without overwriting later user choices.
fix(agent): include goal lifecycle tools in Cowork
wgqqqqq and others added 26 commits August 14, 2026 22:31
A client that exits before answering `initialize` produced one sentence —
"exited before initialization completed" — which tells the user only what
they already know. Its stderr held the whole explanation and went to a
log file, or, for a local agent, to a terminal a packaged app does not
have.

Read both transports' stderr into the log line by line, keep the tail,
and quote it in the error the user is shown. Waiting for EOF first is
what makes the quote complete: the pipes closing is the signal that the
agent is gone, and its last lines are still in flight then.

Also ask the remote host for its Node version in the probe round trip we
already make, and refuse the launch when it is below 20.12 — the release
that added `util.parseEnv`, which the harness imports on its first line.
dsh declares no `engines`, so npm installs it onto Node 18 without a word
and the failure surfaces from deep inside the launcher.
Allow permission changes made during an active turn to affect the
next model round while keeping the current round stable.

- Keep active-turn overrides mutable and process-local
- Clear temporary overrides when the owning turn ends
- Persist session-scoped selections and clear active overrides
- Add a dedicated active-turn permission command
- Distinguish next-message and current-turn scopes in the UI
- Preserve compatibility with older session permission requests
DeepSeek Harness's PTC preset answers a whole step with one `run_code`
call whose argument is a TypeScript program. The bridge classified it by
kind alone, so it landed on the terminal card, which reads `command` —
a field the call does not have — and drew an empty card.

Give it its own identity and card: `run_code` (and anything shaped like
it: Execute kind, a `code` argument, no `command`) becomes `RunCode`,
rendered as the program plus what it printed. A shell call that happens
to carry a `code` argument is still Bash.

While reading the result path: ACP results carry their text in content
blocks, but the terminal card reads `output`/`stdout`, so every ACP Bash
card showed the command with nothing underneath it. Lift that text into
`output` for both cards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An ACP session came back empty after a restart. Its turns were never
written, and three layers each had a reason.

The projection is the only writer of these turns, but it had no storage
slot to write into. That slot arrives with the backend's
`DialogTurnStarted`, and no such event exists for a turn an external
agent runs — nothing in the local runtime starts it. Every save was
therefore deferred, forever. Allocate the slot in the projection instead,
from the turns and catalog it already holds, and decline to guess when
the session has persisted turns none of which are projected yet, where a
guess would overwrite history.

The backend then refused the save it did receive. `save_persisted_dialog_turn`
validates a turn against the runtime's history branch for that session,
and an externally driven session has none — the runtime neither starts
nor completes those turns — so a first turn failed with OutcomeUnknown.
Read the session's `provider` metadata and, when an external agent owns
it, persist straight through. A runtime-owned session still needs its
branch, which the new test pins from both sides.

Finally, the desktop path loaded every session into the session manager
before saving, which for an ACP session restored nothing and rewrote its
persisted mode to a local fallback. Skip the load for the same reason:
there is no runtime state to restore.

The `provider` key and its `acp` value move into core-types, so the ACP
client that writes them and the runtime that reads them back share one
definition rather than two string literals that have to agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closing a dsh session and clicking it again gave a blank one: the bridge
never advertised ACP's `loadSession`, so BitFun had only `session/new` to
fall back on. Every one of a user's stored sessions carries
`acpResumeStrategy: "new"` for that reason. The reopened conversation
lost its history and its context, and its mode picker unlocked and
reverted to the roster default — a session that has already spoken must
not be able to change the composition its transcript was written under.

Implement `session/load`. A live session replays from memory; a cold one
resumes out of the harness's own persistence, which is the case that
matters, since a client restart is exactly when nothing is in memory. The
archive is read through `inspect`, not a listing: a session disposed a
moment ago is still draining, and `list` does not wait for it while
`inspect` does — reopening the session you just closed is the first thing
a user does.

The mode comes back from the session's own log rather than the roster, so
a conversation started under a preset reopens under it however the
default has moved, and `presetOptions` locks the picker for a session
whose conversation has started. A session is refused when it was never
stored, or when it belongs to another directory — answering the latter
would hand back a session whose sandbox boundary points somewhere else.

`scripts/smoke.mjs --load <id>` drives the path against a real
installation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(acp): ship an ACP bridge for DeepSeek Harness
The 0.2.18 Desktop Package run failed in `Package (windows-x64)` with
`spawnSync npm ENOENT` out of `build-profile.mjs`, taking the whole
`frontend:build-all` down with it. Three separate Windows assumptions:

- `npm` is a `.cmd` shim there, and Node has refused to spawn one without
  a shell since CVE-2024-27980. A shell then re-splits every argument, so
  passing an absolute `--pack-destination` would break on its first space.
  Both `npm pack` and `tar` now run *in* the staging directory, which
  leaves their arguments as bare package names and one filename — no
  quoting to get wrong, and no drive letter reaching `tar -f`, which GNU
  tar would read as a remote host.
- `copyTree`'s filter derived a basename by slicing on '/', which on a
  '\'-separated path yields the whole path and therefore matched nothing.
  A vendored tree would have dragged `node_modules` along.
- `hashTree` recorded native separators, so the same sources produced a
  different content stamp per build host. Digests are unchanged on Unix
  (verified byte-for-byte against the previous script).

`prepare:dsh-profile` runs only inside `frontend:build-all`, which no CI
job invokes, so its first Windows execution ever was a release build.
Add a small `windows-latest` job that runs the packaging and asserts the
profile is complete, stamped, and carries nothing it must not ship.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…endor

fix(dsh): package the bridge profile on Windows
Preserve YAML and TOML frontmatter through Markdown and TipTap
round trips without exposing structural delimiters for editing.

- Render labeled frontmatter metadata in preview mode
- Support protected frontmatter editing in IR mode
- Preserve delimiters, line endings, comments, and body spacing
- Show unsafe syntax warnings only when IR requires fallback
- Add focused parser, preview, and editor regression tests
Add the private AgentClient, Session, and Query vertical slice over the existing Agent Runtime owner. Consolidate bounded JSON, JSON-RPC, and WebSocket mechanics in the cross-platform transport foundation while keeping IPC framing and product protocol policy with their owners. Generate strict runtime validators from the Rust wire contract and preserve bounded lifecycle and process-tree cleanup.
cargo check and desktop:dev no longer require packages/dsh-acp/dist-profile.
Official packaging still builds the profile and injects it as a Tauri resource.
…pile-resource

fix(desktop): keep the DeepSeek bridge off the compile path
Keep release-sync cron on the in-repo script and document the host
export/import path so a new server does not need a detached AutoUpdate copy.
docs(deploy): restore the OpenBitFun origin from the BitFun checkout
Remove automatic snap-back when the user scrolls into the reserved
blank below the conversation tail.

- Preserve explicit session, rollback, navigation, and tail alignment
- Keep tail-follow recovery when output catches up with the reader
- Update viewport ownership tests and FlowChat behavior documentation
- keep the file explorer on a single virtualized tree renderer
- prevent scrollbar appearance from shifting tree content
- add regression coverage for stable renderer and scrollbar behavior
Switching devices used to move the whole client onto the target: the UI
swapped, and the device you left stopped being usable. So an account with
several devices could still only run one task at a time.

Split the two concepts that were fused together. An *attachment* is a live
control link to a peer and is what keeps its agent running; the *rendered
surface* is the single device this window draws. Attachments now survive UI
switches, so dispatching a turn on B, switching back to A, and dispatching
another turn there leaves both running.

Three things had to change for that to hold:

- A surface switch no longer mutates the device being left.
  resetProductSurface() runs before the transport swap, so its
  terminal_shutdown_all / lsp_close_workspace calls landed on the outgoing
  device and killed the PTYs and language servers an agent turn was still
  using. It is frontend-only now.

- Product events are routed by source device. The controller re-emits peer
  DeviceEvents under their original event name, so background attachments put
  several agent streams on one bus. Re-emitted payloads carry
  __bitfunSourceDeviceId, and deviceSurfaceRouting delivers a surface-scoped
  event only when its producing device is the rendered one.

- Snapshot reconciliation is no longer Peer-only. A turn left running on the
  local device produces events that routing drops while another device is
  rendered, and the relay has no ACK/replay, so returning to it needs the same
  repair the peer surface already had.

The sidebar row becomes a device switcher: always present once signed in,
listing this machine plus every online device with live running indicators, a
count of devices working elsewhere, and an explicit per-device disconnect that
stays distinct from simply looking somewhere else.
The appearance contract audit requires a compound styled owner to expose at
least four distinct parts; the switcher declared three, so themes could not
address its label, status dots, or disconnect action independently.

Tag the remaining styled nodes and register them, plus the busy/offline states
the status dot already rendered through class names only.
Switching devices while a local session was working reported
"Session lost after adding dialog turn" and the message never ran.

`startTurn` adds its projection turn, then awaits the state-machine
transition, an optional worktree bind, and a model-selection sync before
reading the session back. A surface switch clears every projection
synchronously, so a submission caught in that window resumed against an empty
store and threw. The throw lands before `start_dialog_turn`, which is the real
damage: the turn had reached no host, so the user's message was gone rather
than merely rendered somewhere else. The previous change kept the *backend*
running across a switch but left frontend work in flight over the teardown.

Close the window, then survive losing it anyway:

- `resetProductSurface` waits for in-flight submissions to hand their turn to
  a host before clearing the surface, bounded so a wedged submission cannot
  make the switch feel stuck.
- `sendMessage` captures the store's surface generation and compares it when a
  submission fails. A change means the switch caused the failure, not the
  turn: skip the error toast and the error transition, and re-queue the
  message when no host accepted it. Pending queues are keyed by session and
  survive a switch, so returning to that device drains it.

`TurnTracker` gains `hostAcceptedTurn` so the recovery can tell "never
submitted" from "already running elsewhere" instead of guessing.
Switching away from a working local session and back left the prompt on
screen with the whole response, its progress, and its result gone.

Active-session reconciliation has a wholesale replace path that skips the
forward-progress comparator, so a settled turn can adopt the host's
authoritative copy. Two things make that unsafe. A turn keeps its identity and
user message independently of its rounds, so a windowed or not-yet-checkpointed
snapshot can name the turn while carrying none of its work. And a projection
rebuilt by a surface switch has no state machines, so every turn reads as idle
and every snapshot qualifies for replacement. Reconciling then overwrote a
fully hydrated turn with an empty one — exactly the screen the report showed.

Gate the replace on `snapshotDropsProjectedTurnContent`: a snapshot may correct
a turn, never drop rounds, streams, or tool calls the projection already
shows. Forward progress and genuine host copies still replace as before.

Refusing a snapshot must not also cost the re-attach, since the same rebuilt
surface is what needs one. When a snapshot changes nothing but the host reports
an executing turn and the local machine is idle, align the state machine
anyway. While a turn really is streaming the machine is already processing, so
this cannot churn it on every tick.

Found by reproducing the sequence against the real store rather than by
inspection: the projection goes rounds=1 -> rounds=0 across one reconcile.
Both guards are covered by tests that fail without them.
Keep the current scene visible while a lazy target is loading, then
switch atomically once the target is ready.

Apply a subtle entrance fade only to the incoming scene and add
regression coverage for session and agent scene navigation.
* fix(chat): make remote file mentions reliable

* fix(chat): tolerate sessions without config metadata

* fix(chat): register mention picker error state
* refactor(peer): add the device surface identity and epoch contract

Foundation for the multi-device rework: a DeviceSurfaceId that names which
device a piece of state belongs to, a monotonic activation epoch with an
AbortSignal, a typed SurfaceChangedError for unwinding stale work, and the
key-scoping helper every per-device cache must use.

Nothing consumes it yet; the layers land on top of this contract.

* refactor(peer): isolate device surface state and switching
@GCWing

GCWing commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Thank you for your contribution.

The main branch has been replaced with the 1.0.0 codebase from 1.0.0-explore. As part of this migration and Git history cleanup, the affected pull requests have been temporarily closed.

If your changes are still needed, please reapply them on a fresh branch based on the new main, then open a new PR against main or update and reopen this PR. Please link any replacement PR to this one so we can retain the discussion and review context.

We apologize for the disruption and appreciate your understanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants