Repository navigation
v0.46.1
[0.46.1] - 2026-10-16
Highlight: Auto-Yes と応答 poller の「待ちの間に止まった・作り直された tick」が古い世代のまま書き込む問題をまとめて塞いだ修正リリース。Auto-Yes は tick ごとに世代を取って照合し、途中で止めた回答を覚えて同じ prompt への再入力(
yy)を防ぎ、worktree ID の移行でもその記憶を移す。あわせて Esc が前面の層から 1 層ずつ閉じるよう残りの受け手をそろえ、チャット検索で文字参照の全体ヒットが強調されない問題を直した。
Fixed
-
fix(auto-yes): worktree ID の移行で途中送信の記憶を移し、理由語
partial-answerを型に入れる (#3713): #3706 の「途中で止めた回答の記憶」({画面, session epoch})は worktree ID の移行で新しいキーへ移らず、部分入力が残ったまま移ったセッションに新しい poller が同じ prompt を再入力しえた(yy)。poller はsendPromptAnswerの await の前に送信の試行(画面・記録先のキーと epoch)を registry に登録し、帰ってきた tick は登録した試行へ結果を確定する(キーが届いた後の中断→その時点の記録先へ途中送信を記録、未送信・完了→自分の試行だけ外す。間に await を挟まない)。reconcileWorktreeSessionsは最初の rename の await の前に移送元の session epoch を控え、二段の rename がどちらも成功した session だけ(stage 2 のfalseは完了に入れず skipped に出す)、epoch が控えた値と同じ記憶・試行を新しいキーへ一括で付け替える(全移送元を先に退避するので入れ替え・連鎖でも各記憶は自分の session に一度だけ付き、成功した移送先の古い記憶は置き換え、拒否・失敗・session 不在・他サーバー所有・一覧の失敗では移送先を変えない)。新しいキーの poller は、移ってきた未完了の試行と同じ画面には送らない(旧 tick の帰りより新しいキーの最初の tick が先に来ても再入力しない)。作り直し(epoch の更新)・worktree の削除・全体停止の後に帰ってきた旧 tick は記憶を復活させない。disable→enable では(agent を指定しない全 agent の disable でも)保護を保ち、遅く帰った旧試行は、後から始まった送信が同じ記録先に記録した別 prompt の記憶を上書きせず(試行の登録順の通し番号で判定)、移送の照合で外れた移送元の新しい世代の記憶は消さない。poller が prompt の消失で記憶を忘れるときはその記録先の未完了の試行(移ってきたものも同じキーのものも)をすべて、別の prompt が出て記憶を忘れるときは忘れた記憶と同じ画面の試行だけを失効させ(今の画面へ送信中の試行は保護と帰りの記録を保つ)、遅く帰った旧試行が記憶を復活させて次の同じ確認に答えなくなることを防ぐ(忘れた後に始まった試行は従来どおり記録する)。Enter の記録・epoch・許可・応答 poller・WS の移送は今のまま。あわせて、入れ替え A↔B で両方の Auto-Yes poller が動いているときに B 側の poller が消えていた問題を、全旧 poller を止めてから全後継を始める順にして直した。partial-answerをAutoYesSuppressionReasonに入れて cast を外し、cmate waitはこの理由に「because an earlier answer left partial input; Auto-Yes is waiting for a human」と出す(exit 10・猶予・JSON は不変)。 -
fix(timer): 予約送信のキャンセルが成功したのに送信される問題を修正 (#3707):
executeTimerが session の確認(isRunning)を待つ間にキャンセルが成功しても、戻った後のsendingへの更新がcancelledを上書きして送信していた。待ちの後に状態を同期的に読み直し(await を挟まないので更新と原子的)、pendingでなければ送らず状態も変えない。no-session の更新も同じ読み直しの後ろに置き、キャンセル済みをno_sessionに書き換えない。送信を始めた後のキャンセルを拒む既存の動きは不変。 -
fix(auto-yes): 待ちの間に止めた・作り直した Auto-Yes の tick が、旧い世代のまま回答・再予約しないようにする (#3706): Auto-Yes の poller は tick の冒頭で一度
validatePollingContextを見るだけで、capture・所有権確認・Enter fallback の再読・送信関数の中の待ち(ガードの画面読み・キーの間の待ち・Enter 前の待ち)の後に「まだ同じ世代か」を見ていなかった。そのため Auto-Yes を切った直後でも待ちの間に始まっていた tick が 1 回は回答を送り、in-flight の間に disable → enable すると旧 tick がscheduleNextPollでキーから state を引き直して新しい poller のtimerIdを上書きし予約が 2 本残っていた。tick の冒頭で 世代(startAutoYesPollingが作る poller state そのもの・auto-yes-stateの許可オブジェクト・CLI セッションの epoch)を取り、キーで引き直さず同一性で照合する。capture の後・各待ちの後・再予約の時に照合し、外れた tick は何も変えずに終わる(自分の poller が今も現役のとき=許可の更新・セッションの作り直しだけは鎖を絶やさないよう再予約する)。送信関数sendPromptAnswerとsendSpecialKeysには省略可能な照合関数(stillCurrent/shouldContinue)を足し、偽になった時点で以降のキーを送らずPromptAnswerAbortedError/SpecialKeysAbortedErrorで止める(渡さない手動の送信・他の呼び手の振る舞いは不変)。途中で止めた送信は回答としても error としても数えず、送り終えた回答は監査行と task event を残したまま、poller の状態(重複判定・error count・最終応答時刻)は tick 自身の世代にだけ書き、セッションが作り直されていれば応答 poller の再開と画面配信をしない。 送信の await 中に CLI セッションが作り直された場合は、旧回答の監査行だけを残し(recordAnsweredPromptの新しい引数pendingSavedAtOrBeforeで tick 開始より後に保存された新セッションの pending 札を採らない)、重複判定キー・task のprompt_answered_auto・最終応答時刻は更新しない。Enter fallback の記録は session epoch を持ち、停止・epoch の変更の後に旧送信の帰りが記録を作り直さない(getLastEnterFallbackも別 epoch の記録を返さない)。キーを 1 つ以上送ってから止めた回答は composite key ごとに覚え、同じ session の同じ prompt には次の tick 以降も再入力しない(yyを防ぐ。prompt が画面から消える・別の prompt に変わる・セッションが作り直されると忘れる)。 応答 poller・画面配信の再開は動的 import を待った後に session epoch を読み直してから行う。途中で止めた回答の記憶は prompt の画面識別(type・question・選択肢・カーソル記号を除いたapprovalTarget)で照合し、同じ質問文の別の選択肢・別コマンドは抑止しない(カーソル位置だけの変化は同じ prompt)。人に任せた prompt はlastSuppressionに理由語partial-answerで載せ、prompt の Push 判定(policy-withheld)とcmate waitが「Auto-Yes が回答中」と扱わないようにする。 -
fix(polling): 退役した応答 poller の tick が待ちの後に共有の状態・DB・配信・Push を書き換える問題を修正 (#3705): #3663・#3684・#3701 は見つかった書き込みに 1 か所ずつ
mayActForPollerKeyを足してきたが、tick は待ち(await)の後に自分の鎖が止まっていても、確認待ちの札を「回答済み」にする掃き出し(markPendingPromptsAsAnswered+message_updated)、待ちの episode を閉じるobserveWaitingEdge(false)、行を保存しなかったのに出る「完了」の Push、カーソル(updateSessionState)、障害の Push、TUI の蓄積、skip streak、#2399 の再問い合わせの settle を古いキーのまま書いていた。checker は待ちの点ごと(セッション確認・保留の期限切れ・ジオメトリ委譲・キャプチャ・転写リーダー・Markdown ログ・再問い合わせのリーダー)に所有を問い直し、拒否された tick はそれ以降を書かない(キャプチャの後は読みだけ続け、すでに自分で問う書き込み — prompt 行・応答ハッシュ・保留・即時保存 — の拒否ログは従来どおり出る)。4 つの転写リーダー(claude / codex / antigravity / command-code)、共通の grow、recordUserTurn(新しい結果refused)、転写のカーソル前進、agy の追跡リストも、最後のawaitの後・書き込みの直前に同じキー付きの判定(mayActForAgentInstance)を置く。Markdown ログは poller から呼ぶときだけキーを渡し、ログ自身のawaitの後・書き込みの直前に判定する(停止時に確定する保留の行のログはキー無しで従来どおり残る)。core: 待ち合わせの再起動は、それを引き受けた tick だけが消費する(60 秒超で切り離された旧 tick が後継の後ろに並んだ再起動を始め、実行中の後継を止めていた)。新しい鎖は停止済みの前の鎖もsupersededにする。snapshot は owner をsnapshotOwnerStorageで運び、broadcastTerminalSnapshot・buildCurrentOutput(status latch・未分類フレーム行・構造化 prompt 行)・chat-turn progress の各awaitの後にも後継の有無を見る(後継の無い停止時の最終 snapshot は従来どおり)。progress の本文・turnKey・version は配信できたときだけ記憶する(配信を拒否した本文が後継の同じ本文を抑止しない)。退役した tick が後継の登録した応答ハッシュに一致しても、重複分岐(skip ログの claim・カーソル・再問い合わせの claim とリーダー)へは進まない。self-stop の結果、supersededだけの tick の保存・hold・通知、paused の再開、別キー・tick の外の Stop hook は変えない。対象関数の副作用の前に判定があり、判定から書き込みまでawaitが無いことを AST で検査するガード(tests/unit/guards/retired-tick-write-guard-3705.test.ts)を追加。 -
fix(polling): 退役した tick はプロンプト行を保存せず、転写の完了通知も出さない (#3701): #3663・#3684 の
mayActForPollerKeyの線引きを、まだ owner を見ていなかった 2 か所へ広げた。(1)savePromptMessage— ペインの capture を待つ間にstopPollingされた tick が、遅れて返った画面のプロンプトで行の保存・message配信・task event・Web Push・待ち episode を出していた。判定はプロンプトの dedup(isDuplicatePrompt)より前に置くので、拒否された tick はハッシュを登録せず、後継の poller は同じプロンプトを保存できる。拒否は infoprompt-save-refused。(2)captureStructuredHistoryTurn— reader のawaitの間に退役した tick は relay の完了通知(onRelayTurnCompleted)を出さず、claimCompletionAnnouncementも取らない(後継が同じ completion を通知できる)。判定のキーはgetPollerKeyと同じworktreeId:instanceId、拒否は infostructured-history-completion-refused。戻り値と report は reader の結果のまま。Stop hook(tick の外)からの呼び出しと、supersededだけの tick は従来どおり保存・通知する。reader の中で行われる書き込み(転写行の追加・更新、カーソル)はこの修正の対象外で、退役した tick でも走りうる。 -
fix(ui): PC のファイルパネルの検索欄と共通の Modal も Esc を前面の層から 1 層ずつ閉じる (#3700): #3691 で FileViewer の検索欄だけがそろっていた Esc の層の規則を、残る 2 つの受け手に広げる。PC のファイルパネルの検索欄(
FileSearchBar)は Esc で閉じるときにmarkEscapeClosedLayerで押下に印を付けるので、最大化したファイルで検索欄を開いて Esc を押すと検索だけが閉じ、最大化は次の Esc で解除される(従来は 1 回で両方閉じた)。viewport layer の背面にある検索欄は閉じず印も付けない(FileViewer の検索欄と同じ形。最大化していないパネルで同じパネルのエディターを CSS 最大化しているときも背面として扱い、Esc はエディターの最大化だけを解除する(isUnderViewportLayer)。背面判定isBehindViewportLayerはsrc/lib/escape-key.tsへ移して両者で共有し、FileViewer の振る舞いは不変)。共通のModalの Esc はisEscapeNoLayerClosedを読み、中の層が閉じて印を付けた押下やpreventDefaultした押下では閉じない(FileViewer のモーダル表示で検索欄を閉じる Esc で、モーダルまで一緒に閉じなくなる)。印もpreventDefaultも無い Esc では従来どおり閉じ、disableCloseの振る舞いは変えない。 -
test(search): Mermaid の図を含む返答で Markdown の写像が無いときの図の外のヒットの置き方をテストで固定 (#3692):
placeRawPositionsはmapMarkdownPositionsToDomが null(容器にdata-search-rawの要素が 1 つも無い)を返すとき、Mermaid の置き方の結果(図のソースの中は図の位置、図の外は区間ごとの序数の照合)をそのまま使う。段 1 の結論は (b) 振る舞いを変えない: 図の外を null にする案 (a) は #3503・#3525・#3544 の既存テスト 5 本の期待(印の無い DOM で図の外の語が自分の語に置かれる)を変えるため採らなかった。tests/unit/lib/terminal-highlight-mermaid-outside-3692.test.tsで、印が無いとき図のソースの中のヒットは図の中に・図の外の素の語は自分の語に置かれ、画面に無い原文(**alpha**の*alpha)は置かれないこと、印があるときは図の外を Markdown が置くこと(文字参照の前では序数の照合と結果が違う)を固定。コードの変更は無い。 -
fix(ui): FileViewer の全画面・札の中の Esc・スマホの inline の理由も、Esc を前面の層から 1 層ずつ閉じる (#3691): #3681 の規則(背面の層は押下に触れない/前面で閉じた層は
markEscapeClosedLayerで印を付け、下のモードはisEscapeNoLayerClosedで印を読む)に残り 3 つの受け手をそろえた。FileViewer の全画面(読み取り専用の overlay の window の Esc と、統合画面の Escape の段)は前面の層が閉じた同じ押下では解除・クローズせず、どちらの画面もdata-viewport-layerを持つので、背面の札の理由・2 段目の「…」パネルは最初の Esc を画面に譲る。全画面の検索欄の Esc も印を付け、検索欄と全画面が 1 回で同時に閉じない(検索欄が埋め込みエディターの最大化など viewport layer の背面にあるときは、閉じず印も付けず押下を前面に譲る)。札の中にフォーカスがあるときの Esc(handleRootKeyDown)は document の経路と同じ背面判定を通し、背面なら理由を閉じず押下も claim しない(最大化したエディターの背面で理由だけ閉じて最大化が残る逆順を解消)。スマホの inline の理由は z-index を class のz-50から同じ値の inline style へ移して背面判定と印を付け、CSS 最大化したエディターや FileViewer と 1 回の Esc で同時に閉じない。 -
fix(polling): 退役した tick が即時保存の分岐で古いキーのまま返答を保存・配信・完了通知する問題を修正 (#3684):
recordOrHoldScrapedResponseの保留しない分岐(転写リーダーが何も返さず画面の返答をその場で保存する経路)は呼び手の owner を見ておらず、リーダーや Markdown ログのawaitの間に鎖が停止・60 秒超の再起動・worktree ID の移行で退役しても、createMessage→broadcastMessage('message')→onRelayTurnCompletedを失ったキー(旧 ID)のまま行っていた(#3663 は保留の倉庫とハッシュの登録だけを塞いでいた)。この分岐の直前で保留と同じmayActForPollerKeyを判定し、停止した鎖の tick が自分のキーへ保存するときは 3 つとも行わずscraped-response-save-refusedを info で残す。判定と 3 つの間にawaitは無い。保留の分岐も、保留の倉庫が退役した tick の hold を拒否したときはonRelayTurnCompletedを出さない(受理したときだけ通知する)。保証は DB 行・配信・relay 通知に限り、その前の Markdown のログ保存と呼び手の状態更新は従来どおり。supersededだけの tick(60 秒未満の待機つき再起動)・prompt の pause からの再開・停止した tick の中から行う別キーの操作(relay 配達からの再起動など)は従来どおり保存できる。多対一の移行(項目 1)と上書きされた timer(項目 2)は変えていない。 -
fix(verification):
env-cleanが clean のとき、兄弟の worktree に帰属して除外した追加の名前を出さない問題を修正 (#3683):(N addition(s) attributed to another worktree)の件数だけで項目名が出ず、logTailから何を除外したか確かめられなかった。clean のレポートにignored (attributed to another worktree): <名前, ...>の 1 行を足し、ignored (built-in): …/ignored (options.envCleanIgnoreHomeEntries): …と同じ形にそろえた。判定・exit code・VIOLATED のときの列挙は変わらない。 -
fix(search): 1 文字や空白を描く文字参照(
<・  など)の全体にヒットしたとき、描いた範囲を強調する (#3682): チャット(Chat 面・History)の検索で、</&のように 1 文字を描く参照、  (U+205F・U+200A)のように空白を描く参照の全体にヒットする検索語が、件数には入るのに強調されなかった問題を修正。#3671 は 2 単位以上を描く参照(fj)だけを;で終われるようにしており、1 単位の参照は終端の写像(rawEndToDom)に載らず、空白を描く参照は描いた空白が原文の空白にしか置かれないため&にも置かれていなかった。mapMarkdownPositionsToDomは、1 つの&に置かれた単位がそこの参照の描いたものと一致すれば単位数にかかわらず;で終わるヒットを描いた範囲の後ろまで届かせ、alignSubsequenceは空白を描く参照の空白を(原文の空白より前にあれば)その&に置くようにした。参照の途中で終わるヒット(置かない)、<の&だけのヒット(<)、参照から後ろの語までのヒット、インラインコード内の参照(書かれたとおり)の動きは変えていない。 -
fix(ui): 札の理由とエディターの最大化も、Esc を前面の層から 1 層ずつ閉じる (#3681): #3670 の規則(背面の層は押下に触れない/前面で閉じた層は印を付ける/下のモードは印を読む)に、残っていた 2 つの層をそろえた。PC の札の理由(
VerificationStatusChipのpopover="floating"、<body>直下のZ_INDEX.MODAL=50)は、最大化したファイル(data-viewport-layer、55)に覆われているあいだisCoveredByViewportLayerで背面と分かれば Esc に触れず、1 回目で最大化だけが解除され、見えるようになった理由が 2 回目で閉じる(以前は 1 回で両方が閉じ、解除後の画面では理由が消えていた)。前面で閉じたときはmarkEscapeClosedLayerで押下に印を付ける。判定のため理由の z-index を class のz-50から同じ値の inline style に移した(見た目は不変)。MarkdownEditorの最大化の解除(textarea と document の 2 経路)はisEscapeNoLayerClosedで印のある押下を見送り、前面の浮遊層が閉じた同じ Esc では解除しない(浮遊層が無いときは今どおり 1 回で解除)。最大化中のエディター自身にもdata-viewport-layerを付け(CSS フォールバックのfixed inset-0portal と、ネイティブの Fullscreen API の top layer の両方)、背面にある札の理由・2 段目の「…」パネルが最初の Esc をエディターに譲る。isUnhandledEscapeの意味は変えていないので、他の受け手の挙動は不変。 -
fix(test): 設定モーダルのフォーカス移動テストが高負荷で落ちる問題を修正 (#3674):
Sidebar-settings-modal-2709.test.tsxとSidebarRail-settings-modal-2709.test.tsxで、モーダルが現れた直後にdocument.activeElementを同期で比べていたため、メニューが閉じてフォーカスがトリガーへ戻ってからモーダルへ移るまでの間に負荷で落ちていた。フォーカスの確認をwaitForで待つ形に直した(期待は弱めていない)。 -
fix(verification):
env-cleanが組み込みの除外(zsh の補完のキャッシュ)を設定で除いたものとして表示する問題を修正 (#3672):~/.zcompdump/~/.zcompdump.<host>.<pid>をignored (options.envCleanIgnoreHomeEntries): …の行に出していたため、設定に書いていない項目が設定で除いたと案内されていた。組み込みの除外はEnvProbeDiff.ignoredBuiltInに分け、ignored (built-in): …の別行に出す。設定に書いた項目は従来どおり設定の行。判定(違反になるか)は変わらない。 -
fix(search): 2 文字を描く文字参照(
fjなど)の全体にヒットしたとき、描いた文字すべてを強調する (#3671): チャット(Chat 面・History)の検索で、原文のfjの 7 文字全体にヒットする検索語(例fj)が強調されなかった問題を修正。原文→DOM の写像は原文の位置ごとに最初の描画単位だけを持ち、参照の最後の;にはどの単位も結びつかなかったため、;で終わるヒットが置けず null になっていた。mapMarkdownPositionsToDomにヒットの終端用の写像(rawEndToDom)を足し、1 つの&に 2 単位以上が置かれた参照(fj→fj、𝔄→ サロゲートペア)は;で終わるヒットが描いた単位すべての後ろまで届くようにした。参照の後ろの普通の語(#3661 の直し)、&だけのヒット(最初の 1 文字)、名前の途中で終わるヒット(置かない)、1 文字を描く参照(<の全体は従来どおり置かない)、インラインコード内の参照(書かれたとおり)の動きは変えていない。Mermaid の図を含むメッセージでは、図の外のヒットも mermaid 側の写像(原文の文字列の n 回目を画面の文字列の n 回目に対応させる)で置かれていたため、fjは画面に無く強調されず、通常文とインラインコードに同じ参照があると 2 件が同じコード内に重なって置かれていた。図の外のヒットは図の無いメッセージと同じ Markdown 側の写像(今回の修正)で置き、mermaid 側の写像は図のソース上のヒットだけに使うようにした(図の中のヒットの動きは不変)。 -
fix(ui): 2 段目の「…」パネルと最大化したファイルが開いた状態の Esc が、1 回で両方を閉じる問題を修正 (#3670): 「…」パネル(
ActionBarItemsOverflow)は document の Esc で閉じるがpreventDefault()しないため(札の理由の document listener が同じ押下で閉じる必要がある、#3660)、window で Esc を受ける最大化の解除(FilePanelContent)にも同じ押下が届き、1 回の Esc で 2 層が閉じていた。Esc は前面の層から閉じる。両方とも<body>直下の portal で、最大化したファイル(fixed inset-0、Z_INDEX.MAXIMIZED_EDITOR=55)が「…」パネル(Z_INDEX.MODAL=50)より前面にあるので、パネルはisCoveredByViewportLayer(src/lib/escape-key.ts、data-viewport-layerの層と実際の z-index・文書順を比べる)で背面と分かれば押下に触れず、1 回目で最大化が解除され、見えるようになったパネルが 2 回目で閉じる。パネルが前面にあるときは閉じた押下に印を付け(markEscapeClosedLayer、イベントごとの WeakSet)、最大化の解除はisEscapeNoLayerClosedで印のある押下を見送る。パネルの z-index は判定のため class のz-50から同じ値の inline style に移した。isUnhandledEscapeの意味は変えていないので、他の受け手(札の理由・アクティビティペイン等)の挙動は不変。 -
fix(polling): worktree ID の一括移行で保留中の画面読み取りが置き去りになり、退役した tick がそれを書き換える問題を修正 (#3663):
migrateResponsePollerWorktreeIdsは poller と dedup の状態を新しい ID へ移していたが、#2436 の保留(__pendingScrapedResponses)は旧キーに残り、確定されても旧 ID のままなので外部キーで失敗して返答が消えていた。保留は全移送元を先に退避してから移送先へ置き(入れ替え・連鎖・paused も)、本文・期限・timestamp・付帯情報を保って worktreeId だけ新しい ID にする。移送先で既に動いていた鎖は止め、その保留は今の ID で先に確定する。解決できずに止める鎖の保留は新しい ID で確定する。あわせて保留の倉庫は、呼び手の tick が退役(stopped:停止・60 秒超の再起動・移行)していれば自分のキーへの作成・本文更新・削除・確定を拒否し(別セッションへの操作、たとえば relay 配達からの再起動の確定は通す)、期限の settle は await の後に呼び手と「同じキーの同じエントリー」を確かめてから discard / flush する。通常の待機つき再起動(supersededだけ)の tick は従来どおり hold でき、再起動時の停止で確定される。保留だけ(鎖も pause も無い)を移すときも移送先の鎖を止めて既存の保留を確定する。移行の時点で tick が走っている鎖(runningの記録の owner がそのキーの今の owner と同じもの)は、移送元のキーの上でだけ応答のハッシュを捨ててから一括で移す(多対一の移行で別の移送元から届いたハッシュは残る)(その tick はハッシュを登録した後で保存か保留かがまだ決まっておらず、移すと後継が未保存の返答を重複と見て取りこぼす)。後継は同じ画面を自分で判定して保留・保存する。代わりに、移行の瞬間にその tick が保存を終えていた返答は、後継がもう 1 行保存しうる(欠落より重複を取る、#2436 と同じ判断)。tick が走っていなかった鎖・paused の鎖は従来どおりハッシュを移し、画面の返答を二重保存しない。停止した鎖の tick は自分のキーへ応答のハッシュを登録しない(保留の倉庫と同じ判定)。prompt のハッシュと prompt-skip の扱いは変わらない。