完成時氣泡改顯示最後一句回覆,不再延到下一輪才出現

根本原因:
Stop 事件時 main.js 已從 transcript 附上最後一句助理訊息(narration),
但 renderer 的 Stop 分支寫死顯示「完成!」沒使用它,也沒把 narrationId
標記為已顯示;下一輪第一個工具事件呼叫 sayActivity 時,transcript 裡
最新的助理訊息仍是上一輪結尾、id 又沒被消費過,就被當成新話顯示出來。

影響:
對話收尾只看得到「完成!」,看不到 AI 的最後一句回覆;等使用者送出
下一句指令後,寵物反而冒出上一輪的舊訊息,時序錯亂。

修法:
Stop 時若帶有 narration,氣泡直接顯示「 最後一句」並消費 narrationId,
沒有才退回「完成!」;UserPromptSubmit 也先把當下 narrationId 標記為已
顯示,雙重保險。另修 release.js 只挑檔名含當前版本的產物,避免 dist/
殘留的舊版檔案混進新 release。版號 2.0.9。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 16:40:32 +08:00
co-authored by Claude Fable 5
parent 7060571df1
commit 4e4563e861
4 changed files with 15 additions and 5 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "claude-pet", "name": "claude-pet",
"version": "2.0.8", "version": "2.0.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "claude-pet", "name": "claude-pet",
"version": "2.0.8", "version": "2.0.9",
"devDependencies": { "devDependencies": {
"electron": "^43.4.1", "electron": "^43.4.1",
"electron-builder": "^26.0.12" "electron-builder": "^26.0.12"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "claude-pet", "name": "claude-pet",
"version": "2.0.8", "version": "2.0.9",
"description": "Codex Pets 相容的 Claude Code 桌面寵物(spriteVersionNumber 29 個動作列 + 16 方向追視)", "description": "Codex Pets 相容的 Claude Code 桌面寵物(spriteVersionNumber 29 個動作列 + 16 方向追視)",
"main": "main.js", "main": "main.js",
"private": true, "private": true,
+10 -1
View File
@@ -409,6 +409,9 @@ function handleEvent(p) {
break; break;
case "UserPromptSubmit": case "UserPromptSubmit":
setState(s, "running"); setState(s, "running");
// 這時 transcript 裡最新的助理訊息還是上一輪的結尾,
// 先標記成已顯示,免得接下來的工具事件把它當成新話講出來
if (p.narrationId) lastNarrationId = p.narrationId;
hideBubble(); hideBubble();
break; break;
case "PreToolUse": case "PreToolUse":
@@ -456,7 +459,13 @@ function handleEvent(p) {
// Codexturn 完成且未讀 → review(播 review 3 次後沉澱),直到使用者回到該 thread 才回 idle。 // Codexturn 完成且未讀 → review(播 review 3 次後沉澱),直到使用者回到該 thread 才回 idle。
// 這裡對應成:維持 review 直到該 session 有下一個動作(UserPromptSubmit)、結束或 7 天到期。 // 這裡對應成:維持 review 直到該 session 有下一個動作(UserPromptSubmit)、結束或 7 天到期。
setState(s, "review"); setState(s, "review");
say("✅ 完成!", 8000, false, title); // 收尾時把最後一句回覆講出來,而不是只說「完成」
if (p.narration) {
lastNarrationId = p.narrationId || lastNarrationId;
say(`${firstSentence(p.narration)}`, 8000, false, title);
} else {
say("✅ 完成!", 8000, false, title);
}
break; break;
case "StopFailure": case "StopFailure":
// Codexturn 失敗 → failed 狀態(播 failed 3 次後沉澱),維持到下一個 turn 或 1 小時到期 // Codexturn 失敗 → failed 狀態(播 failed 3 次後沉澱),維持到下一個 turn 或 1 小時到期
+2 -1
View File
@@ -144,8 +144,9 @@ async function main() {
console.log(`Tag : ${TAG}`); console.log(`Tag : ${TAG}`);
if (!fs.existsSync(DIST_DIR)) throw new Error(`找不到 ${DIST_DIR},先執行 npm run dist`); if (!fs.existsSync(DIST_DIR)) throw new Error(`找不到 ${DIST_DIR},先執行 npm run dist`);
// dist/ 可能留著前幾版的產物,只挑檔名含當前版本的,免得舊檔混進新 release
const files = fs.readdirSync(DIST_DIR) const files = fs.readdirSync(DIST_DIR)
.filter((f) => ASSET_PATTERNS.some((re) => re.test(f))) .filter((f) => ASSET_PATTERNS.some((re) => re.test(f)) && f.includes(pkg.version))
.map((f) => path.join(DIST_DIR, f)) .map((f) => path.join(DIST_DIR, f))
.filter((f) => fs.statSync(f).isFile()); .filter((f) => fs.statSync(f).isFile());
if (!files.length) throw new Error(`${DIST_DIR} 裡沒有 .exe / .zip,先執行 npm run dist`); if (!files.length) throw new Error(`${DIST_DIR} 裡沒有 .exe / .zip,先執行 npm run dist`);