dsh-ssh-tui 0.8.2-rc.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,9 +10,15 @@
10
10
 
11
11
  ---
12
12
 
13
- ## 0.8.2-rc.1 — 2026-10-03(候选版;本版本地已就位,未打 tag、未发布)
13
+ ## 0.8.2 — 2026-10-05(`latest` + `next`)
14
14
 
15
- 发版说明:[`docs/release-notes-0.8.2-rc.1.md`](release-notes-0.8.2-rc.1.md)。
15
+ 发版说明:[`docs/release-notes-0.8.2.md`](release-notes-0.8.2.md);候选版说明保留在
16
+ [`docs/release-notes-0.8.2-rc.1.md`](release-notes-0.8.2-rc.1.md)。
17
+ 本版由 **0.8.2-rc.1**(2026-10-04,只上 `next`)沉淀一周后转正,**内容与候选版相同**,
18
+ 下列变更即候选版以来累积的全部变更。
19
+
20
+ > `dsh-ssh-tui` is now presented as DSH Relay. The npm package name and the install command
21
+ > have not changed. —— `dsh-ssh-tui` 现在以 DSH Relay 为产品名展示;包名与安装命令没有变化。
16
22
 
17
23
  B2("what deserves to exist")的六个阶段 B2.1 – B2.6 已完成并通过最终审计,本节汇总用户能看到的
18
24
  部分。详细证据与冻结记录见 [`docs/checkpoints.md`](checkpoints.md) 与
@@ -113,6 +119,8 @@ B2("what deserves to exist")的六个阶段 B2.1 – B2.6 已完成并通过
113
119
 
114
120
  ### 已知问题
115
121
 
122
+ - **0.8.2-rc.1 → 0.8.2 无代码变更**:转正只改版本号与发版材料,包内 `lib/` 与候选版逐字节相同
123
+ (候选版沉淀期间未收到新的回归报告)。
116
124
  - Windows / ConPTY **实机人工验收已完成**(2026-10-03,维护者逐条走过 12 项清单,结论见
117
125
  [`docs/checkpoints.md`](checkpoints.md)「Windows 实机轮」):可自动化的部分由 `verify-batch --home
118
126
  <dir>` 覆盖(12 PASS · 2 SKIP · 0 FAIL),**没有自动化、只能人眼的两目(输入法组合、多行中文粘贴)
package/README.en.md CHANGED
@@ -1,17 +1,52 @@
1
- # dsh-ssh-tui
1
+ # DSH Relay
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/dsh-ssh-tui?style=flat-square&color=4b6fff)](https://www.npmjs.com/package/dsh-ssh-tui)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/dsh-ssh-tui?style=flat-square)](https://www.npmjs.com/package/dsh-ssh-tui)
5
5
  [![CI](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml)
6
6
  [![dshfind](https://dshfind.com/api/badge/cyjyyd/dsh-ssh-tui)](https://dshfind.com/en/plugins/cyjyyd/dsh-ssh-tui?ref=badge)
7
7
 
8
- A DeepSeek Harness terminal for jump hosts, headless servers, and high-latency
9
- SSH. Plain ANSI and incremental redraws. No browser required. **When the SSH
10
- session drops mid-turn, the Host keeps the turn; reconnecting with the same
11
- command attaches back to it, so the session is not lost.**
8
+ **A resilient terminal workspace for DeepSeek Harness.**
9
+ remote-first · reconnectable · in your terminal
10
+
11
+ **SSH drops. The session survives.**
12
+ **Reconnect. Reattach. Continue.**
13
+
14
+ That is the whole idea behind the name: the *display* can disappear without the
15
+ *session* disappearing. The host process stays up, and the same command attaches
16
+ back to the same session — transcript, plan cards, approvals, queued messages, all
17
+ where you left them. It is a remote-first terminal workspace, not just an
18
+ SSH-flavoured screen.
19
+
20
+ > **What happens to the turn that was running? That depends on `/disconnect`:**
21
+ > the default `pause` stops the in-flight turn when the display goes away and keeps
22
+ > the session — you reconnect and carry on; `continue` lets that turn keep running
23
+ > in the background and finish without you. The host survives under both. The demo
24
+ > below runs `continue` (the caption says so); do not read it as the default.
25
+
26
+ > **The npm package name has not changed, and neither has the install command.**
27
+ > The published coordinate is still `dsh-ssh-tui`; `DSH Relay` is the product's
28
+ > display name. See [`docs/decisions/brand-dsh-relay.md`](docs/decisions/brand-dsh-relay.md).
29
+
30
+ ```bash
31
+ npm i -g @deepseek-ai/dsh
32
+ dsh plugin --profile tui add dsh-ssh-tui@latest
33
+ dsh --profile tui
34
+ ```
35
+
36
+ Reconnect with `dsh --profile tui --resume`.
37
+
38
+ ![Reconnect demo: a turn streams, the window is closed, the host keeps running, the same command attaches back, and the session is where it was; the demo runs the continue policy, so that turn also finished in the background](docs/screenshots/reconnect.gif)
39
+
40
+ *Recorded, not staged: a real PTY, a real host, a real SIGHUP, a real resume
41
+ (`npm run screenshots:reconnect` regenerates it). The demo sets `/disconnect
42
+ continue`, which is why the turn finishes on screen after the window is gone; under
43
+ the default `pause` the same run would stop mid-turn and the session would still be
44
+ there to attach back to.*
12
45
 
13
46
  中文部署指南:[README.md](README.md)
14
47
 
48
+ Plain ANSI and incremental redraws. No browser required.
49
+
15
50
  If you mostly work over SSH — a jump host, a test box, a keyboard-only
16
51
  session — start here. A local desktop terminal with themes and layout
17
52
  you already like can stay as it is.
@@ -55,6 +90,40 @@ already pinned in `pnpm-lock.yaml` (often 0.3.7). Do not put `--profile`
55
90
  after `add`: `dsh plugin add --profile tui add dsh-ssh-tui` is not valid.
56
91
  Remove with `dsh plugin --profile tui remove dsh-ssh-tui`.
57
92
 
93
+ ## Verified, not assumed
94
+
95
+ <!-- bench:begin -->
96
+ Measured on 100×20, median of 40 runs per action (`npm run bench`; this block is
97
+ generated by `npm run bench:report`, not typed by hand):
98
+
99
+ | Action | ms | bytes | rows addressed | full clears |
100
+ |---|---|---|---|---|
101
+ | one streamed delta while an answer is arriving | 1.64 | 501 | 3 | **0** |
102
+ | one repaint while the agent is busy (the wait card’s clock) | 1.28 | 465 | 3 | **0** |
103
+ | one page key inside a report Screen (PgDn, then PgUp) | 1.41 | 2457 | 17 | **0** |
104
+ | one cursor key inside an open control-plane menu (↓, then ↑) | 1.27 | 588 | 4 | **0** |
105
+ | one character into the wizard’s own field | 0.38 | 140 | 1 | **0** |
106
+
107
+ **0 full-screen clears** across all five actions. That is the performance promise for this
108
+ version: however slow the terminal and however bad the link, frames repaint by row — no tick
109
+ repaints the whole screen.
110
+ <!-- bench:end -->
111
+
112
+ Both commands above are the evidence: `npm run bench` (the numbers), `npm run freeze`
113
+ (that the architecture stayed what it claims). CI re-runs them on every leg — Linux and
114
+ Windows, four host versions. The link-side promises have their own real-PTY probes:
115
+ drop/reattach, a host that outlives the window mid-turn, a resume with history, and the
116
+ "terminal does not answer cursor probes" case; `npm run freeze` lists them.
117
+
118
+ **Tested DSH Relay on a terminal we have not covered yet?**
119
+ [Add a compatibility report](https://github.com/cyjyyd/dsh-ssh-tui/issues/new?template=terminal-compatibility.yml) — one
120
+ form, no code required; results land in the community section of
121
+ [`docs/terminals.md`](docs/terminals.md).
122
+
123
+ If DSH Relay makes remote Harness sessions more reliable for you,
124
+ [starring the repository](https://github.com/cyjyyd/dsh-ssh-tui) helps other terminal
125
+ users find it.
126
+
58
127
  ## Official headless vs this TUI
59
128
 
60
129
  There is no shipped TUI. The default terminal entry on a remote box is
@@ -741,8 +810,10 @@ at the slowest tier. `DSH_TUI_PAINT_MS` always wins (40–1000). The stats line
741
810
  starts with `SSH ●●●○ 90ms` (1 pip red, 2 yellow, 3+ green). The probe
742
811
  does not write into the transcript.
743
812
 
744
- Idle hangup exits the Host. A busy turn keeps it; `--resume` attaches to that
745
- process. Do not start a second Host.
813
+ Idle hangup exits the Host. A *busy* session keeps the Host alive so you can attach
814
+ back; whether the turn itself keeps running across the gap is the `/disconnect`
815
+ policy (`pause` stops it by default, `continue` finishes it in the background).
816
+ `--resume` attaches to that process. Do not start a second Host.
746
817
 
747
818
  ## Troubleshooting (Q&A)
748
819
 
package/README.md CHANGED
@@ -1,23 +1,26 @@
1
- # dsh-ssh-tui
1
+ # DSH Relay
2
+
3
+ **A resilient terminal workspace for DeepSeek Harness.**
4
+ 远端优先 · 可重连 · 就在终端里
2
5
 
3
6
  [![npm](https://img.shields.io/npm/v/dsh-ssh-tui?style=flat-square&color=4b6fff)](https://www.npmjs.com/package/dsh-ssh-tui)
4
7
  [![npm downloads](https://img.shields.io/npm/dm/dsh-ssh-tui?style=flat-square)](https://www.npmjs.com/package/dsh-ssh-tui)
5
8
  [![CI](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml)
6
9
  [![dshfind](https://dshfind.com/api/badge/cyjyyd/dsh-ssh-tui)](https://dshfind.com/zh/plugins/cyjyyd/dsh-ssh-tui?ref=badge)
7
10
 
8
- **A resilient terminal frontend for DeepSeek Harness.**
9
-
10
- 纯 ANSI · 增量重绘 · SSH 断线重接 · Windows / ConPTY · Host-aware activation · 无需浏览器
11
-
12
- **连接可以断,终端可以换,Harness 可以升级;正在工作的会话不应该因此变得脆弱。**
11
+ **SSH 断了。会话还在。接回来继续。**
13
12
 
14
- English: [README.en.md](README.en.md)
15
-
16
- ---
13
+ “Relay” 说的是这件事:**显示可以消失,会话不必跟着消失**。宿主进程活着,同一句命令接回来
14
+ 就回到原来的会话 —— 转写、计划卡片、审批、排队中的消息都在原处。
15
+ 这是远程优先的终端工作区,不只是“一个 SSH 风格的界面”。
17
16
 
18
- ## 30 秒开始
17
+ > **断线时正在跑的那一轮会怎样?取决于 `/disconnect`:**
18
+ > 默认 `pause` —— **当前这一轮在窗口离开时停下**,会话本身不丢,接回来接着用;
19
+ > 显式设成 `continue` —— 当前这一轮在显示端离开后**继续在后台跑完**。
20
+ > 两种策略下宿主都不退出。演示动图用的是 `continue`(字幕里标了),别把它当成默认行为。
19
21
 
20
- 需要 Node.js ≥ 22.19 和 DeepSeek Harness CLI。
22
+ > **npm 包名没有变,安装命令也没有变。** 发布坐标仍然是 `dsh-ssh-tui`;
23
+ > `DSH Relay` 是产品展示名。见 [`docs/decisions/brand-dsh-relay.md`](docs/decisions/brand-dsh-relay.md)。
21
24
 
22
25
  ```bash
23
26
  npm i -g @deepseek-ai/dsh
@@ -25,6 +28,51 @@ dsh plugin --profile tui add dsh-ssh-tui@latest
25
28
  dsh --profile tui
26
29
  ```
27
30
 
31
+ 需要 Node.js ≥ 22.19 和 DeepSeek Harness CLI。断开之后接回来:`dsh --profile tui --resume`。
32
+
33
+ ![重连演示:一轮正在流式输出时窗口关闭,宿主进程不退出,同一条命令接回来,这个会话还在原处;演示用 continue 策略,所以这一轮也在后台跑完了](docs/screenshots/reconnect.gif)
34
+
35
+ *实拍,不是摆拍:真 PTY、真宿主、真 SIGHUP、真 resume(`npm run screenshots:reconnect` 可重放)。
36
+ 演示显式设了 `/disconnect continue`,所以画面上这一轮在窗口消失后跑完了;换成默认的 `pause`,
37
+ 同一段会停在中断处,会话照样接得回来。*
38
+
39
+ English: [README.en.md](README.en.md)
40
+
41
+ ---
42
+
43
+ ## 已测,不是号称
44
+
45
+ <!-- bench:begin -->
46
+ 在 100×20 下实测,每个动作 40 次取中位(`npm run bench`;本节由 `npm run bench:report` 生成,
47
+ 不是手抄的):
48
+
49
+ | 动作 | 毫秒 | 字节 | 寻址行 | 整屏清屏 |
50
+ |---|---|---|---|---|
51
+ | 流式输出时的一次重绘 | 1.64 | 501 | 3 | **0** |
52
+ | 等待卡片时钟走一格 | 1.28 | 465 | 3 | **0** |
53
+ | 报告屏翻一页(PgDn / PgUp) | 1.41 | 2457 | 17 | **0** |
54
+ | 菜单里移动一次光标 | 1.27 | 588 | 4 | **0** |
55
+ | 向导字段里打一个字符 | 0.38 | 140 | 1 | **0** |
56
+
57
+ 五个动作合计 **0 次整屏清屏**。这条是这一版的性能承诺:不管终端多慢、链路多差,
58
+ 画面按行增量重绘,不会因为一次 tick 把整屏重画一遍。
59
+ <!-- bench:end -->
60
+
61
+ 跑法就是上面两条命令;这两条断言在 CI 的每一条腿上都会重跑(Linux 与 Windows、四个宿主版本)。
62
+ 链路上的承诺另有真 PTY 探针:断链重接、断线后宿主存活、带历史的 resume、以及"终端不回光标位置"
63
+ 时的链路显示,各自一个脚本,全部在 CI 里跑(`npm run freeze` 会把它们列出来)。
64
+
65
+ **你在我们没覆盖过的终端上试过 DSH Relay 吗?**
66
+ [提一份终端兼容报告](https://github.com/cyjyyd/dsh-ssh-tui/issues/new?template=terminal-compatibility.yml) —— 不用读代码,填一张表就行,
67
+ 结果会进 [`docs/terminals.md`](docs/terminals.md) 的「社区实测」区。
68
+
69
+ 如果 DSH Relay 让你的远程会话更可靠了,
70
+ [给仓库点个 star](https://github.com/cyjyyd/dsh-ssh-tui) 能帮别的终端用户找到它。
71
+
72
+ ## 30 秒开始
73
+
74
+ 上面三条命令装好就进工作区。之后常用的几条:
75
+
28
76
  恢复旧会话:
29
77
 
30
78
  ```bash
@@ -237,7 +285,8 @@ docs/screenshots/slow-link.json
237
285
  - 调工具;
238
286
  - 运行子代理;
239
287
 
240
- TUI 可以保留 Host,并允许重新接入。
288
+ TUI 可以保留 Host,并允许重新接入(保留的是**宿主**;这一轮本身是否在断线期间继续跑,看
289
+ [`/disconnect`](#ssh-断了之后) 的策略)。
241
290
 
242
291
  ```bash
243
292
  dsh --profile tui --resume
@@ -0,0 +1,62 @@
1
+ # 0.8.2 发版说明
2
+
3
+ > **发布方式:`latest` + `next`。** 上一次发版:**0.8.1**(2026-09-30,`latest` + `next`)。
4
+ > 本版由 **0.8.2-rc.1**(2026-10-04 只上 `next`)沉淀一周后转正:候选版期间没有收到新的回归报告,
5
+ > CI 五条腿与全部真 PTY 探针持续全绿,Windows / ConPTY 实机人工验收也已完成(见
6
+ > [`checkpoints.md`](checkpoints.md)「Windows 实机轮」)。**内容与 0.8.2-rc.1 完全相同**,
7
+ > 逐项说明见 [`release-notes-0.8.2-rc.1.md`](release-notes-0.8.2-rc.1.md)。
8
+
9
+ > `dsh-ssh-tui` is now presented as DSH Relay.
10
+ > The npm package name and the install command have not changed.
11
+ >
12
+ > **`dsh-ssh-tui` 现在以 DSH Relay 为产品名展示;npm 包名与安装命令没有变化。**
13
+ > 发布坐标仍是 `dsh-ssh-tui`,安装命令仍是
14
+ > `dsh plugin --profile tui add dsh-ssh-tui@latest`。依据:[`decisions/brand-dsh-relay.md`](decisions/brand-dsh-relay.md)。
15
+
16
+ ## 头等事:全新安装**第一次就会出现配置向导**(此前根本不会出现)
17
+
18
+ 0.8.1 及以前,一台没有任何配置的机器上装好插件、启动 `dsh --profile tui`,看到的是一个空工作区、
19
+ 一行提示都没有,模型调用必然失败——**配置向导永远不会自动出现**。原因是前端拉起的 Host 永远带
20
+ `--resume=<id>`(连它自己刚铸的新 id 也带),而首启判定把 `resume` 当成"这台机器配置过"。
21
+ 0.8.2 起只看凭据存不存在。详见候选版说明第一节。
22
+
23
+ ## 另外两件用户直接看得见的事
24
+
25
+ - **resume 不再有一段 1–3 秒黑屏**:进屏序列现在跟着第一帧一起发出,加载提示全程在屏
26
+ (20k 事件实测:清屏与落地同一 tick,此前中间有 3.5 秒全黑)。
27
+ - **思考卡片不再发白**:`reasoning` 角色此前只写属性、不写颜色,"暗"完全靠 faint;不实现 faint 的终端
28
+ 会退回默认前景色。四个调色板现在各自给出 muted 颜色。
29
+ - **链路那一格不再把"绘制节奏"当延迟**:终端不回 `CSI 6n` 时显示 `未测` / `n/a`,不再印一个看起来像
30
+ 测量值的 160ms。
31
+
32
+ ## 兼容
33
+
34
+ 上游兼容窗口与 0.8.1 相同:`0.2.0-rc.1` / `0.2.0-rc.2` / `0.1.7-rc.1` / `0.1.7-rc.2`。
35
+ **0.1.5 及更早的宿主不再支持。** 本版不改协议、不加命令、不动上游接口。
36
+
37
+ ## 安装 / 升级
38
+
39
+ ```sh
40
+ dsh plugin --profile tui add dsh-ssh-tui@latest
41
+ ```
42
+
43
+ 装完退出再启动。`/status` 可确认当前版本;装不上先看 `/doctor`。
44
+ 更新时请显式带 `@latest`,否则已有 lockfile 可能继续沿用旧版本。
45
+
46
+ ## 实测数字(本版,100×20)
47
+
48
+ | 动作 | 毫秒(中位) | 字节 | 寻址行 | 全屏清屏 |
49
+ |---|---|---|---|---|
50
+ | 流式 delta | 1.47 | 501 | 3 | **0** |
51
+ | 等待卡时钟 | 1.26 | 465 | 3 | **0** |
52
+ | 报告屏翻页 | 1.40 | 2457 | 17 | **0** |
53
+ | 菜单光标移动 | 1.09 | 588 | 4 | **0** |
54
+ | 向导字段打字 | 0.25 | 140 | 1 | **0** |
55
+
56
+ 复现:`npm run bench`;README 里的同一张表由 `npm run bench:report` 从这次真跑生成。
57
+
58
+ ## 已知问题
59
+
60
+ 见候选版说明的「已知限制」一节。要点:Windows / ConPTY 实机人工验收**已完成**(12 项清单;
61
+ 两处 ConPTY 盲区在探针里显式 `SKIP` 并在 Linux 腿上真跑,`SKIP` 不算通过);`未测` 那一格在
62
+ Windows 本机看不到(ConPTY 代答光标位置),由管道父进程形状的探针覆盖。
Binary file
package/docs/terminals.md CHANGED
@@ -52,6 +52,29 @@
52
52
  - **`?` 的含义**:该终端*可能*生效但需要用户配置(xterm 的 `allowWindowOps`、tmux 的 `set-clipboard on`),
53
53
  所以仍然**照发**;而 `—` 的终端上一次会话只提醒一次,告诉你剪贴板可能没写进去。
54
54
 
55
+ ### 证据等级:作者/CI 实测 vs 社区实测
56
+
57
+ 上面那张表是**作者与 CI 实测**的一等证据:判定代码有单元用例(`tests/terminal-caps.test.mjs`),
58
+ 每种终端真起一次 TUI 看它到底发了哪些序列(`scripts/tui-term-probe.mjs`),CI 每条腿都跑。
59
+ 下面这张表是**社区实测**:读者在自己的终端上试出来的结果,**证据等级不同,不要混着读**。
60
+
61
+ | 终端 | 环境(OS / 传输) | 结论 | 来源 | 日期 |
62
+ |---|---|---|---|---|
63
+ | _(待填)_ | | | | |
64
+
65
+ **怎么贡献一行**:在 GitHub 上提一份
66
+ [终端兼容报告](https://github.com/cyjyyd/dsh-ssh-tui/issues/new?template=terminal-compatibility.yml) ——
67
+ 不用读代码,填一张表(OS、终端、本地/SSH、TERM、版本,以及哪些能力试过、哪些没试过)。
68
+ 报告会由维护者整理到这里,并注明来源与日期。
69
+
70
+ 两栏的差别要一直保留:
71
+
72
+ - **作者/CI 实测** = 有可复现的判定代码与探针,改坏了 CI 会红;
73
+ - **社区实测** = 一个人的一次观察,能证明"在这个环境下能跑",不能证明"在别的版本上也行"。
74
+
75
+ 一条报告即使结论是"能用",也只进社区栏——除非它同时补上了对应的 fixture 或探针,那时它升级为
76
+ 作者/CI 一栏,并在矩阵表里出现一行。
77
+
55
78
  ## 覆盖方式(用户可强制指定)
56
79
 
57
80
  自动判定错了就手动覆盖,两个环境变量都长期有效:
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "dsh-ssh-tui",
3
- "version": "0.8.2-rc.1",
4
- "description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
3
+ "version": "0.8.2",
4
+ "description": "Remote-first terminal UI for DeepSeek Harness with SSH reconnect, Windows/ConPTY and incremental ANSI rendering.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "dsh",
8
8
  "dsh-plugin",
9
- "plugin",
10
9
  "tui",
11
10
  "terminal",
12
11
  "ssh",
13
- "cordis"
12
+ "coding-agent",
13
+ "cli",
14
+ "remote-development",
15
+ "windows-terminal",
16
+ "conpty"
14
17
  ],
15
18
  "author": "cyjyyd",
16
19
  "repository": {
@@ -58,6 +61,7 @@
58
61
  "docs/screenshots/headless.png",
59
62
  "docs/screenshots/workspace.png",
60
63
  "docs/screenshots/slow-link.gif",
64
+ "docs/screenshots/reconnect.gif",
61
65
  "LICENSE",
62
66
  "docs/display-mode.md",
63
67
  "docs/terminals.md",
@@ -108,7 +112,9 @@
108
112
  "probe:home": "node scripts/probe-home.mjs --probe",
109
113
  "probe:home:drop": "node scripts/probe-home.mjs --probe --script tui-drop-probe.mjs",
110
114
  "screenshots": "npm run build && node scripts/capture-readme-frames.mjs",
111
- "screenshots:slow": "npm run build && node scripts/capture-slow-link.mjs"
115
+ "screenshots:slow": "npm run build && node scripts/capture-slow-link.mjs",
116
+ "screenshots:reconnect": "npm run build && node scripts/capture-reconnect.mjs",
117
+ "bench:report": "node scripts/bench-report.mjs"
112
118
  },
113
119
  "engines": {
114
120
  "node": ">=22.19"