Repository navigation
v0.47.0
[0.47.0] - 2026-10-21
Highlight: Auto-Yes が答えずに止まった確認を、CLI から画面を確かめたうえで直接答えられるようになりました(
commandmate promptで 1 回観測し、respond --expect-screen <kind> --observation <token>で観測した画面にだけ答える)。あわせて Auto-Yes の安全側の修正をまとめました。agy のログイン方法の画面や footer の切れた画面に番号で答えない、Submit / Cancel の確認画面に答えない、hook 受信の窓(8 秒)と tick の競合で約 4 時間止まっていた問題(#3767)を直すなど。サーバーの再起動で外れた Auto-Yes を「再起動で解除」と表示し、start --daemonのサーバーの出力をlogs/server.logに残すようになりました。
Added
-
feat(cli): 直近の send が「未送信/送信済み/不確定」のどれかを
prompt --jsonのlastSendと send の失敗メッセージで示す (#3759): 起動の窓が時間切れになって最初の message が届かなかったとき(#3746:Antigravity prompt not ready)、その message が未送信なのか送信済みなのか不確定なのかを示すものが無かった。sendUserMessageが (worktree, tool, instance) ごとに直近の試行の結末をプロセスのメモリ(globalThis、上限 1024 件)へ記録する新モジュールsrc/lib/session/send-attempt-state.tsを追加し、記録はat・state・reason・messageDigest(本文の SHA-256 の先頭 16 桁のみ。本文は保存しない)を持つ。stateはnot-sent(本文のキーを 1 つも打つ前に失敗:prompt-not-ready/session-starting/prompt-waiting/foreign-session/model-switch-failed/composer-not-empty)、sent(submit を確かめた)、indeterminate(一部を打った後に失敗、または結果を確かめられない:body-not-landed/replaced-by-completion/submit-unconfirmed、submit を確かめられず claude / codex の composer にまだ本文が見えるときはcomposer-residue、それ以外の未知の失敗はsend-error)。未知の失敗をnot-sentにしない(「分からない」を「再送してよい」と読ませない)。commandmate prompt --json(#3755)の payload にlastSend: { at, state, reason }を足し(記録が無ければ null、古いサーバでは項目なし。digest は公開しない)、人向けの表示にもlast send:行を出す。send ルートの 500 の失敗メッセージ(prompt not readyなど)の末尾に[send state: not-sent (prompt-not-ready): …]を足す(同じ本文で、この要求の開始後に作られた記録のときだけ)。ルートが send サービスの前で断る試行(セッション起動の失敗・他サーバのセッション)もnot-sentとして記録する。どの状態でも自動で再送しない: 再送するのは利用者/監督のエージェントで、indeterminateのときはcapture --paneで画面を確かめる前に再送しないよう docs(ja / en の cli-operations-guide)に書いた。記録は try/catch の内側で行い、記録できなくても send の結果(SendUserMessageResultの形を含む)は変わらない。 -
feat(cli):
respond --expect-screen <kind> --observation <token>— 観測した画面にだけ答える厳格モード (#3756):commandmate promptの観測トークンを付けて答えると、サーバが送る直前に取り直した画面で kind・トークン(pane・session 世代・画面の出現・選択肢・選択中の位置)・信頼の確認のフォルダ(--expect-target、省くと何も送らず画面のフォルダを返す)・答えの選択肢・画面が pane の最下部で開いていること(生成中の返答に引用された信頼の確認には答えない)を確かめ、どれかが合わなければ何も送らず exit 40 と観測した kind を返す。専用のPOST /api/worktrees/:id/prompt-response/strictへ送り、endpoint の無い古いサーバでは止まる(照合の無い/prompt-responseへ fallback しない)。答えるのはantigravity-trustとantigravity-permissionだけで、番号の無い信頼の確認は画面に見えている選択肢の label の一意一致(Yes→Yes, I trust this folder)を ↑/↓ と Enter として送り、番号・--default・同名や複数に当たる label・画面外の選択肢は拒否する。キーは 1 つずつ送り、矢印の後に画面を取り直して同じ画面のまま選択が動いたことを確かめてから次を送る。送信を始めた後の部分送信・結果不確定は exit 41(再送してはいけない)、Enter の後はoutcome(advanced/next-prompt/session-ended/unchanged/indeterminate)を返し、成功は前の 3 つだけ。対象 instance の Auto-Yes が ON の間はantigravity-permissionを厳格には答えない(exit 40auto_yes_active。先にauto-yes --disable)。トークンは送る前に使い切り、同じ instance の厳格な送信はサーバ内で直列化、--operation-idは worktree・instance ごとに要求の中身と一緒に覚え、キーを送った操作の同じ再送は覚えている結果を返して二重に送らず、中身の違う再利用はoperation_id_conflict(exit 41)、exit 40 の結果は覚えない。接続できなかった要求は exit 40(server_unreachable)。起動処理(#3757)の「Enter 1 回の借り」とは、厳格な送信の間は借りを取らせず、信頼の確認に答えたら借りを捨て、起動の窓が開いている間・借りが取られた直後は答えないことで二重に答えない。誰が・どの画面に・どの選択肢を送ったかを server.log(strict-prompt-answered/strict-prompt-response-refused)と task の履歴(prompt_answered_human)に残す。--jsonは stdout に結果オブジェクト 1 個。auto mode の許可を得る手段ではない。 -
feat(cli): 止まっている画面を機械で読める形で 1 回観測する
commandmate prompt <wt> --instance <id> [--json]と観測トークンを追加 (#3755):GET /api/worktrees/:id/current-output?observe=1が同じ capture からpromptObservation(schemaVersion付き)を返し、promptはそれを表示する。payload は解決済みの対象(instance・pane)、観測時刻、session 世代、画面の kind・観測トークン・質問・承認対象(信頼確認ならフォルダ)・選択肢・現在の選択、回答経路、Auto-Yes の有効状態と poller の状態。kind は新しい共通の語彙(src/lib/prompt-observation/kinds.ts:antigravity-trust/antigravity-model-picker/antigravity-permission/antigravity-slash-menu/composer/none/unknown)で、promptData.typeや agy のantigravity_selection_list(信頼確認・picker・slash メニューをまとめる)とは混ぜない。agy の信頼確認・picker・slash メニューは新しい読み取り(detection/tools/antigravity/screens.ts)で区別し、番号付きの許可は既存のdialog.tsを使う。計測していない画面はunknown。観測トークン(po1.<発行時刻>.<HMAC>、120 秒、プロセスごとの鍵)は instance・session 世代・pane・画面の出現(サーバが instance ごとに覚えるappearedAt。別の画面・composer・none を観測すると捨てる)・kind・質問・選択肢・承認対象・選択中の位置に結び付き、使い捨て。unknownと session 世代が判らないときは発行しない(tokenWithheldに理由)。検証の入口verifyObservationToken()/consumeObservationToken()/observationBindingOf()を後続の厳格なrespond(#3756)のために export した(この変更ではrespondは変えない)。観測と観測の間に消えて同じ文面で再び出た画面は同じ出現と見なす。観測が失敗してもcurrent-outputはpromptObservationを付けずに従来の body を返す。「答えなかった理由」は 2 項目に分けた: poller の実際の記録(pollerRecord、画面との対応screenCorrelationは記録が画面を名指ししないためunknown/type-mismatch/no-prompt-on-screenのどれか)と、契約の方針をいま当て直した結果(contractEvaluation、理由はAutoYesSuppressionReasonの語のみ)。記録が無いことを「回答可能」とは扱わない。取得できたら確認が無くても exit 0、セッションが無いときは exit 21。waitは poll にobserve=1を付け、exit 10 の payload に同じobservationを足す(exit code・待機・猶予・autoYesSuppressionは不変、古いサーバでは付かない)。 -
feat(slash-commands): codex 0.163.0 の
/configをスラッシュコマンドのカタログに追加 (#3722):openai/codexのslash_command.rsを rust-v0.161.0 と rust-v0.163.0 で比べ、SlashCommand::Config(説明 "configure Codex preferences")の追加を確認(集合は 61 → 62)。attestation の codex を 0.163.0 の読み取りから書き直した。
Changed
-
docs(claude-md): CLAUDE.md を実態に合わせて更新し CLI 節を圧縮 (#3737): 技術スタックの版を major のみに(Next.js 15 / React 19 / Tailwind CSS 4。AGENTS.md も同様)、ファイル構成ツリーを主要ディレクトリだけに絞って存在しない
components/auth/を除去し tests にe2e/などを追記、コマンド表に/orchestrateほか 7 本を追加、CLI 節を bash ブロックから事故防止の規則の箇条書きへ圧縮(20,817→約 15,300 bytes)、「Server Components 優先」を「必要なときだけuse client」へ、必須チェック節を.commandmate/verify.yaml参照とサイズ guard 2 つに整理、本文のIssue #N履歴表記を除去、pm-auto-dev.mdのファイル構成更新条件を「トップレベルの増減時のみ」に変更。 -
refactor(search):
terminal-highlight-markdown.tsを 510 行から 152 行に分割 (#3698): 文字参照(CHARACTER_REFERENCE〜referenceAt)をterminal-highlight-markdown-reference.tsへ、alignSubsequence・mapMarkdownPositionsToDomとそれだけが使うdata-search-rawの読み戻し(parseAttr・parseSpans)をterminal-highlight-markdown-align.tsへ移した。SEARCH_RAW_ATTRとRawSpanは循環を避けるため align 側で定義し、元のモジュールはSEARCH_RAW_ATTR・mapMarkdownPositionsToDomを再 export する(既存の import 元は無変更)。依存は 本体 → align → reference の一方向。振る舞いは不変で、分割先 2 本に単体テストを追加した。 -
refactor(polling):
migrateResponsePollerWorktreeIdsを段ごとの小さな関数に分け、複雑度を 34 から 6 に下げる (#3697): 改名対象の収集・稼働中ポーラの振り分け・旧キーからの持ち上げ(Phase 1)・一時停止中チェーンと保留スクレイプの行き先の収集・行き先を占有していたチェーンの停止・新 ID での再始動(Phase 2)をresponse-poller-core.ts内の非公開関数に移した。処理の順序・ログ名・コメントは変えず、関数は同期のまま(awaitは足していない)。公開 API と import 元は変わらない。 -
refactor(verification):
scope-gate.tsの glob 照合と log_tail の書式を別モジュールへ分割 (#3647): 895 行に増えていたsrc/lib/verification/scope-gate.tsを 617 行に縮めた。globToRegExp〜ScopeMatcherと、それが使うALWAYS_ALLOWED_PREFIX/SCOPE_EXEMPT_ALWAYS_ALLOWED/SCOPE_EXEMPT_CONTRACT_PATH/ScopeDecisionを新しいscope-gate-glob.tsへ、formatPatterns〜formatOrchestratorExcludedと、それが使うMAX_REPORTED_VIOLATIONS/SCOPE_PATTERN_ARROW/ScopeAdmissionを新しいscope-gate-format.tsへ、コメントごと書き換えずに移した。scope-gate.tsは移した公開名をすべて同じ名前で再 export するので import 元は変わらず、新しいモジュールはscope-gate.tsを import しない(循環なし)。scope ゲートの判定・log_tail の文言・並びは変わらない。分割先それぞれに単体テスト(scope-gate-glob.test.ts/scope-gate-format.test.ts)を追加した。 -
refactor(ui):
MessageInputの送信ボタンとメタ行をmessage-input/の下へ分ける (#3606): 複雑度 33 → 20。送信ボタン(ComposerSendButton)と Auto-Yes・キー案内のメタ行(ComposerMetaRow)を部分コンポーネントにした。DOM・class・data-testid・props・import 元は不変で、動きは変えていない。 -
refactor(ui): サイドバーのブランチ行
BranchListItemの複雑度を 33 から 24 に下げる (#3604): 進み具合の保存と表示の保持(#3594)をフックuseWorktreeLifecycleStatus(src/components/sidebar/branch-list-item/use-worktree-lifecycle-status.ts)へ、進み具合の札とBranchRowStatusPillContextをWorktreeStatusPill(同WorktreeStatusPill.tsx)へ、行ボタンの右余白と next action 行のクラス選びをbranchRowButtonPaddingClass/branchNextActionClass(同row-classes.ts)へ移した。BranchListItem.tsxはBranchRowStatusPillContextを同じ名前で再 export し、公開する名前・props・描画結果・クラス文字列は変えない。 -
refactor(ui):
TerminalSplitContainerの状態・ハンドラ・描画をterminal-split-container/の下へ分ける (#3603): 複雑度 46 → 24、行数 1,571 → 721(#3605 を同梱)。分割先は「…」パネル(ActionBarItemsOverflow)・リサイザ・2x2 グリッドの配置・レイアウト操作・History / Files トグル・単一分割のセッション枠の部分コンポーネントと、フォーカス/選択/配置の effect・リサイズ・項目ホスト・分割ごとのハンドラのフック。ActionBarItemsOverflow/clampOverflowPanelLeft/overflowPanelMaxWidth/InstancePlacementRequest/SelectedSplitは元のモジュールから再 export し、import 元と props は不変。動きは変えていない。 -
refactor(ui):
TerminalSplitPaneContentの分岐を小さな関数へ移し、複雑度を 31 から 18 に下げる (#3569): 足元の表示判定(showPrompt/showNav/showEscapeHatch)をsrc/components/worktree/terminal-split-pane-content/pane-gates.tsのshouldShowPromptPanel/shouldShowNav/shouldShowEscapeHatchへ、入力欄の宛先表示("To: · ")を同composer-target.tsのbuildComposerTargetへ、式とコメントをそのまま移した。公開名・props・表示・送信先は不変。 -
refactor(ui):
TerminalSplitPaneの複雑度を下げる(振る舞い不変) (#3567):TerminalSplitPaneの複雑度が ESLintcomplexityの 25 を超えていたため、ヘッダの操作群(SplitActionControls)・セッション周りのチップ(SplitSessionChips)・ドラッグ&ドロップ状態(useSplitDragDrop)・バー内メモ位置(useBarNotePosition)・セレクタ title(resolveSelectorTitle)をterminal-split-pane/配下の新モジュールへ移した。分岐・文言・DOM は変えていない。 -
refactor(ui):
NewTaskDialogの表示部分をNewTaskDialogParts.tsxへ分け、複雑度を 43 から 25 未満に下げた (#3566): 一覧状態の通知・直近の宛先・リポジトリ/ブランチ/エージェントの選択・起動の通知・モデル欄・Auto-Yes 欄・失敗表示を別コンポーネントに切り出しただけで、振る舞い・文言・DOM は変えていない。 -
refactor(chat):
splitChatThinkingが節ごとの原文の行範囲lineRangesを返し、chat-search-sectionsの目印差し込みと再実行をやめた (#3524): 検索用の思考節の範囲を、見出しになりえない行を目印に置き換えた原文をsplitChatThinkingに再度通して求めていた処理を、分割の走査(scanThinkingSections)がすでに持つ[start, end)を結果に出す形へ置き換えた。body/reasoning/blocksとchatSearchSectionsの結果は変えていない。
Fixed
-
fix(cli): 厳格
respondが agy の許可の確認でカーソル以外の選択肢を選べない問題を修正 (#3812): agy の許可の確認(antigravity-permission)は承認対象(approvalTarget)を footer まで読むため、選択肢の行と>の印がそこに入っており、Downのたびにトークンの束縛と画面の出現の指紋が変わって、キーの間の照合が「画面が動いた」(exit 41screen_moved_mid_send)と判定していた(#3796 の実機 UAT 2026-10-19:No, cancelを送るとDown1 回で止まり、再試行のたびにカーソルが 2 → 3 と進んで(Persist to settings.json)に残った)。束縛と指紋は、agy のリーダが選択肢と読んだ行(承認対象の末尾の番号つきの選択肢のまとまりで、ラベルが画面の選択肢と一致するもの)の>と字下げだけをそろえた承認対象で作り、カーソルの位置はselectedPositionだけが持つ。それより上の本文(コマンドや here-document の中の1. itemのような番号つきの行とその字下げ)は 1 文字も変えない。選択肢の文言・数・質問・承認を求めるコマンドが変わったときは従来どおり 41 で止まり、信頼確認(承認対象はフォルダ)の動きは変わらない。あわせて、Enterの前で止まった結果(delivery: 'partial')には、同じ画面の最後の読み取りで分かったカーソルの位置をhighlightLeftAt({ position, label }、分からなければ null)として返し、messageにも書く(CLI の利用ガイド ja/en の--jsonの項目にも追記。永続許可の選択肢に残ったことを、人が pane で Enter を押す前に分かるようにするため。キーを戻す操作は、止まった原因の画面へさらにキーを送ることになるので行わない)。 -
fix(ui): agy のターミナル面が黒く見える問題を修正 (#3809): ターミナル面は全 1000 行の capture を渡し、antigravity は自動追従が ON で空行の圧縮も対象外だったため、末尾の約 970 行の空行へスクロールして可視の文字が 0 になっていた(headless・有頭の Chromium のどちらでも再現)。
LAYOUT_PADDING_COMPACTED_TOOLS(src/config/terminal-display-compaction.ts)に antigravity を加え、PC のターミナル面・/sessionsのタイル・スマホが同じ設定を通って末尾の空行を落とす(表示のみ。状態検出・Auto-Yes・保存用の生の capture は不変)。claude / codex / opencode / copilot の表示は変えない。 -
fix(detection): agy のログイン方法の画面に Auto-Yes が番号で答えないようにする (#3808): agy 1.4 の未ログイン画面(
Select login method:の下に> 1. Google OAuth/2. Use a Google Cloud project、footer↑/↓ Navigate · enter Select)は番号付きダイアログの構造判定を通り、agy の読み取り(readAntigravityNumberedDialog)がmultiple_choiceのプロンプトとして読んでいたため、状態判定はprompt_detected(approvalTargetにロゴの行まで入る)になり、同じ読み取りを使う Auto-Yes のポーラーが番号でログイン方法を選びえた(#3797 は観測の kind だけをunknownにしていた)。読み取りは footer 行が許可の画面の↑/↓ Navigate · tab Amendのときだけ読むようにし(新しいANTIGRAVITY_PERMISSION_FOOTER_PATTERN、#3797 と同じ条件)、観測(classify.ts)の個別の footer 判定はこれに寄せて削除。読み取りが断った番号付きの agy 画面は汎用の番号リスト解析にも渡さない(detectPromptOnCleanFrameとassessPromptAnswerability)ので、ポーラー・/prompt-response・状態判定(未分類のフレームunknown_frame)のどれも答えない。狭い幅で footer が切れた・折り返した、または版違いで綴りが違う許可の画面も同じく答えない側に倒れる。footer が画面外にある・↑/↓ Navで切れている画面も、footer に依らない判定(新しいisAntigravityUnvouchedNumberedScreen: 最下部が1.からの 2 つ以上の連番の選択肢で、どれかに>が付き、その下に入力欄が無く、許可の読み取りが読まず、最後の選択肢の直下が/feedbackのesc Cancel · 1-6 Selectfooter でもない)で汎用解析に戻さず、状態判定もunknown_frameにする。/feedbackのメニューと、>の付かない返答の番号リストの読みは変わらず、生成中の画面(spinner・Generating)や番号で始まる送信文のエコーは確認画面と読まない。1.1.27 の許可の画面(コマンドの実行・ファイルの作成)への応答は変わらない。 -
fix(agent-health): codex の
/modelが起動完了前に拒否されるとscreen-pickerを不合格にしていた問題を修正 (#3803): 日次確認は「変化の止まった最初の画面」を起動完了とみなして(waitForStartup)すぐに/modelを 1 回だけ送っていたが、codex 0.163.0 は入力欄を描いた後もしばらく起動中で、そのとき/modelに「Model selection is disabled until startup completes.」と返し選択画面を開かない(2026-10-19 のレポート)。scripts/agent-health/tool-table.tsの選択画面の定義に、起動中の拒否を示すnotReady(codex はCODEX_PICKER_NOT_READY)を追加し、送信後にその文言が新たに出たら 3 秒おいて同じコマンドを送り直す(最初の送信から 45 秒まで。新しいモジュールsrc/lib/agent-health/picker-open.tsのsendUntilPickerOpens)。送るたびに Enter は 1 回だけで、選択画面が開いている可能性のあるあいだは閉じるキー以外を送らない(#3053 の約束は不変)。拒否が続いて開かなかったときは summary に「起動完了前として N 回拒否された」と出す。notReadyを持たない claude の選択画面と、開いた・何も出なかったときの動きは従来どおり。 -
fix(cli): agy 1.4 の
/modelpicker をantigravity-model-pickerと読み、番号付きのログイン方法の画面を許可ダイアログと読まない (#3797):prompt --jsonのscreen.kindが、agy 1.4 の/modelpicker(Switch Modelと機種の行の間にSearch:の入力欄と下線が入り、入力欄が picker の上に残る描画)でunknownになっていた(#3773 の UAT・#3788 の 3)。実機 agy 1.4.2 の画面(200x1000、私設 tmux)をtests/fixtures/prompt-observation-3797/に採取し、src/lib/prompt-observation/antigravity-model-picker.tsで画面の形がすべてそろうときだけ picker と読む: 1 行のままのKeyboard: ↑/↓ Navigate ←/→ Effort enter Select esc Go Back|Clear searchの footer が最後に描かれた行で(下は空行と右寄せの機種表示だけ)、その上 30 行以内にSwitch Model→Search:→下線→機種の行(>はちょうど 1 つ)→Effortが順に並ぶこと。footer が折り返された画面・下に入力欄がある引用・強調が 0 か 2 の画面・検索で 0 件の画面は読まず、unknownのまま。1.1.27 の picker は従来どおりdetection/tools/antigravity/screens.tsが読む。あわせて、未ログインの agy が出す番号付きのSelect login method:(↑/↓ Navigate · enter Select)がantigravity-permission(厳格なrespondが答える kind)と読まれていたのを、実測した許可ダイアログの footer(tab Amend)で終わる画面だけをantigravity-permissionにしてunknownに戻した。picker・slash・unknownは従来どおり厳格なrespondが送る前に拒否する(答える範囲は広げていない)。ヘッドレスの Chromium で agy の端末が真っ黒になる件は headless 由来ではなく、上端寄せの 200x1000 ペインの末尾約 970 行の空行へ自動追従でスクロールするためで(有頭の Chromium でも同じ)、UI 側(src/config/terminal-display-compaction.ts)の扱いとして本変更には含めない。 -
fix(auto-yes): agy の送信が途中で切れた回の窓の超過・窓の後に固まった記録・セッション待ちの通知の遅れを直す (#3796): #3789 / #3791 の独立レビューで残った低指摘 3 件。いずれも誤答は起こさず、通知・表示が遅れる/古くなる側の問題で、どれも答える方向には倒していない(既存の陰性テストは不変)。1. 受信窓で受け付けた agy の確認への送信が
noteAnswerSentの前に切れたとき(sendPromptAnswerの失敗、送信中に tick が世代を失った中断、Enter フォールバックのsend-keys失敗)、その確認はansweredのまま無音で残り続けていた。切れた時点から猶予(18 秒)を数えるようにし、過ぎても確認が残っていればno-hook-receiptとして記録・ログする(hint は「送る途中で送信が止まり…キーの一部が入力に残っている可能性がある」)。送信を始める前の失敗(所有確認など)は従来どおり数えず、停止・再有効化の後に後継が新しい受信で受け付けた確認には、旧送信の帰還は中断でも成功でも猶予を付けない(送信開始時の受信区間だけを対象にする。通常送信・Enter フォールバックとも)。2. 窓の中で poller が記録した理由(partial-answer、契約の抑止など)は、窓が閉じて gated な読み取りが確認を見なくなると再刻印されず、cmate waitが 60 秒後に古いとして捨てていた(確認は画面に残ったまま)。受け付けた確認が残っている tick ごとに、その確認の初出以降の記録を再刻印し、同じ確認へのno-hook-receiptより優先して残す。partial-answer(出来事の記憶)は確認が残る間ずっと再刻印し、契約由来の理由はその tick の現在の評価に従う(今の契約が抑止していれば、既存の記録が無くても・契約が外れて消した後に新しい契約が抑止しても、その理由を記録する。理由が変われば新しい理由を記録し、契約が外れれば記録を消す。残っているpartial-answerはそれより優先する)。確認が消えれば再刻印は止まる。3. セッション待ち(MAX_BACKOFF_MS=60 秒)の poller は、待ちに入った直後にセッションが作られても次の tick まで答えず、push gate の保留もその tick+2 tick まで延びて人への通知が最大約 64 秒遅れた。createSessionが作ったセッション名をlib/tmux/session-created-signalで知らせ、待っている poller は保留中の timer を差し替えて 1 poll 間隔後に tick する(timer 発火・別の timer・停止・置き換え後は何もしないので、二重の連鎖や停止後の再起動は生まない)。push gate の保留は、保留開始時に読んだ tick より早まった実際の tick(sessionWaitTickAtの前倒し、またはsessionAppearedAt)に合わせて終わる(#3791 の値より後ろには延ばさない)。一度採用した早い時刻は hold 自身に保持し、前倒しした tick の capture 失敗・通常予約・再予約でも後ろへ戻らない(tick が走った時刻はsessionWaitTickRanAtに残す)。 -
fix(auto-yes): セッション待ちから戻る poller が答える流れの待機で、push gate が 9 秒で誤通知しないようにする (#3791): push gate の
auto-yes-answeringの保留(AUTO_YES_ANSWER_GRACE_MS=9 秒)は「次の tick は cooldown 以内」を前提にしていた。poller の tick 間隔はポーリング・cooldown・thinking(5 秒)なら 9 秒に収まるが、capture がセッション無しで失敗したときの待ち(MAX_BACKOFF_MS=60 秒)は収まらず、その間にセッションが立ち上がると(send --auto-yesはセッションより先に Auto-Yes を有効にする)次の tick で答える流れでも 9 秒でauto-yes-not-answeredの通知が 1 回出ていた。poller はセッション待ちの tick だけ予定時刻をsessionWaitTickAtに記録し、gate は保留を始めたときにそれを一度だけ読んで、保留の終わりを「起点+9 秒」と「その tick+2 tick(AUTO_YES_ANSWER_AFTER_TICK_MS)」の遅い方にする(新設autoYesHoldEndsAt())。エラーが続く backoff では延ばさない(Auto-Yes が壊れているときは従来どおり 9 秒で通知する)。記録した tick は、それを予定した poller が動いている間だけ数える(停止・off→on で新しい poller になれば捨て、on のままの再 enable では残す)。waiting-push-notifierの再判定タイマーは 9 秒ごとに聞き直すので、保留中に poller が止まっても来ない tick を待ち続けない。Claude の確認画面の取りこぼし形(#3770 の残り)は、隔離した実機の画面を資格情報を複製せずに採取できないため未対応。 -
fix(cli-tools): 起動処理が答えた信頼確認に Auto-Yes が窓の後で答え直さず、copilot の信頼確認も起動処理に任せる (#3790): #3776 の残りの指摘。gemini の起動処理が待ちの最後の試行で信頼確認に Enter を押すと、gemini が再起動の行を描く前に起動の窓が閉じ、poller が答えていない確認と読んで
1+Enter を重ねうる経路が 5 秒(GEMINI_TRUST_SETTLE_MS)より遅い再起動で残っていた。起動処理は押したキーをrecordLaunchDialogPressで記録し、poller はその後LAUNCH_DIALOG_PRESS_SETTLE_MS=30 秒は窓の有無にかかわらず確認を起動処理のものとして答えない(それを過ぎても描かれたままの確認は、キーが届かなかったものとして従来どおり安全な行のときだけ答える。次の起動で記録は消える)。copilot の「Confirm folder trust」は起動処理が1だけで答える一方、poller も1+Enter を答えており、起動処理の1が先に届くと poller の1が入力欄に入ってユーザーの発言として送られることを実機で確かめた(copilot 1.0.91、私設 tmux・一時 HOME)。copilot の起動も gemini と同じ窓を開き、poller は窓の中・押した直後はagent-launch-dialog(dialog=copilot-folder-trust)で抑止し、窓の後は既定が1. Yes(このセッションだけの信頼)の宣言どおりの並びのときだけ答える(2.は全 checkout 共通の~/.copilot/config.jsonに書くので答えない)。claude の信頼確認(2.1.302、番号なし)は poller がそもそもプロンプトと読まないので変更なし。gemini の信頼確認への poller の1+Enter は実 gemini 0.64.0 で1だけで確定し、Enter は再起動中に消えることを確かめた(変更なし)。隔離手順(docs/user-guide/uat-isolation.md)に、gemini の自動更新を止める設定(general.enableAutoUpdate/enableAutoUpdateNotificationをfalse)とnpm_config_prefixの逃げ道を実測つきで追記した。 -
fix(auto-yes): agy で答えた確認が画面に残り続けると Auto-Yes が理由を残さず黙る問題を修正 (#3789): #3767 の受信窓の判定は、答えた確認が窓(8 秒)の後も画面に残る tick を
answeredとして記録もログもせずに見送っており、答えが届かなかったときや、受信の無い別の確認がそれと入れ替わったとき(#3767 のレビューの L2)に、セッションがlastSuppressionnull・ログ 0 件のまま止まっていた。Auto-Yes が受理した確認へ実際にキーを送ってからANTIGRAVITY_ANSWERED_DIALOG_GRACE_MS(窓 8 秒+観測の猶予 5 秒+応答後の cooldown 5 秒=18 秒)を過ぎても確認が残っているときを新しい判定outlivedとし、既存の理由語no-hook-receiptでlastSuppressionに記録して、antigravity-autoyes-withheld-no-hook-receiptに「キーを送ってから 18 秒を過ぎても確認が画面に残っている(閉じなかったか、受信の無い別の確認と入れ替わった)」hint を出す。猶予の起点は送信が tmux に届いた時点で、受理しても送らなかった経路(他サーバのセッション・途中まで入った入力・基本ルールや契約が答えない など)はoutlivedにせず、その tick が記録した本当の理由を上書きしない。答えは足さない(もう一度は答えない)ので誤承認の側には動かず、猶予の内は従来どおり黙る。cmate waitのno-hook-receiptの文言とガイド(ja/en)の場合分けもこの状態を含む形に揃えた。読み取りの空白で episode を捨てる件(旧判定と同じ)と、答えた確認が新しい受信を使って入れ替わった確認が止まる件(L2)は、直すと #3767 の既存の対照(停止後の再開・置き換わりへの応答)と食い違うか誤承認の側に倒れるため直さず、テストで現状を固定した。持ち越しの 7 秒の閾値は、本番ログの実測(応答の間隔の上乗せは最大 0.63 秒、読み取りの間隔は最大約 5.6 秒)で余裕 1.37 秒以上を確かめて据え置いた。 -
fix(cli): 厳格な
respondで信頼確認にNo, exitと答えると exit 41outcome_indeterminateになる問題を修正 (#3788): agy はNo, exitで終了するが、tmux のセッションはシェルを残して生き続けるため、Enter 後の観測はrunning:true・kind:unknownになり、session-endedを「観測がunavailableか画面が無い」ときだけにしていた判定がindeterminateと読んでいた(#3773 の実機 UAT)。送った答えがその画面の「終了を選ぶ答え」(kinds.tsのEXITING_STRICT_ANSWERS。位置・選択肢の数・その位置のラベルの 3 つがすべて合うときだけ。信頼確認は 2 択の 2 番目がNo, exit。並びが逆・選択肢が 3 つ・文言違いの画面では当てはまらず 41)で、かつ Enter 後の読み取りがunknownのときだけペインの前景コマンド(#{pane_current_command})を引き、シェル(zsh/bash/-zshなど、#3089 のisShellPaneCommand)に戻っていればsession-ended(exit 0)にする。前景は #1922 の gateway であるcli-sessionの新しいreadSessionPaneCommand()で読み、createStrictRespondDepsがStrictRespondDeps.paneCommandとして渡す。Yesや permission の答えのあとにシェルが前景に戻っても(agy が別の理由で落ちた)、前景が agy のまま・取得できない(null・例外)・画面が読めなかった・同じ画面のまま、はどれも従来どおり exit 41(迷ったら 41 の安全側は不変)。agy 1.4.1 の実機で、起動中はagy、終了後はzshになることを私設 tmux(-L)・一時 HOME で確かめ、終了後のペインを fixture にした。 -
fix(cli-tools): gemini と agy の起動時の信頼確認を、カーソルが安全な行にあるときだけ Enter で通し、Auto-Yes と二重に答えない (#3776): 起動時の確認を宣言(質問文・押してよい行・カーソル行・キー)と純関数
decideLaunchDialogで読む共通部品src/lib/cli-tools/launch-dialog.tsを追加し、gemini と antigravity のwaitForReady(agy は #3757 の「次の send が借りの Enter を送る」経路も)をこれに乗せた。以前は質問文が画面にあるだけで Enter を送っていたので、カーソルが gemini の3. Don't trust(trustedFolders.jsonに永続)や agy のNo, exit(agy が終了)にあってもそれを確定していた。安全な行(gemini は1. Trust folder、agy はYes, I trust this folder)以外では送らずlaunch-dialog-heldを、文言や描画が宣言と合わないときはlaunch-dialog-unrecognizedを warn に残す(画面本文はログに出さない)。実機測定(gemini v0.62.0・私設 tmux socket・一時 HOME、2026-10-17)で、Auto-Yes の poller がこの確認を既定(=カーソル行)の multiple choice と読んで1(カーソルが Don't trust なら3)を答えていたと分かったため、codex の起動ダイアログと同じagent-launch-dialogで抑止する。抑止は起動処理の待ちの間(新しいsrc/lib/cli-tools/launch-window.tsの窓)だけで、窓の後に残った確認(Enter が届かなかった・自動更新などで遅れて出た)はカーソルが安全な行にあれば poller が既定を答える(文言や描画が宣言と合わない画面でも、選択肢が信頼確認のものなら、既定が1. Trust folderでない限り答えない。窓は Enter の後 5 秒は閉じない)。確認とみなすのは質問文の下に宣言した選択肢の行が並ぶ画面だけで、答える prompt がその選択肢であるときに限る(返答中の引用や diff、別の許可画面は抑止しない)(信頼の確認の Enter の後の gemini は自分で再起動し、未サインインの HOME では認証方式の選択が出るので、二重の Enter はそこで既定を確定しうる)。あわせてforgetAutoYesHold(key, since)が別 episode の Auto-Yes hold を消さないようにした(waiting-push-notifierの hold 解除が次の episode の hold まで消し、通知が最大 1 hold 分遅れていた)。 -
fix(send): Command Code の plan review による送信拒否を
lastSendでnot-sent (plan-review)と読む/信頼の確認の行を webapp-guide に追記 (#3772): 本文を打つ前の拒否が文言で判別されずindeterminate(send-error)になっていたため、エラーにcode(PLAN_REVIEW_SEND_REFUSED)を付けclassifySendFailureが code で判別する。webapp-guide(ja・en)のダイアログカード表に「信頼の確認(agy)」の行を追加。timer-manager / relay-delivery の失敗理由には send state を付けない(既存テストが文言を固定。lastSendで読める)。 -
fix(cli): 厳格な
respondと観測トークンの残りの指摘(#3755・#3756 のレビューの続き)を修正 (#3771): ①引用された信頼確認を trust と読まない —↑/↓ Navigateの行の下に生成中の出力が続くフレームはunknown側(下に空行と右寄せのモデル状態行しか無い実フレームは従来どおり trust)。②strict の経路で prompt-observation が投げた例外は 40observation_failed(画面の本文を含まない)で受け、ログに出す例外は名前・code・message 先頭 80 文字までにする。③answerStrictlyが途中で例外を投げても、実際に送ったkeysSentを結果に使い、キーを 1 つでも送っていれば task 履歴にprompt_answered_humanを残す。TaskEventPayloadに任意のansweredByとoptionを足した(後方互換、マイグレーション無し)。④信頼確認のscreen.answerViaをterminalからstrict(respond --expect-screenで答えられる)に変更(permission 画面・picker・slash は不変)。⑤agy の起動窓が信頼確認へ Enter を送る直前に strict の hold を見て、hold 中は送らない(残余競合を狭めた)。⑥exit 41 でもoperation_in_progress/operation_id_conflictは何も送っていない(keysSent0)ことを docs(ja/en)に明記。 -
fix(auto-yes): push gate の Auto-Yes hold を off/episode 終了/1 時間で捨て、agy の起動窓を参照カウント化し、gemini の信頼確認の文言違いを直す (#3770):
autoYesHoldsの起点が Auto-Yes を off→on にしても古いまま残り on 直後にauto-yes-not-answeredの誤通知が 1 回出る問題を、auto-yes-inactiveを返したとき・episode が閉じたとき・enabledAtが起点より新しいときの起点更新と、1 時間経った hold の掃除で解消。beginStartupWindow/endStartupWindowは参照カウントを持ち、並行 relaunch で先の finally が後の窓を閉じない。gemini は隔離環境で実測(v0.62.0 は起動約 2 秒でDo you trust the files in this folder?を出す=30 回の窓は十分)し、窓切れではなく文言違い(旧Do you trust this folder?のみ照合)で信頼確認が一度も処理されないことを直し、capture の例外を debug ログに残す。Claude の確認画面の述語は実機の画面が無いため緩めず、取りこぼす合成 fixture が現状 false であることを固定した。 -
fix(auto-yes): agy の確認が hook 受信の窓(8 秒)を 5 秒周期の tick にまたがれて答えられなくなる問題を修正 (#3767): agy は hook に応答してから数秒後に確認を描くため、poller の次の tick が受信から 8.7 秒後(窓の外)になり、以後は新しい受信が無く約 4 時間答えなかった(server.log 実測: 最初の withheld はすべて直前の受信から 8.1〜9.6 秒後)。受信窓の判定を「tick の時刻」から「確認が最初に画面に出た時刻(直前に確認が無かった tick と受信の遅い方)」に変更。受信より前から出ていた確認・窓より後に出た確認・受信が無いとき・使い済みの受信には従来どおり答えない(受信は 1 つの確認にだけ使う)。止めたときは新しい理由
no-hook-receiptをlastSuppression・wait・prompt --jsonに載せ、ログの hint を「一度も受信が無い」「受信から N 秒後に出た(窓外)」「受信が途絶えた」「受信より前から出ていた」で区別する。利用ガイド(ja/en)に理由語の行と止まったときの対処を追記。 -
fix(ui): agy の信頼の確認(番号の付かない選択)で、UI が「端末で操作が必要(信頼の確認)」と表示するように (#3758): agy の
Do you trust the contents of this project?は番号が付かないためisAntigravityNumberedDialogの対象外で、検出はwaiting/antigravity_selection_list(isSelectionListActive)、prompt にはならない(PromptPanel・スマホのシートは出ない)。Auto-Yes もこの画面には答えない(#3757)のに、チャット面は汎用の「選択リストが開いています」の帯、ターミナル面(PC の分割ペインのフッタ・スマホのドック・/sessionsのタイル)は矢印のパッドだけで、エージェントが答えを待って止まっていることをどの面も言わなかった。selection-shapeにisTrustConfirmationFrame(frame, cliToolId)(検出器のisAntigravityTrustScreenを検出器と同じ末尾 15 行に当てるだけ。新しい検出の写しではない)を足し、チャット面の帯をその画面のときだけchatSurface.reasonTrustConfirmationに替え(data-reasonはselectionListのまま、カードと矢印は不変)、ターミナル面はSelectionListKeysがパッドの上にtrustConfirmation.noticeを出す。フレームを持たないスマホのドックは、controller の poll が同じ応答のフレームからisTrustConfirmationActiveを読んで渡す。画面から答えを送る新しいボタンは作らない(承認の送信は #3756)。番号付きの prompt と、Switch Model など他の選択リストの表示は変わらない。本物のbuildCurrentOutputに実フレーム(antigravity-live-2364/trust-dialog.txt)を通した応答を、本物の親(PC はTerminalSplitPaneContent、スマホはWorktreeDetailRefactored)から描いて固定した。 -
fix(antigravity): 起動の窓より後に出た信頼の確認を一度だけ処理し、auto-yes が答えない画面で通知を抑え続けない (#3757): agy の起動は 1〜60 秒超でばらつき(隔離 tmux 実測で信頼の確認が起動の 61 秒後に出た)、
waitForReadyの約 34 秒の窓が先に終わると確認は誰にも処理されなかった。窓が Enter を送らずに終わったときは「Enter を 1 回借りている」と記録し、次の send のwaitForPromptが確認を見たら一度だけ Enter を送る(記録は同期的に取り出すので、同じペインを待つ 2 つの send でも Enter は 1 回。窓で送った場合は記録しない。待ちはこの間だけ 60 秒)。時間切れの logantigravity-prompt-detection-timeoutに画面の状態(screenと末尾数行)・reason: startup-trust-unhandled・capture の例外の回数と最後の例外を出し、その後のprompt not readyは信頼確認が未処理であることをエラー文に書く。auto-yes の poller は信頼確認を番号なしのためプロンプトと読めず(detectPromptOnCleanFrameがisPrompt: false→ dialog gate にも Enter fallback にも届かない)何も記録していなかったので、起動の窓の外ではこの画面に理由startup-trust-unhandled(AutoYesSuppressionReasonに追加、cmate waitの文言つき)を記録して通知させる(窓の中で処理される確認は従来どおり無音)。push gate は記録が無いときのauto-yes-answeringをAUTO_YES_ANSWER_GRACE_MS(cooldown+2 tick = 9 秒)に限り、過ぎた待ちはauto-yes-not-answeredで通知する。猶予の起点は episode のsince(hook 由来で古いことがあり、連続する prompt の間は凍結される)ではなく、gate がその episode を最初に抑えた時刻と poller の最後の応答時刻の遅い方。waiting-push-notifierは保留が終わる時点で gate に問い直す(問い直しで再び抑えたときのログは debug)。 -
fix(auto-yes): 再起動の解除表示の取り残し 4 点を修正 (#3753): ID 移送の待ち中に OFF → shutdown 記録 → 移送の順で、停止記録に旧 ID が残り次回起動で「再起動で解除」が戻る問題を、移送後に記録を書き直して直した。
getAutoYesStateが無効な状態にも期限判定をかけ、手動 OFF や状態の無い key への OFF がstopReason: 'expired'に書き換わる問題を、有効な許可の期限切れだけに限って直した。GET /api/worktrees/:id/auto-yesの instances が instanceId だけで畳まれ、ツールの違う同 instanceId の 2 key が後勝ちになる問題を、roster のツールの key を採用して直した。webapp-guide(ja・en)に「再起動で解除」の表示と消える条件を追記。 -
fix(auto-yes):
AskUserQuestionの確認画面(Submit / Cancel)に、ブラウザ側の Auto-Yes と Claude の確認画面でも答えない (#3748): #3497 はサーバーの poller だけで Command Code の確認画面(❯ 1. Submit/2. Cancel)への応答を止めており、サーバーの poller が動いていないときはブラウザ側のuseAutoYesが画面を見ずに1を送っていた。また Claude の確認画面(Ready to submit your answers?/❯ 1. Submit answers/2. Cancel)には、サーバー側でも1を送っていた。判定を共有の述語isAutoYesDeclinedConfirmPage(src/lib/polling/auto-yes-confirm-page.ts。Command Code はisCommandCodeQuestionConfirmPage、Claude は新設のisClaudeAskUserQuestionConfirmPageで、転写の末尾が1. Submit answers/2. Cancel(カーソル付き)・その直上の行そのものがReady to submit your answers?・さらに上に✔ Submitのタブ行、の 3 条件をすべて満たすときだけ真。文言を含む Bash / Edit の許可や質問は確認画面と読まない)にまとめ、サーバーの poller(抑止の手順 3.3)がこれを通る。この述語はサーバー専用(logger → fs に届く)なので、ブラウザは client 安全なisConfirmPageShapedPrompt(src/lib/polling/auto-yes-confirm-shape.ts)で、選択肢がちょうどSubmit(Submit answers)/Cancelの prompt には画面に関わらず答えない。抑止の理由は確認画面専用のquestion-confirm-pageを新設し(これまではunclassified-frameを流用しており、cmate waitの文言が「どの検出器も画面を認識しなかった」と実際と逆を言っていた)、cmate waitは「Submit/Cancel の確認は今の回答をそのまま確定するので人に返す」と表示する。poller:response-sentのログにpromptType・質問の本文(500 文字で切る)・送った答え(番号・y・[Enter])を追加。Claude の通常の番号付き許可(Yes/No)と yes/no の質問には従来どおり答える。 -
fix(schedule): 同期中に止めた worktree / 全体の cron が readCmateFile の完了後に作り直される問題を修正 (#3735):
syncSchedulesはreadCmateFileの await の後、manager の状態も停止も確かめずに cron を作成・差し替えていたため、stopScheduleForWorktree(リポジトリ削除・セッション後始末)やstopAllSchedules(shutdown)の直後に同期が戻ると、削除済み worktree の cron や shutdown 後の cron が生き返り、次の同期(60 秒)で stale として消えるまでにジョブが発火しうる状態だった。ManagerStateに manager の世代(stopAllSchedulesで加算)と worktree ごとの停止回数(stopScheduleForWorktreeで加算)を持たせ、同期の開始時に読み、await の後に同期的に比較する。全体停止があれば同期を打ち切り(部分的なactiveScheduleIdsで再起動後の schedule を stale 判定しないため)、その worktree が止められていれば飛ばす(activeScheduleIdsにも足さず、mtime キャッシュも捨てて次の同期で拾い直す)。開始済みジョブの実行・記録と DB の upsert は止めない。initialized=false中のsyncSchedulesNowを no-op にする案は、世代判定で足りたため採らなかった。 -
fix(tmux): 送信の直後の capture が、送信の前に始まった capture の古い画面を受け取る・キャッシュに書き戻す問題を修正 (#3734):
invalidateCache()は cache だけを消して inflight を残していたため、無効化の後に来た呼び手も無効化の前に始まった fetch に合流して送信前の画面を受け取り、その fetch が #2297 の 250 ms 後の再無効化より遅く返ると送信前の画面がCACHE_TTL_MS(5 秒)残っていた。capture cache に世代(session ごと+全体)を持たせ、invalidateCache()は session の世代を進めて該当の inflight も消し、clearAllCache()は全体の世代を進める。getOrFetchCapture()とcaptureSessionOutputFresh()は始めた時点の世代を控え、await の後に世代が変わっていれば書き戻さない(呼び手自身には結果を返す)。getOrFetchCapture()の finally は自分の promise のときだけ inflight を消すので、clearAllCache()の後に始まった新しい fetch の singleflight が外れない。無効化が無いときの動き(同時の呼び手が 1 回の fetch を共有し結果をキャッシュする)は変わらない。 -
fix(ws): 待ちの間に要らなくなった terminal 購読と OpenCode V2 の model の latch を捨てる (#3733):
handleTerminalSubscribeは旧購読の解除・checkSessionOwnership・transport.subscribeを await した後、まだ求められているかを見ずに購読を登録していたため、待ちの間に切断されると registry に subscriber が残り(1 session 4 本の上限まで漏れると server の再起動まで「Terminal subscriber limit reached」)、待ちの間のterminal_unsubscribeは効かず、同じ socket の 2 回目の subscribe は後に返った方が先を上書きして先を解除しなかった。ClientInfo.terminalSubscriptionSeqを subscribe の開始・unsubscribe・切断で進め、各 await の後に見比べて、古くなっていれば得た購読をすぐ解除して何も送らず返る。onErrorの fallback(snapshot とterminal_error)、onOutput・onExitも送る直前に現役かを見る。入力・resize は直さない(受け取った時点の購読へ送るのが意図どおりで、所有権は #3334 の確認が守る)。動くのはTMUX_CONTROL_MODE_ENABLED=trueのときだけ。OpenCode V2 ではsyncOpencodeV2History/recordOpencodeV2TurnEndにisCurrentを足し(usage と同じ形)、読み取りの間に購読が閉じた・置き換わったときは model の latch(と #2357 の変化の broadcast・Push)だけを飛ばす。閉じた turn の行は従来どおり保存する(requestIdで重複しない)。 -
fix(relay): 取り消しが成功を返した relay の返答・通知が依頼元に送られる問題を修正 (#3732):
deliverOneは待機判定(findRelayHoldReason)を await した後も一覧取得時点の本文をそのままsendUserMessageしていたため、その間(または前の relay の送信中)にrelays cancelが 200 を返した relay の返答・prompt 通知が composer に入り、一覧の後に期限切れ掃除で保留がexpired通知に置き換わった場合も古い返答を送ったうえで期限切れ通知を消していた。送信直前に同期的に行を読み直し、保留の kind と本文が一覧時点と同じで(期限切れ通知以外は)relay が open のときだけ進み、同じ区間で「確保」(globalThis の集合、src/lib/relay/relay-delivery-claims.ts)を取る。確保中の取り消しはwithdrawRelayが{ ok: false, reason: 'delivering' }、POST /api/relays/:id/cancelが 409RELAY_DELIVERINGで答え(配達はそのまま完了してdeliveredになる)、commandmate relays cancelはこれをエラーとして exit 2 で表示する。送信がprompt_waiting・失敗・例外で終わったとき、および prompt 通知の配達後は確保を外すので以後の取り消しは成功する。確保中の relay は期限切れ掃除も見送る(返答と期限切れ通知の二重送信と、通知の消去を防ぐ)。期限切れ通知の配達は取り消しの有無にかかわらず従来どおり。 -
fix(cli):
start --daemonで起動したサーバーの出力をlogs/server.logに残す (#3731):DaemonManager.start()がnpm run startをstdio: 'ignore'で spawn していたため、start --daemonとそれで再起動するcommandmate updateのサーバーは stdout / stderr がすべて/dev/nullに捨てられ、docs が案内するtail -f ~/.commandmate/logs/server.logのファイルは存在しなかった。以後は設定ディレクトリのlogs/server.log(--issue <N>の並列サーバーはlogs/server-<N>.log。新設のgetServerLogPath())をO_APPEND | O_NOFOLLOW・0600 で開いて子の stdout / stderr に渡し(親は spawn 後に自分の記述子を閉じる)、起動ごとに区切り行を書く。起動時に 10MB 以上ならscripts/build-and-start.shと同じく.1〜.3の 3 世代へ回す(rotateServerLog()。回した各世代も 0600 にし、scripts/start.shの 0640 のファイルを回しても権限が緩まない。シンボリックリンクは回さず辿らない)。ログを開けないときは警告して従来どおり出力を捨て、サーバーの起動は止めない。commandmate statusは稼働中と PID ファイルが古いままのサーバーにLog:行でパスを出す(--all側も同じ)。CM_LOG_DIRは使わない — サーバー自身が.envから読む会話ログの置き場所であり、このパスは spawn の前とstatusから .env 無しに決まる必要があるため。 -
fix(auto-yes): サーバーの再起動で外れた Auto-Yes を「再起動で解除」と表示し、
poller:stoppedに止まった理由を出す (#3730): 再起動で Auto-Yes の許可は全部オフに戻るが、そのことがどこにも出ず、もともとオフだった instance と区別がつかなかった(orchestrate の worker が /rebuild の後に許可ダイアログで黙って止まる)。許可は自動では戻さないまま、正常停止(SIGINT / SIGTERM)のときに「その時点で有効かつ期限内だった許可」(poller が未起動のものを含む)を DB ごとのファイル<CM_DB_PATH>.auto-yes-shutdown.json(一時ファイル→rename、0600。cm.dbとcm-N.dbは別ファイル)に記録し、次の起動で両方の listen より前に読み込んで、期限内のものをenabled: false・stopReason: 'server-restart'の状態として入れる(poller は起動しない)。表示は元の許可の期限まで続き、ON / OFF の操作で消え、期限内なら 2 回目の再起動でも残る。見える場所:capture --jsonのautoYes.stopReason、GET /api/worktrees/:id/auto-yesのstopReason(1 件とinstancesの各項目)、GET /api/worktreesの行のautoYesStopReasonByInstance(autoYesByInstanceは武装中だけのまま)、lsの AUTO_YESoff(restart)、instancesの AUTO_YESno(restart)(--jsonはautoYesStopReason)、waitの人待ち(exit 10 と--on-prompt human)の stderr の 1 行(再有効化のコマンドは待機対象の--instance付き。付けないと既定の primary が有効になる)、PC の分割・スマホ・Sessions のタイルのトグル横の「再起動で解除」(en: Released by restart。PC は通知がある間も全インスタンスの状態を読み直すので、alias の表示も期限で消える)。exit 10 の条件・Push・permission hook の判定は変えていない。記録が書けない・読めない・壊れている・消せないときは従来どおり全部オフ・理由なしで起動を続け、SIGKILL やクラッシュでは何も記録されない。停止が始まった後に ownership の待ちから戻った有効化は 503 で断り、記録の後に受け付けた OFF・セッション停止・instance 削除は記録を書き直して取り下げる(次の起動で消した表示が戻らない)。起動時の worktree ID の移動は、移動先に既にある状態(復元の後の ON / OFF)を復元した通知で上書きしない。あわせてstopAutoYesPolling(compositeKey, reason)にし、poller:stoppedが全経路でreasonを出す(manual/expired/not-enabled/invalid-key/stop-pattern/consecutive-errors/session-killed/instance-removed/worktree-deleted/orphan-swept/shutdown/worktree-id-migrated/replaced/reset)。poller の停止理由は許可を外さない(ID の移動と置き換えでは許可はそのまま)。 -
fix(agent-health): ツールの利用上限(429)に達した画面の check を不具合として起票せず、
usage-limitの skip にする (#3729): 2026-10-16 の command-code 1.81.0 はYou've reached your weekly usage limit. Resets in 1d 17h(裏でPOST /alpha/generate → 429)でプロンプトを処理せず、hook-correlation・screen-running・screen-approvalの 3 本(#3717〜#3719)が CommandMate の不具合として自動起票されていた。AgentHealthSkipKindに環境要因の種別usage-limit(要約での呼び方「利用上限に達している」)を足し、新設のsrc/lib/agent-health/usage-limit.tsのjudgeUsageLimitで、fail になる判定のうち判定した画面(hook-correlationはそのツールのセッションで見た画面)に上限の表示があるものだけを skip にする(判定していればの summary はskipReason、画面の末尾はevidenceに残す。pass はそのまま)。上限の文言はツールごとの行の表USAGE_LIMIT_SIGNATURES(command-code の実測 2 行と claude・codex の文言)で、「上限に達した」ことを言う文言だけを見る —usage limit・429・Resets in単独や、claude の「up to 50% of your weekly usage limit」・codex の「You have 1 usage limit reset available」は上限と読まない。skip はcoverage.counts.skipByKindと要約の 1 行目に数えるので、上限が続いても検出の空洞化が見える。 -
fix(auto-yes): 契約が止めた agy のダイアログを hooks.json の向き先のせいにしない (#3724): 契約の
autoYes(mode: offなど)が antigravity の許可ダイアログへの応答を止めると、サーバーはaskと答えるのでダイアログは画面に残り、hook の受信記録の窓(8 秒)を過ぎた後は毎分antigravity-autoyes-withheld-no-hook-receipt(「~/.gemini/config/hooks.jsonの向き先(#2622)を確認」)が出ていた(実例: 477 件。hook は届いており原因は契約)。logIfWithheldForWantOfReceiptは案内を出す前に、関門なしで読んだpromptDataを poller 本来の判定resolveAutoAnswerWithPolicy(基本ルール → 契約の順)に通し、契約が抑止した場合だけ別名の info ログantigravity-autoyes-withheld-by-policy(worktreeId/instanceId/reason(mode-off/deny-pattern/type-not-allowedなど)/mode/taskId/hint: 契約の autoYes を確認)を出す。基本ルールが答えない確認(checkbox・入力を要する選択肢)と、契約が無い・答えを許している場合は従来どおり hooks.json の行。毎分 1 回の間引き(WITHHELD_NO_RECEIPT_LOG_INTERVAL_MS)は両方の行で同じ記録を共有する。reason/modeとtaskIdを同じ task から取るため、auto-yes-policy.tsに 1 回の引き当てで両方を返すgetSessionAutoYesPolicyEntry()を追加(getSessionAutoYesPolicyと同じ TTL キャッシュ。既存の呼び出し元は不変)。lastSuppressionは変えない。 -
fix(auto-yes): 契約が
autoYes.mode: offで止めている instance を、有効化の時点とls・instances・UI で見えるようにする (#3723): 契約がmode: offの instance で Auto-Yes を有効にしても一度も答えないのに、auto-yes --enable/send --auto-yesは「enabled」と出して exit 0、lsは残り時間、instancesはyes、UI は ON を表示し、理由はcapture --jsonのautoYes.lastSuppressionにしか出なかった(CommandAgent #680 で antigravity が許可ダイアログのまま 11 時間停止)。判定は poller と同じgetSessionAutoYesPolicyを通し、blockedはmode === 'off'のときだけ。POST /api/worktrees/:id/auto-yes(有効化)とGET …/auto-yes?cliToolId=の応答にcontractPolicy({ taskId, mode, allowPromptTypes, denyPatternCount }かnull)、GET /api/worktreesのautoYesByInstance[instance]とGET …/auto-yesのinstancesに省略可のblockedByContract: trueを追加。CLI はmode: offで stderr にWarning:(exit 0 のまま、sendは有効化の応答を読むようになった。send --contract … --auto-yesは task がまだpendingでサーバーのcontractPolicyがnullなので、送る契約のautoYesで判定する)、safe/allow-listedは答える種類をNote:の 1 行で添える。ls/instancesのAUTO_YES列はblocked、instances --jsonは該当行にだけautoYesBlockedByContract: true。ワークツリー詳細の Auto Yes トグルは ON のまま「契約で停止中(mode: off)」を表示し(ja / en)、いずれかの instance で Auto-Yes が有効なあいだは 12 秒ごとに Auto-Yes の状態を取り直して、ページを開いたまま契約が始まる・終わるのに追従する。契約が無い・modeが null・safe・allow-listedの instance の表示と JSON は変更前と同じ。 -
fix(auto-yes): Command Code の
AskUserQuestion確認画面(❯ 1. Submit/2. Cancel)に Auto-Yes が1で答えない (#3497): 自由記述の質問でワーカー自身の提案が回答候補に入った確認画面に、Auto-Yes がSubmitで答え、ワーカーがその提案を人の承認として実装し始めた(Kewton/Musunest#283)。Review ページの読み取り(#2755)は← to go back and edit行まで揃ったときだけ画面を断っており、その行の無いフレームは汎用の番号リスト解析がSubmitを既定とする 2 択のmultiple_choiceとして読み、選択肢がSubmit/Cancelだけの自由記述の質問は質問リーダー自身が同じ形で読んでいた。新しいisCommandCodeQuestionConfirmPage(raw)(最後の罫線の直下にタブ帯があり、1. Submitと2. Cancelの両方がある)を Auto-Yes の抑止 3.3 として判定し、該当すれば鍵を送らず人に返す。既定で外し、設定は無い。抑止は既存のunclassified-frameとして記録され(capture --jsonのautoYes.lastSuppression/cmate waitが理由を出せる)、WARNpoller:auto-yes-skipped-question-confirmに質問の本文(500 文字まで)と選択肢を残す。状態判定・回答パネル・履歴は変えないので人には今までどおり質問が見える。通常の yes/no と選択のプロンプト(#2521 / #2754 の質問画面を含む)への自動応答は変わらない。
Documentation
-
docs(docs): CLI 利用ガイドの no-hook-receipt (6) に送信中断時の説明を追加 (#3810): agy の確認への送信が途中で切れた場合(#3796)も、切れた時点から猶予(18 秒)を数えて過ぎても確認が残れば no-hook-receipt として記録される旨を、日英の CLI 利用ガイドの no-hook-receipt 理由一覧の (6) に追記(入力欄にキーの一部が残る可能性と、中断後に partial-answer が記録された場合はその理由を保ち no-hook-receipt としない例外を含む)。
-
docs(auto-yes): 利用ガイドの
lastSuppressionに、載っていなかった理由語 4 つを追記 (#3721):agent-launch-dialog/unclassified-frame/startup-trust-unhandled/question-confirm-pageの意味とcmate waitの表示の文言を、ja・en のcli-operations-guide.mdに表で足し、型AutoYesSuppressionReasonの 9 語すべてをガイドで引けるようにした。modeがnullになることと、prompt --jsonのpollerRecord/contractEvaluationが同じ語を使うことも書いた。コードの変更は無い。