pi-web-ui 0.61.0 → 0.62.0

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.
@@ -1,48 +1,48 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <!--
4
- pi-web-ui launchd agent — macOS auto-start at login.
5
-
6
- Install:
7
- npm i -g pi-web-ui
8
- cp deploy/com.xingshuyin.pi-web-ui.plist ~/Library/LaunchAgents/
9
- # edit ProgramArguments / WorkingDirectory / PI_WEB_CWD for your setup
10
- launchctl load ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist
11
- # (or: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist)
12
-
13
- Find the pi-web-ui binary path with: which pi-web-ui
14
- -->
15
- <plist version="1.0">
16
- <dict>
17
- <key>Label</key>
18
- <string>com.xingshuyin.pi-web-ui</string>
19
-
20
- <key>ProgramArguments</key>
21
- <array>
22
- <string>/usr/local/bin/pi-web-ui</string>
23
- </array>
24
-
25
- <key>RunAtLoad</key>
26
- <true/>
27
-
28
- <!-- Restart if it crashes -->
29
- <key>KeepAlive</key>
30
- <true/>
31
-
32
- <key>WorkingDirectory</key>
33
- <string>/Users/YOUR_USER</string>
34
-
35
- <key>EnvironmentVariables</key>
36
- <dict>
37
- <key>PI_WEB_PORT</key>
38
- <string>8787</string>
39
- <key>PI_WEB_CWD</key>
40
- <string>/Users/YOUR_USER</string>
41
- </dict>
42
-
43
- <key>StandardOutPath</key>
44
- <string>/tmp/pi-web-ui.log</string>
45
- <key>StandardErrorPath</key>
46
- <string>/tmp/pi-web-ui.err</string>
47
- </dict>
48
- </plist>
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <!--
4
+ pi-web-ui launchd agent — macOS auto-start at login.
5
+
6
+ Install:
7
+ npm i -g pi-web-ui
8
+ cp deploy/com.xingshuyin.pi-web-ui.plist ~/Library/LaunchAgents/
9
+ # edit ProgramArguments / WorkingDirectory / PI_WEB_CWD for your setup
10
+ launchctl load ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist
11
+ # (or: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist)
12
+
13
+ Find the pi-web-ui binary path with: which pi-web-ui
14
+ -->
15
+ <plist version="1.0">
16
+ <dict>
17
+ <key>Label</key>
18
+ <string>com.xingshuyin.pi-web-ui</string>
19
+
20
+ <key>ProgramArguments</key>
21
+ <array>
22
+ <string>/usr/local/bin/pi-web-ui</string>
23
+ </array>
24
+
25
+ <key>RunAtLoad</key>
26
+ <true/>
27
+
28
+ <!-- Restart if it crashes -->
29
+ <key>KeepAlive</key>
30
+ <true/>
31
+
32
+ <key>WorkingDirectory</key>
33
+ <string>/Users/YOUR_USER</string>
34
+
35
+ <key>EnvironmentVariables</key>
36
+ <dict>
37
+ <key>PI_WEB_PORT</key>
38
+ <string>8787</string>
39
+ <key>PI_WEB_CWD</key>
40
+ <string>/Users/YOUR_USER</string>
41
+ </dict>
42
+
43
+ <key>StandardOutPath</key>
44
+ <string>/tmp/pi-web-ui.log</string>
45
+ <key>StandardErrorPath</key>
46
+ <string>/tmp/pi-web-ui.err</string>
47
+ </dict>
48
+ </plist>
@@ -1,88 +1,88 @@
1
- # pi-web-ui behind nginx at a sub-path: http://<host>:83/pi/
2
- # Backend app: http://127.0.0.1:8787 (default PI_WEB_PORT env)
3
- #
4
- # IMPORTANT: pi-web-ui 0.23+ checks the WebSocket Origin against the request
5
- # Host (hostname AND port). Every proxied location MUST forward the original
6
- # Host with $http_host (keeps the port). Using $host (drops the port) or
7
- # leaving Host unset (defaults to 127.0.0.1:8787) makes the upgrade fail with
8
- # 403 — the page loads but chat/terminal keep reconnecting.
9
- #
10
- # Topology (two listeners on one port: frp + LAN coexist):
11
- # frp (public) -> <PUBLIC_IP>:<PUBLIC_PORT> -> 127.0.0.1:83 (PROXY protocol v2)
12
- # LAN users -> http://<LAN_IP>:83/pi/ (plain HTTP listener)
13
- #
14
- # frpc sends PROXY v2 to nginx (transport.proxyProtocolVersion = "v2"):
15
- # * 127.0.0.1:83 proxy_protocol — only the local frp client connects
16
- # here (it speaks PROXY v2; nginx then sees the real visitor IP).
17
- # * <LAN_IP>:83 plain HTTP — LAN browsers, no PROXY header needed.
18
- #
19
- # Simpler alternative (no real client IPs): drop proxy_protocol entirely and
20
- # use a single `listen 83;` — then frpc must NOT set proxyProtocolVersion.
21
- #
22
- # The frontend uses absolute paths (/ws WebSocket, /assets/*, /favicon.svg,
23
- # /api/file…) so those get their own proxied locations next to /pi/.
24
-
25
- # Reuse for Upgrade/Connection headers (WebSocket).
26
- map $http_upgrade $connection_upgrade {
27
- default upgrade;
28
- '' close;
29
- }
30
-
31
- server {
32
- listen 127.0.0.1:83 proxy_protocol;
33
- listen <LAN_IP>:83;
34
-
35
- server_name _;
36
-
37
- # Trust PROXY-protocol headers only from the local frp client; plain
38
- # (LAN) connections keep their real $remote_addr untouched.
39
- set_real_ip_from 127.0.0.1;
40
- real_ip_header proxy_protocol;
41
-
42
- # ---- main entry: strip /pi/ and forward to the app root ----
43
- location /pi/ {
44
- proxy_pass http://127.0.0.1:8787/;
45
- proxy_http_version 1.1;
46
- # $http_host keeps the port — origin check compares hostname AND port.
47
- proxy_set_header Host $http_host;
48
- proxy_set_header X-Real-IP $remote_addr;
49
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
50
- proxy_set_header X-Forwarded-Proto $scheme;
51
- proxy_set_header Upgrade $http_upgrade;
52
- proxy_set_header Connection $connection_upgrade;
53
- }
54
-
55
- # ---- WebSocket (the frontend connects to ws://<host>/ws) ----
56
- location /ws {
57
- proxy_pass http://127.0.0.1:8787;
58
- proxy_http_version 1.1;
59
- proxy_set_header Host $http_host;
60
- proxy_set_header Upgrade $http_upgrade;
61
- proxy_set_header Connection $connection_upgrade;
62
- proxy_read_timeout 3600s;
63
- proxy_send_timeout 3600s;
64
- proxy_set_header X-Real-IP $remote_addr;
65
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
66
- }
67
-
68
- # ---- absolute asset paths baked into index.html ----
69
- location /assets/ {
70
- proxy_pass http://127.0.0.1:8787;
71
- }
72
- location = /favicon.svg {
73
- proxy_pass http://127.0.0.1:8787;
74
- }
75
- location = /favicon-streaming.svg {
76
- proxy_pass http://127.0.0.1:8787;
77
- }
78
-
79
- # ---- media preview / download / health API ----
80
- location /api/ {
81
- proxy_pass http://127.0.0.1:8787;
82
- }
83
-
84
- # bare root → app entry
85
- location = / {
86
- return 302 /pi/;
87
- }
88
- }
1
+ # pi-web-ui behind nginx at a sub-path: http://<host>:83/pi/
2
+ # Backend app: http://127.0.0.1:8787 (default PI_WEB_PORT env)
3
+ #
4
+ # IMPORTANT: pi-web-ui 0.23+ checks the WebSocket Origin against the request
5
+ # Host (hostname AND port). Every proxied location MUST forward the original
6
+ # Host with $http_host (keeps the port). Using $host (drops the port) or
7
+ # leaving Host unset (defaults to 127.0.0.1:8787) makes the upgrade fail with
8
+ # 403 — the page loads but chat/terminal keep reconnecting.
9
+ #
10
+ # Topology (two listeners on one port: frp + LAN coexist):
11
+ # frp (public) -> <PUBLIC_IP>:<PUBLIC_PORT> -> 127.0.0.1:83 (PROXY protocol v2)
12
+ # LAN users -> http://<LAN_IP>:83/pi/ (plain HTTP listener)
13
+ #
14
+ # frpc sends PROXY v2 to nginx (transport.proxyProtocolVersion = "v2"):
15
+ # * 127.0.0.1:83 proxy_protocol — only the local frp client connects
16
+ # here (it speaks PROXY v2; nginx then sees the real visitor IP).
17
+ # * <LAN_IP>:83 plain HTTP — LAN browsers, no PROXY header needed.
18
+ #
19
+ # Simpler alternative (no real client IPs): drop proxy_protocol entirely and
20
+ # use a single `listen 83;` — then frpc must NOT set proxyProtocolVersion.
21
+ #
22
+ # The frontend uses absolute paths (/ws WebSocket, /assets/*, /favicon.svg,
23
+ # /api/file…) so those get their own proxied locations next to /pi/.
24
+
25
+ # Reuse for Upgrade/Connection headers (WebSocket).
26
+ map $http_upgrade $connection_upgrade {
27
+ default upgrade;
28
+ '' close;
29
+ }
30
+
31
+ server {
32
+ listen 127.0.0.1:83 proxy_protocol;
33
+ listen <LAN_IP>:83;
34
+
35
+ server_name _;
36
+
37
+ # Trust PROXY-protocol headers only from the local frp client; plain
38
+ # (LAN) connections keep their real $remote_addr untouched.
39
+ set_real_ip_from 127.0.0.1;
40
+ real_ip_header proxy_protocol;
41
+
42
+ # ---- main entry: strip /pi/ and forward to the app root ----
43
+ location /pi/ {
44
+ proxy_pass http://127.0.0.1:8787/;
45
+ proxy_http_version 1.1;
46
+ # $http_host keeps the port — origin check compares hostname AND port.
47
+ proxy_set_header Host $http_host;
48
+ proxy_set_header X-Real-IP $remote_addr;
49
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
50
+ proxy_set_header X-Forwarded-Proto $scheme;
51
+ proxy_set_header Upgrade $http_upgrade;
52
+ proxy_set_header Connection $connection_upgrade;
53
+ }
54
+
55
+ # ---- WebSocket (the frontend connects to ws://<host>/ws) ----
56
+ location /ws {
57
+ proxy_pass http://127.0.0.1:8787;
58
+ proxy_http_version 1.1;
59
+ proxy_set_header Host $http_host;
60
+ proxy_set_header Upgrade $http_upgrade;
61
+ proxy_set_header Connection $connection_upgrade;
62
+ proxy_read_timeout 3600s;
63
+ proxy_send_timeout 3600s;
64
+ proxy_set_header X-Real-IP $remote_addr;
65
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
66
+ }
67
+
68
+ # ---- absolute asset paths baked into index.html ----
69
+ location /assets/ {
70
+ proxy_pass http://127.0.0.1:8787;
71
+ }
72
+ location = /favicon.svg {
73
+ proxy_pass http://127.0.0.1:8787;
74
+ }
75
+ location = /favicon-streaming.svg {
76
+ proxy_pass http://127.0.0.1:8787;
77
+ }
78
+
79
+ # ---- media preview / download / health API ----
80
+ location /api/ {
81
+ proxy_pass http://127.0.0.1:8787;
82
+ }
83
+
84
+ # bare root → app entry
85
+ location = / {
86
+ return 302 /pi/;
87
+ }
88
+ }
@@ -1,54 +1,54 @@
1
- <?xml version="1.0" encoding="UTF-16"?>
2
- <!--
3
- pi-web-ui Task Scheduler task — Windows auto-start at logon.
4
-
5
- pi-web-ui server install 默认使用「登录 Run 键 + wscript 隐藏启动」(HKCU,无需管理员、
6
- 无黑窗),并自动迁移旧版计划任务安装。本 XML 仅保留给需要手动注册计划任务的场景
7
- (例如管理员环境),不是默认路径。
8
- -->
9
- <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
10
- <RegistrationInfo>
11
- <Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
12
- </RegistrationInfo>
13
- <Triggers>
14
- <LogonTrigger>
15
- <Enabled>true</Enabled>
16
- </LogonTrigger>
17
- </Triggers>
18
- <Principals>
19
- <Principal id="Author">
20
- <LogonType>InteractiveToken</LogonType>
21
- <RunLevel>LeastPrivilege</RunLevel>
22
- </Principal>
23
- </Principals>
24
- <Settings>
25
- <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
26
- <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
27
- <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
28
- <AllowHardTerminate>true</AllowHardTerminate>
29
- <StartWhenAvailable>false</StartWhenAvailable>
30
- <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
31
- <IdleSettings>
32
- <StopOnIdleEnd>false</StopOnIdleEnd>
33
- <RestartOnIdle>false</RestartOnIdle>
34
- </IdleSettings>
35
- <AllowStartOnDemand>true</AllowStartOnDemand>
36
- <Enabled>true</Enabled>
37
- <Hidden>false</Hidden>
38
- <RunOnlyIfIdle>false</RunOnlyIfIdle>
39
- <WakeToRun>false</WakeToRun>
40
- <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
41
- <Priority>7</Priority>
42
- <RestartOnFailure>
43
- <Interval>PT1M</Interval>
44
- <Count>3</Count>
45
- </RestartOnFailure>
46
- </Settings>
47
- <Actions Context="Author">
48
- <Exec>
49
- <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
50
- <Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
51
- <WorkingDirectory>%USERPROFILE%</WorkingDirectory>
52
- </Exec>
53
- </Actions>
54
- </Task>
1
+ <?xml version="1.0" encoding="UTF-16"?>
2
+ <!--
3
+ pi-web-ui Task Scheduler task — Windows auto-start at logon.
4
+
5
+ pi-web-ui server install 默认使用「登录 Run 键 + wscript 隐藏启动」(HKCU,无需管理员、
6
+ 无黑窗),并自动迁移旧版计划任务安装。本 XML 仅保留给需要手动注册计划任务的场景
7
+ (例如管理员环境),不是默认路径。
8
+ -->
9
+ <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
10
+ <RegistrationInfo>
11
+ <Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
12
+ </RegistrationInfo>
13
+ <Triggers>
14
+ <LogonTrigger>
15
+ <Enabled>true</Enabled>
16
+ </LogonTrigger>
17
+ </Triggers>
18
+ <Principals>
19
+ <Principal id="Author">
20
+ <LogonType>InteractiveToken</LogonType>
21
+ <RunLevel>LeastPrivilege</RunLevel>
22
+ </Principal>
23
+ </Principals>
24
+ <Settings>
25
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
26
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
27
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
28
+ <AllowHardTerminate>true</AllowHardTerminate>
29
+ <StartWhenAvailable>false</StartWhenAvailable>
30
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
31
+ <IdleSettings>
32
+ <StopOnIdleEnd>false</StopOnIdleEnd>
33
+ <RestartOnIdle>false</RestartOnIdle>
34
+ </IdleSettings>
35
+ <AllowStartOnDemand>true</AllowStartOnDemand>
36
+ <Enabled>true</Enabled>
37
+ <Hidden>false</Hidden>
38
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
39
+ <WakeToRun>false</WakeToRun>
40
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
41
+ <Priority>7</Priority>
42
+ <RestartOnFailure>
43
+ <Interval>PT1M</Interval>
44
+ <Count>3</Count>
45
+ </RestartOnFailure>
46
+ </Settings>
47
+ <Actions Context="Author">
48
+ <Exec>
49
+ <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
50
+ <Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
51
+ <WorkingDirectory>%USERPROFILE%</WorkingDirectory>
52
+ </Exec>
53
+ </Actions>
54
+ </Task>
@@ -1,31 +1,31 @@
1
- # pi-web-ui systemd unit — Linux auto-start on boot.
2
- #
3
- # Install (adjust User/WorkingDirectory/Environment to taste):
4
- # sudo npm i -g pi-web-ui
5
- # sudo cp deploy/pi-web-ui.service /etc/systemd/system/
6
- # sudo systemctl daemon-reload
7
- # sudo systemctl enable --now pi-web-ui
8
- # sudo systemctl status pi-web-ui
9
- #
10
- # Logs: journalctl -u pi-web-ui -f
11
-
12
- [Unit]
13
- Description=pi-web-ui — web chat for the pi coding agent
14
- After=network.target
15
-
16
- [Service]
17
- Type=simple
18
- # Run as an unprivileged user (change to your user, e.g. yourname)
19
- User=YOUR_USER
20
- # The workspace the agent operates in (read/edit/bash/write)
21
- WorkingDirectory=/home/YOUR_USER
22
- Environment=PI_WEB_PORT=8787
23
- # Point at your pi config dir if it's not the default ~/.pi/agent
24
- #Environment=PI_CODING_AGENT_DIR=/home/YOUR_USER/.pi/agent
25
- ExecStart=/usr/bin/pi-web-ui
26
- Restart=on-failure
27
- RestartSec=5
28
- # npm global bin may live elsewhere (nvm, etc.) — find with: which pi-web-ui
29
-
30
- [Install]
31
- WantedBy=multi-user.target
1
+ # pi-web-ui systemd unit — Linux auto-start on boot.
2
+ #
3
+ # Install (adjust User/WorkingDirectory/Environment to taste):
4
+ # sudo npm i -g pi-web-ui
5
+ # sudo cp deploy/pi-web-ui.service /etc/systemd/system/
6
+ # sudo systemctl daemon-reload
7
+ # sudo systemctl enable --now pi-web-ui
8
+ # sudo systemctl status pi-web-ui
9
+ #
10
+ # Logs: journalctl -u pi-web-ui -f
11
+
12
+ [Unit]
13
+ Description=pi-web-ui — web chat for the pi coding agent
14
+ After=network.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ # Run as an unprivileged user (change to your user, e.g. yourname)
19
+ User=YOUR_USER
20
+ # The workspace the agent operates in (read/edit/bash/write)
21
+ WorkingDirectory=/home/YOUR_USER
22
+ Environment=PI_WEB_PORT=8787
23
+ # Point at your pi config dir if it's not the default ~/.pi/agent
24
+ #Environment=PI_CODING_AGENT_DIR=/home/YOUR_USER/.pi/agent
25
+ ExecStart=/usr/bin/pi-web-ui
26
+ Restart=on-failure
27
+ RestartSec=5
28
+ # npm global bin may live elsewhere (nvm, etc.) — find with: which pi-web-ui
29
+
30
+ [Install]
31
+ WantedBy=multi-user.target
@@ -82,14 +82,14 @@ export class QuiesceRejectedError extends Error {
82
82
  * programs wait for input that never comes. Legacy Chinese files are often
83
83
  * GBK/GB2312 — read them with the right encoding, never paste mojibake into
84
84
  * reasoning/answers. */
85
- const WINDOWS_PERSONA = `You are a coding agent running on Windows. The bash tool runs Git Bash (bash.exe), not PowerShell. Follow these rules to avoid hanging the session:
86
-
87
-
88
-
89
- - ALWAYS pass a timeout parameter to the bash tool (in seconds). There is NO default timeout — a command that never finishes (servers, watchers, infinite loops, slow downloads/installs) will hang the entire conversation indefinitely. Pick a generous timeout for long-running work, but never omit it.
90
- - NEVER run interactive or foreground long-running commands through the bash tool (vi, less, top, python -, node -, npm run dev, sleep 10000). For servers/daemons use background execution with output redirected to a log file, then poll the log; stop them when done.
91
- - In the interactive terminal (TTY) — which is Git Bash too, not PowerShell — NEVER use heredocs (<<'EOF' ... EOF) or here-strings, and NEVER start interactive programs (vi, less, python -, node -, npm init): they wait for keyboard input that never arrives and hang the terminal forever. Prefer writing a temp script file (e.g. .pi-tmp.sh) and running it non-interactively. ALWAYS pass a timeout to long-running commands (e.g. \`timeout 120 npm run dev\`).
92
-
85
+ const WINDOWS_PERSONA = `You are a coding agent running on Windows. The bash tool runs Git Bash (bash.exe), not PowerShell. Follow these rules to avoid hanging the session:
86
+
87
+
88
+
89
+ - ALWAYS pass a timeout parameter to the bash tool (in seconds). There is NO default timeout — a command that never finishes (servers, watchers, infinite loops, slow downloads/installs) will hang the entire conversation indefinitely. Pick a generous timeout for long-running work, but never omit it.
90
+ - NEVER run interactive or foreground long-running commands through the bash tool (vi, less, top, python -, node -, npm run dev, sleep 10000). For servers/daemons use background execution with output redirected to a log file, then poll the log; stop them when done.
91
+ - In the interactive terminal (TTY) — which is Git Bash too, not PowerShell — NEVER use heredocs (<<'EOF' ... EOF) or here-strings, and NEVER start interactive programs (vi, less, python -, node -, npm init): they wait for keyboard input that never arrives and hang the terminal forever. Prefer writing a temp script file (e.g. .pi-tmp.sh) and running it non-interactively. ALWAYS pass a timeout to long-running commands (e.g. \`timeout 120 npm run dev\`).
92
+
93
93
  Many legacy Chinese text files (.html/.txt/.md/.log, exported documents) are GBK/GB2312 encoded: the read tool decodes UTF-8 only and will show mojibake (乱码) for them. If a file's content looks garbled, read it through the terminal instead: in Git Bash use \`cat file | iconv -f GBK -t UTF-8\` (or \`iconv -f GBK -t UTF-8 file\`); in cmd use \`chcp 65001 && type file\`; in PowerShell use \`Get-Content -Encoding Default file\`. Never paste mojibake into your reasoning or answer — describe the decoded content instead.`;
94
94
  /** bash 工具输出限制/过滤管道引导:模型习惯套 `| tail/-n`、`| head`、`| grep`、`| less`
95
95
  * 等限输出。这些管道在持久终端里会①缓冲(可见终端全程哑火、看不到实时进度)②把退出码
@@ -2166,6 +2166,63 @@ export class ClientSession {
2166
2166
  await this.interruptRun(this.conv, "已停止");
2167
2167
  this.flushSnapshot();
2168
2168
  }
2169
+ /**
2170
+ * Remove ONE queued prompt text (the ✕ on a pending bubble) so it is neither
2171
+ * shown nor eventually delivered. The pi SDK has no per-item queue API, so we
2172
+ * drain the SDK queue (clearQueue), drop the target text and re-queue the rest
2173
+ * in their original order; the SDK re-emits queue_update which re-syncs
2174
+ * conv.queueSteering / conv.queueFollowUp.
2175
+ */
2176
+ async removeQueued(kind, text) {
2177
+ const conv = this.conv;
2178
+ // Always-defined display mirrors; also the provenance of bubble rendering.
2179
+ const local = kind === "steer" ? conv.queueSteering : conv.queueFollowUp;
2180
+ if (!local.includes(text)) {
2181
+ // Already gone (delivered / cleared elsewhere) — just refresh the display.
2182
+ this.flushSnapshot();
2183
+ return;
2184
+ }
2185
+ const s = this.conv.session;
2186
+ if (!s) {
2187
+ // Runtime not bound yet (fresh conversation) — drop the display mirror;
2188
+ // a later queue_update reconciles any SDK-side state.
2189
+ const i = local.indexOf(text);
2190
+ if (i >= 0)
2191
+ local.splice(i, 1);
2192
+ this.flushSnapshot();
2193
+ return;
2194
+ }
2195
+ const { steering, followUp } = s.clearQueue();
2196
+ const keptSteering = steering.filter((t) => !(kind === "steer" && t === text));
2197
+ const keptFollowUp = followUp.filter((t) => !(kind === "followUp" && t === text));
2198
+ // Re-queue the survivors in original order. Guard each call so a single
2199
+ // failure can't leave the queue half-drained silently.
2200
+ for (const t of keptSteering) {
2201
+ try {
2202
+ await s.steer(t);
2203
+ }
2204
+ catch (err) {
2205
+ this.emit({
2206
+ type: "notice",
2207
+ level: "error",
2208
+ text: `重新入队插队消息失败:${err.message}`,
2209
+ });
2210
+ }
2211
+ }
2212
+ for (const t of keptFollowUp) {
2213
+ try {
2214
+ await s.followUp(t);
2215
+ }
2216
+ catch (err) {
2217
+ this.emit({
2218
+ type: "notice",
2219
+ level: "error",
2220
+ text: `重新入队排队消息失败:${err.message}`,
2221
+ });
2222
+ }
2223
+ }
2224
+ this.flushSnapshot();
2225
+ }
2169
2226
  /** Re-push the current list on request (panel opened); prunes dead entries first. */
2170
2227
  async listBgServers() {
2171
2228
  await this.bg.listAndPush();
@@ -448,9 +448,9 @@ export async function buildAttachmentMessages(ctx, attachments) {
448
448
  content: [
449
449
  {
450
450
  type: "text",
451
- text: `
452
- <vision-bridge>
453
- ${transcript}
451
+ text: `
452
+ <vision-bridge>
453
+ ${transcript}
454
454
  </vision-bridge>`,
455
455
  },
456
456
  ...(pathImg
@@ -1157,6 +1157,20 @@ export class DshClientSession {
1157
1157
  }
1158
1158
  await this.promptConv(conv, text, attachments, queue);
1159
1159
  }
1160
+ /**
1161
+ * Remove ONE queued prompt text (the ✕ on a pending bubble). DSH queues are
1162
+ * display-only — the prompt was already handed to the runtime inbox, so this
1163
+ * only drops the pending bubble from the UI (there is no per-item runtime
1164
+ * cancel). Removes the first occurrence of `text`.
1165
+ */
1166
+ removeQueued(kind, text) {
1167
+ const conv = this.conv;
1168
+ const arr = kind === "steer" ? conv.queue.steering : conv.queue.followUp;
1169
+ const i = arr.indexOf(text);
1170
+ if (i >= 0)
1171
+ arr.splice(i, 1);
1172
+ this.flushSnapshot();
1173
+ }
1160
1174
  /** 向指定会话发提示(审查注入/重发用;不处理 fromDisk fork 与命名)。 */
1161
1175
  /** 排空期拒绝新工作(与 pi 引擎同文案;存量运行继续)。 */
1162
1176
  quiesceBlocked() {