@liguoshuai/pi-web-chat 2.14.5 → 2.14.12
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/docs/CHANGELOG.md +43 -0
- package/package.json +3 -3
- package/public/app.js +36 -0
package/docs/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
## [2.14.12] - 2026-09-05
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **修复 HarmonyOS `versionCode` 位数与数值回退**:将 `clients/harmony/AppScope/app.json5` 的 `versionCode` 从错误的三位缩短格式(`21411`)恢复为标准的七位格式(`2141200`),并统一全仓版本号至 2.14.12。修正自 2.14.7 起 `versionCode` 由 `2140600` 误缩为 `21407` 造成的数值回退,避免 HarmonyOS 应用商店因版本号非单调递增而拒绝上架。
|
|
14
|
+
|
|
15
|
+
## [2.14.11] - 2026-09-05
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **固化服务端与测试进程生命周期回收**:在 `createServer().close()` 中增加 `httpServer.closeAllConnections()` 及 `wss.clients` 强制 terminate 机制,杜绝单元测试及异常退出时因 TCP Keep-Alive 或 WebSocket 连接残留导致 `server.close()` 阻塞挂死。
|
|
19
|
+
|
|
20
|
+
## [2.14.10] - 2026-09-05
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **兼容 Node 18+ 原生测试参数**:移除 `node --test` 中 Node 18 不支持的 `--test-timeout` 选项,统一在异步测试内部通过标准 `setTimeout` + `try ... finally` 进行跨版本超时控制与资源清理。
|
|
24
|
+
|
|
25
|
+
## [2.14.9] - 2026-09-05
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **修复 CI 依赖安装与 Lockfile 同步**:补全 `clients/web/package.json` 中的依赖声明并同步 `pnpm-lock.yaml`,修复 GitHub Actions 流水线中 `pnpm install --frozen-lockfile` 报错。
|
|
29
|
+
|
|
30
|
+
## [2.14.8] - 2026-09-05
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **修复移动端(Android & HarmonyOS)新会话无法正常拉起且卡在“思考中…”的关键 Bug**:
|
|
34
|
+
- **反序列化强类型报错丢包修复**:`GenericServerMessage` 中 `message`、`code`、`id`、`content` 等多态字段由 `String?` 调整为 `JsonElement?`。修复 Pi 发送 `message_start` / `message_end`(`message` 为 JSON 对象)及 `pi_exit`(`code` 为数字)时抛出 `JsonDecodingException: Expected string, but had BEGIN_OBJECT/0` 导致事件被静默丢弃、会话状态无法流转的问题。
|
|
35
|
+
- **Prompt 失败响应与错误透传闭环**:服务端在 `activeAgent.send(prompt)` 发生预检失败或 Agent 进程死亡时立即回传 `success: false` 响应;移动端(Android `ChatRepository` 与鸿蒙 `ChatViewModel`)补充 `response.command === "prompt"` 失败处理分支,立即重置 `isStreaming = false` 并展示错误卡片,杜绝无响应时永久卡在“思考中…”。
|
|
36
|
+
- **新建会话状态全链路同步**:移动端在触发 `new_session` 成功后,自动重置流式状态并向服务端重新请求 `get_state` 与 `get_available_models`,同时将当前已选模型及思考级别重新应用到新会话 Agent 实例中。
|
|
37
|
+
- **模型报错与异常 UI 渲染完善**:补充 `message_end` 中 `stopReason === "error"` 及 `MessageStatus.ERROR` 的多端错误占位与提示渲染,避免异常时产生空白或假死气泡。
|
|
38
|
+
- **单元测试防挂死与超时保护**:为全仓 Node 测试命令增加 `--test-timeout=30000` 超时保护,网关 WebSocket 单元测试增加显式超时 reject 与 `try ... finally` 资源安全释放,防止异常未捕获导致测试进程永久挂起阻塞流水线。
|
|
39
|
+
|
|
40
|
+
## [2.14.7] - 2026-09-05
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- **修复僵死任务问题**:服务端 `send()` 对 `prompt`/`steer`/`client_send` 等长运行命令增加可配置超时(默认 10 分钟,环境变量 `LONG_RUNNING_TIMEOUT_MS`),超时后清理 pending entry、重置 streaming 状态并广播 error 事件,防止 pi 子进程挂起时 agent 成为不可回收的僵尸。
|
|
44
|
+
- **前端 `tool_execution_end` 清理 activeToolCalls**:工具执行完成时从 `activeToolCalls` Map 中删除该 entry,避免 stale 条目累积。此前仅在 `finalizeStreamingMsg`/`clearChat` 等批量清空,若 `tool_execution_end` 事件丢失则工具永远卡在"执行中…"。
|
|
45
|
+
- **WebSocket 断线时标记 stale 工具为"已中断"**:`ws.onclose` 中遍历 `activeToolCalls`,将仍为"执行中…"状态的 tool block 标记为"已中断"并停止计时,防止断线后残留僵尸工具块。
|
|
46
|
+
- **`backfill_end` 后清理 stale 工具**:重连回放结束后,若服务端表示 `!streaming`,将所有仍为"执行中…"的 tool block 标记为"已中断",处理 buffer 溢出丢失 `tool_execution_end` 事件的场景。
|
|
47
|
+
|
|
48
|
+
## [2.14.6] - 2026-09-05
|
|
49
|
+
|
|
50
|
+
### Docs
|
|
51
|
+
- 在 `AGENTS.md` 新增「⚡ 效率、专注与职业化准则」章节,从效率(并行执行、合并命令、禁止冗余读取、最小路径)、专注(单一目标、范围控制、先澄清后动手、一次到位)、职业化(诚实不作假、先诊断后修复、最小改动、透明收尾)三个维度规范 AI 智能体行为,提升交付效率与专业度。
|
|
52
|
+
|
|
10
53
|
## [2.14.5] - 2026-09-05
|
|
11
54
|
|
|
12
55
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liguoshuai/pi-web-chat",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.12",
|
|
4
4
|
"description": "A ChatGPT/Gemini-style web UI for the pi coding agent, powered by pi's RPC mode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"express": "^4.21.2",
|
|
44
44
|
"ws": "^8.18.0",
|
|
45
|
-
"@liguoshuai/pi-chat-protocol": "2.14.
|
|
46
|
-
"@liguoshuai/pi-chat-server": "2.14.
|
|
45
|
+
"@liguoshuai/pi-chat-protocol": "2.14.12",
|
|
46
|
+
"@liguoshuai/pi-chat-server": "2.14.12"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "node --check server.js && node --check bin/pi-web-chat.js && node --check public/app.js",
|
package/public/app.js
CHANGED
|
@@ -1932,6 +1932,22 @@ function connectWs(opts = {}) {
|
|
|
1932
1932
|
isConnecting = false;
|
|
1933
1933
|
if (ws._suppressOnclose) return;
|
|
1934
1934
|
|
|
1935
|
+
// Mark any tool blocks still in "执行中…" as interrupted — otherwise they
|
|
1936
|
+
// stay as zombie "执行中…" blocks forever after a disconnect mid-execution.
|
|
1937
|
+
for (const [, tc] of state.activeToolCalls) {
|
|
1938
|
+
const stateEl = tc.head?.querySelector(".state");
|
|
1939
|
+
if (stateEl && stateEl.classList.contains("running")) {
|
|
1940
|
+
stateEl.className = "state error";
|
|
1941
|
+
stateEl.textContent = "已中断";
|
|
1942
|
+
}
|
|
1943
|
+
if (tc.durationEl && tc.durationEl._startedAt) {
|
|
1944
|
+
const dur = Math.max(0, Date.now() - tc.durationEl._startedAt);
|
|
1945
|
+
tc.durationEl.className = "tool-duration";
|
|
1946
|
+
tc.durationEl._startedAt = null;
|
|
1947
|
+
tc.durationEl.textContent = `耗时 ${formatDuration(dur)}`;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1935
1951
|
wasDisconnected = true;
|
|
1936
1952
|
setConnStatus("disconnected");
|
|
1937
1953
|
scheduleReconnect();
|
|
@@ -2006,6 +2022,22 @@ function handlePiMessage(obj) {
|
|
|
2006
2022
|
syncSessionHistory(state.currentSessionFile, false);
|
|
2007
2023
|
}
|
|
2008
2024
|
} else {
|
|
2025
|
+
// Server says not streaming — finalize any stale tool blocks that were
|
|
2026
|
+
// left in "执行中…" by a partial backfill (e.g. buffer overflow lost
|
|
2027
|
+
// the matching tool_execution_end event).
|
|
2028
|
+
for (const [, tc] of state.activeToolCalls) {
|
|
2029
|
+
const stateEl = tc.head?.querySelector(".state");
|
|
2030
|
+
if (stateEl && stateEl.classList.contains("running")) {
|
|
2031
|
+
stateEl.className = "state error";
|
|
2032
|
+
stateEl.textContent = "已中断";
|
|
2033
|
+
}
|
|
2034
|
+
if (tc.durationEl && tc.durationEl._startedAt) {
|
|
2035
|
+
const dur = Math.max(0, Date.now() - tc.durationEl._startedAt);
|
|
2036
|
+
tc.durationEl.className = "tool-duration";
|
|
2037
|
+
tc.durationEl._startedAt = null;
|
|
2038
|
+
tc.durationEl.textContent = `耗时 ${formatDuration(dur)}`;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2009
2041
|
finalizeStreamingMsg();
|
|
2010
2042
|
state.streaming = false;
|
|
2011
2043
|
state.aborting = false;
|
|
@@ -2285,6 +2317,10 @@ function handlePiMessage(obj) {
|
|
|
2285
2317
|
stateEl.textContent = obj.isError ? "错误" : "完成";
|
|
2286
2318
|
stateEl.className = "state" + (obj.isError ? " error" : "");
|
|
2287
2319
|
}
|
|
2320
|
+
// Remove from activeToolCalls so stale entries don't accumulate.
|
|
2321
|
+
// Previously only cleared wholesale in finalizeStreamingMsg/clearChat/etc,
|
|
2322
|
+
// which meant a missed tool_execution_end left tools stuck in "执行中…" forever.
|
|
2323
|
+
state.activeToolCalls.delete(obj.toolCallId);
|
|
2288
2324
|
}
|
|
2289
2325
|
break;
|
|
2290
2326
|
}
|