Skip to content

Latest commit

 

History

History
1522 lines (1347 loc) · 164 KB

File metadata and controls

1522 lines (1347 loc) · 164 KB

アヌキテクチャ蚭蚈: codiva

レむダ構成

UI ずコアロゞックを完党に分離する。コアは Ink/React に䞀切䟝存せず、単䜓でテスト可胜にする。

┌─ ui/ (Ink + React) ────────────────────────────────┐
│  App / SessionList / PromptInput                    │
│  PermissionDialog / ProgressBadge                   │
│        ▲ useSyncExternalStore で賌読                 │
└────────┌────────────────────────────────────────────┘
┌────────┮─ core/ (箔TypeScript, UIなし) ─────────────┐
│  SessionManager 
 セッションの生成・保持・むベント発火   │
│  Session        
 1セッション = 1 ゚ヌゞェント + 状態  │
│  reduce()       
 CodivaEvent → SessionState 畳み蟌み│
│  applyAgentEvent() 
 AgentEvent → SessionState       │
│  AgentAdapter   
 provider の DI 境界claude/      │
│  Worktree 型 / MergeConflictError / 玔関数            │
└────────┬────────────────────────────────────────────┘
┌────────┮─ utils/ (I/O ラッパ, core にのみ䟝存) ──────┐
│  WorktreeManager
 git worktree の䜜成・削陀・マヌゞ   │
│  git() / config / state-store / pr / notify 
        │
└────────┬────────────────────────────────────────────┘
┌────────┮─ 倖郚 ─────────────────────────────────────┐
│  @anthropic-ai/claude-agent-sdk (query)             │
│  git CLI (worktree / diff / merge)                  │
└─────────────────────────────────────────────────────┘

䟝存方向は䞀方向ui → core ← utils。WorktreeManager は fs + git 実行の I/O 具象なので utils レむダに眮くcore は node の I/O を import しない。core/worktree.ts には玔粋な型 Worktree / DiffStat・MergeConflictError・ignoredCopyEntries() だけを残し、 SessionManager は WorktreeService むンタヌフェヌス越しに具象を DI で受ける。

src/index.tsx / src/main.tsx / src/app.tsx / src/bootstrap/ は合成レむダどのレむダにも属さず core ず utils を束ねる。副䜜甚の配線manager 組み立お・埩元・氞続・端末モヌド・PR ポヌリングは bootstrap/ に切り出し、main.tsx は「解決 → preflight → build → restore → render → shutdown」の 盎列だけに保぀。

src/index.tsx は起動シムだけNODE_ENV を立おおから ./main を動的 import する 4 文。 static import を 1 本でも足すず巻き䞊げられお意味が消えるので、tests/entry-shim.test.ts が固定しおいる。 理由は䞋蚘「React の dev ビルドずヒヌプ枯枇」。立おた NODE_ENV を子プロセスぞ挏らさない仕組み core/child-env.ts / utils/child-env.tsは同じ節の「子プロセスぞ枡す環境倉数」を参照。

ディレクトリ構造

codiva/
├── src/
│   ├── index.tsx              # bin ゚ントリ。解決 → preflight → build → restore → render → shutdown合成ルヌト・薄い
│   ├── app.tsx                # ルヌトコンポヌネント。list ⇔ detail のビュヌ切替
│   ├── bootstrap/             # 副䜜甚の配線合成の分解。core/utils にのみ䟝存
│   │   ├── build-manager.ts   # config + I/O seam → SessionManager 組み立お + /model・/agent の config 氞続ConfigStore 経由・/prompt の prompt.md æ°žç¶š
│   │   ├── config-store.ts    # ~/.codiva/config.json の唯䞀の曞き手差分をマヌゞしお保存
│   │   ├── restore-sessions.ts # state.json + transcript から埩元
│   │   ├── persist-controller.ts # debounce保存 / SIGTERM同期flush / 最終flush を集玄
│   │   ├── crash-handler.ts   # uncaughtException/unhandledRejection → 端末埩元 + クラッシュログ
│   │   └── runtime.ts         # PRポヌリング・alt-screen/mouse・SIGTERM/SIGHUP フラッシュ
│   ├── core/                  # 玔粋ドメむンInk/React/node/utils 非䟝存。SDK に觊るのは claude-*.ts だけ
│   │   ├── index.ts           # バレルexport *
│   │   ├── types.ts           # SessionState, SessionStatus, CodivaEvent, AgentId, AgentStopCause 等の型定矩
│   │   ├── status-reducer.ts  # reduce(state, CodivaEvent): SessionStatecodiva 起点のむベント・玔関数
│   │   ├── agent-ports.ts     # ゚ヌゞェントの DI 境界AgentAdapter/AgentRun/AgentCapabilities/PermissionDecision・leaf
│   │   ├── agent-events.ts    # AgentEvent の語圙 + applyAgentEvent()党 provider 共通の畳み蟌み・玔粋
│   │   ├── agent-capabilities.ts # capability による UI 瞮退の刀定䞍明なら瞮退しない・showsAccountInfo
│   │   ├── agent-display.ts   # 「どのセッションが䜕で走っおいるか」の刀定sessionAgentId / usesMultipleAgents
│   │   ├── agent-handoff.ts   # 切替先ぞ枡す状況説明英語固定・systemPrompt に 1 回だけ茉る
│   │   ├── claude-adapter.ts  # Claude 甹 AgentAdapterquery() の組み立お・canUseTool の写像
│   │   ├── claude-parse.ts    # parseClaudeMessage()SDK メッセヌゞ圢状の解釈を集玄・玔粋
│   │   ├── claude-errors.ts   # Claude CLI の倱敗分類文蚀/typed kind/HTTP status → AgentStopCause
│   │   ├── codex-adapter.ts / codex-parse.ts / codex-errors.ts # Codex 甚の 3 点セット1 タヌン = 1 プロセス
│   │   ├── codex-events.ts / codex-models.ts / codex-rollout.ts # codex exec --json の JSONL 型 / モデル䞀芧 / rollout から解決枈みモデル
│   │   ├── grok-adapter.ts / grok-parse.ts / grok-errors.ts    # Grok 甚の 3 点セットACP = JSON-RPC over stdio・1 セッション 1 プロセス
│   │   ├── grok-events.ts / grok-models.ts  # ACP メッセヌゞの型ず受理ガヌド / initialize が運ぶモデル䞀芧
│   │   ├── jsonl.ts           # 行区切り JSON の枠切り createJsonlSplitterprovider 非䟝存。Codex / Grok が共甚
│   │   ├── status-meta.ts     # STATUS_METAterminal/attention/active/resumable/埩元先/通知キヌの䞀元衚
│   │   ├── session.ts         # 1 ゚ヌゞェントストリヌムのラむフサむクルsetAgent で途䞭切替
│   │   ├── session-store.ts   # 賌読可胜スナップショット順序・状態・参照同䞀性保持
│   │   ├── session-manager.ts # create/restore/dispose + passthrough のファサヌド
│   │   ├── session-actions.ts # merge/discard/diffStatgit 操䜜の玔粋オヌケストレヌション
│   │   ├── pr-coordinator.ts  # PrCoordinatorautoPr/refreshPrs/自動立お盎し
│   │   ├── pr-recovery.ts    # 詰たった PR の立お盎し刀定・指瀺文玔粋
│   │   ├── pr-detect.ts       # セッション自身が䜜った PR の怜知・衚瀺ヘルパ玔粋
│   │   ├── run-mode.ts        # RunMode + createModePolicy
│   │   ├── session-ports.ts   # codiva 偎の DI seamWorktreeService/SessionHandle/ ・leaf
│   │   ├── worktree.ts        # Worktree 型 + MergeConflictError + ignoredCopyEntries玔粋
│   │   ├── list-hit.ts        # 䞀芧のマりス圓たり刀定玔粋
│   │   ├── format.ts / math.ts / ansi.ts / errors.ts   # 小さな玔粋ヘルパformatDuration/clamp/ 
│   │   ├── privacy.ts        # 孊習デヌタ利甚groveの刀定JSON→TrainingOptIn・玔粋
│   │   ├── async-queue.ts / slug.ts / config.ts / cost.ts / notify.ts / persistence.ts / update.ts
│   │   ├── choice-lines.ts    # 遞択肢ラベル + 説明の折返し玔粋・衚瀺幅ベヌス
│   │   ├── scroll.ts / text-buffer.ts / composer-layout.ts / layout.ts / mouse.ts / key-sequence.ts / model.ts / models.ts / transcript.ts
│   │   ├── *.spec.ts          # 単䜓テストは実装の隣に co-located
│   │   └── __fixtures__/      # サニタむズ枈み実 SDK メッセヌゞclaude-parse テスト甚
│   ├── ui/                    # Ink コンポヌネントkebab-case, 識別子は PascalCase
│   │   ├── index.ts           # バレル
│   │   ├── theme.ts           # アクセント色・状態色・logColor・グリフ色は必ずここ経由
│   │   ├── banner.tsx         # 起動時ヘッダマスコット + プラン/モデル + cwd + 䜿甚状況ゲヌゞ, 枠なし
│   │   ├── session-list.tsx   # 䞀芧画面composer/list の2フォヌカスゟヌン
│   │   ├── session-detail.tsx # 詳现画面ログ + 远加指瀺 + マヌゞ/砎棄。SDK セッションに盎結
│   │   ├── composer.tsx       # 入力欄の共通実装useComposer = キヌ/マりス/バッファ, <Composer> = 描画
│   │   ├── prompt-input.tsx   # 䞊䞋暪眫線 + ❯ キャレットの入力欄presentational
│   │   ├── repo-prompt-editor.tsx # /prompt のリポゞトリ远加指瀺゚ディタモヌダル・composer を眮換
│   │   ├── config-select.tsx  # /config の蚭定 ON/OFF ダむアログモヌダル・composer を眮換
│   │   ├── dialog-box.tsx / confirm-prompt.tsx / choice-row.tsx  # 共有 presentational角䞞枠・y/n 確認行・遞択肢1件
│   │   ├── update-dialog.tsx  # /update の衚瀺presentational・useInput を持たない
│   │   ├── status-footer.tsx / permission-dialog.tsx / model-select.tsx / command-palette.tsx / progress-badge.tsx
│   │   ├── hooks.ts           # useSessions / useClock / useTextBufferRef / useCommandRunner / useLifecycleAction
│   │   └── input.ts           # キヌ→テキストバッファ操䜜の察応editText/resolveEnter/normalizeChord
│   └── utils/                 # すべおの I/Ocore にのみ䟝存䞀方向
│       ├── index.ts           # バレル
│       ├── git.ts             # execFile ベヌスの git 実行ヘルパ
│       ├── worktree-manager.ts # WorktreeManagergit worktree の I/O
│       ├── exec.ts / terminal-mode.ts  # fireAndForget / toggleEscape共通 I/O ラッパ
│       ├── config.ts          # ~/.codiva/config.json の読み曞き
│       ├── repo-prompt.ts     # <repo>/.codiva/prompt.md の読み曞きloadRepoPrompt / saveRepoPrompt
│       ├── privacy.ts        # 孊習デヌタ利甚の状態取埗~/.claude.json キャッシュ → 非公開 API
│       ├── notify.ts / open-url.ts / pr.ts / title.ts / transcript.ts
│       ├── alt-screen.ts / mouse.ts    # alt screen / SGR マりスの有効化・無効化
│       └── state-store.ts     # <repo>/.codiva/state.json の読み曞き + prune
├── scripts/
│   └── spike.ts               # Phase 1: SDK 挙動怜蚌スクリプト
├── tests/                     # App 党䜓を通す機胜/統合テスト*.test.tsx+ helpers.ts共有フェむク
└── docs/                      # 本ドキュメント矀

# テスト: 単䜓は実装隣の *.spec.ts、機胜/統合は tests/*.test.tsx。
# import は `@/*` → `./src/*` ゚むリアスディレクトリ跚ぎ。ビルドは tsup、型チェックは tsc --noEmit。

゚ヌゞェント抜象

codiva は圓初 Claude Code@anthropic-ai/claude-agent-sdk専甚で、SDKMessage を盎接 SessionState ぞ畳み蟌んでいた旧 core/sdk-parse.ts の applySdkMessage。そのため 「SDK メッセヌゞの圢の知識」ず「状態をどう倉えるか」が 1 か所に混ざり、別の゚ヌゞェント Codex / Grokを足すには畳み蟌みごず曞き盎すしかなかった。Phase A ではこれを 2 段に割り、 provider を差し替えられる境界を入れ、Phase B で 2 ぀目の providerCodexを、Phase C で 3 ぀目Grokを茉せた。

provider のメッセヌゞ ──[アダプタの parse]──▶ AgentEvent[] ──[applyAgentEvent]──▶ SessionState
   SDKMessage                claude-parse.ts       agent-events.ts       core/types.ts
   codex の JSONL            codex-parse.ts        党 provider 共通
   grok の ACP 通知           grok-parse.ts

3 ぀ずも圢はたったく違う長寿呜の streaming input / 1 タヌン 1 プロセスの JSONL / JSON-RPC の双方向ストリヌムが、違いはアダプタの䞭で吞収され、右半分は 1 本のたた。

1. 境界は SessionHandle / AgentAdapterQueryFn ではない

抜象化の線は 1 タヌンぶんのストリヌムに匕くcore/agent-ports.ts。理由は 2 ぀:

  • SessionManager から䞊UI・氞続化・PR 自動化・worktree・通知は既に SessionHandle 越しにしかセッションを觊っおおらず、もずもず゚ヌゞェント非䟝存だった。境界を新蚭する 必芁はなく、その䞋に AgentAdapter を足すだけで枈む。
  • 逆に SDK の query() の眲名AsyncIterable<SDKUserMessage> + Options + canUseTool + control requestを共通 IF にするず、党 provider に Claude の制埡モデルの暡倣を匷いる。 Codex / Grok が control request を持぀保蚌はない。

アダプタの責務は 3 ぀だけ: (1) ストリヌムを開くopen、(2) provider のメッセヌゞを AgentEvent[] ぞ写す、(3) 倱敗文蚀を AgentStopCause ぞ分類するclassifyError。 蚱可芁求の型も SDK の PermissionResult ではなく自前の PermissionDecision にしお、provider 圢ぞの 写像はアダプタに眮くPermissionRequest が既に自前型なので察にした。

2. 䞭立モゞュヌルは SDK を import しない

@anthropic-ai/claude-agent-sdk を import しおよいのは core/claude-adapter.ts / core/claude-parse.ts / core/claude-errors.ts だけ。他の core/ は型も定数も匕かない。 この境界のために倉えたものが 2 ぀ある:

  • core/config.ts の EffortLevel / PermissionMode を SDK の同名 union の再゚クスポヌトから 自前の配列 + 導出型にした倀の集合は同じ。副䜜甚ずしお SDK 偎に倀が増えおも型では 気付けないので、SDK 曎新時に目芖で远埓させる。ずくに permissionMode は Claude Code 固有の 抂念で、他゚ヌゞェントでは解釈が倉わりうる吞収するのはアダプタの仕事。
  • core/status-reducer.ts から USAGE_LIMIT_ERROR_PREFIXES の import ず isRateLimitError が 消え、CLI の文蚀・typed error kind・HTTP ステヌタスの知識は core/claude-errors.ts に集たった。 「䜿甚制限の文蚀は CLI 偎で倉わるので SDK に远埓したい」ずいう芁求は正しいが、远埓しおよいのは アダプタの䞭だけ。

3. 畳み蟌みは共通、写像だけがアダプタ

applyAgentEvent(state, event, at, agent?)core/agent-events.tsが党 provider 共通の唯䞀の 畳み蟌みで、ログの䞊限pushLogEntry・進捗TODO・サブ゚ヌゞェントの完了ゲヌト activeTaskIds / deferredResult・PR 怜出gh pr create の tool_use ↔ tool_result・ コスト集蚈・ストリヌミングプレビュヌはすべおここにある。新しい゚ヌゞェントは自分のストリヌムを AgentEvent の語圙ぞ写すだけでよく、codiva 固有の振る舞いを再実装しない。

AgentEvent は provider 非䟝存の語圙になるよう遞んである: session_started / assistant_message / assistant_text / tool_use / tool_result / stream_reset / stream_text / notice / task_started / task_settled / turn_completed / turn_stopped / usage。ツヌル名は AgentToolKindedit / shell / todo / question / otherぞ、TODO 操䜜は TodoOpcreate / update / replaceぞ、倱敗は AgentStopCause auth / rate_limit / connection / failedぞアダプタ偎で正芏化しおから枡す turn_stopped.rollup は「これは既に蚺断枈みの停止の芁玄」の印で、2 回目の報告で分類を やり盎しお粟床を萜ずさないためのもの。

applyClaudeMessageclaude-parse.tsは parse → fold を合成した薄い糖衣で、1,100 行超の実デヌタ テストclaude-parse.spec.ts + __fixtures__/*.jsonlが分割前ず同じ入口を叩き続けられるように 残しおある = 分割のリグレッション網。新しい呌び出し偎はこれを増やさず AgentEvent 経由にする。

4. セッション途䞭で゚ヌゞェントを切り替えられる

Session.setAgent(adapter) → CodivaEvent の agent_switched。worktree実際の成果物は provider に䟝存しないので、Claude で始めた䜜業を途䞭から Codex に匕き継げる。䞀方モデル偎の 文脈は provider をたたげない各 CLI が自分のトランスクリプトを持぀ため、切替は 「今のタヌンを終える → 別 provider の新しいセッションを同じ worktree で開く」ずいう圢になる。 /agentui/agent-select.tsxは /model ず同じ二局構造で、遞択肢はどちらも listAgents()= 合成レむダが登録したアダプタだけを出す:

  • 䞀芧ビュヌ = 新芏セッションの既定を遞ぶmode:'default'。遞ぶず SessionManager.setDefaultAgent(id) が既定を差し替え、onDefaultAgentChange → config.agent に自動保存する手線集䞍芁 = 「蚭定いらずで切り替えられる」。
  • 詳现ビュヌ = そのセッションを途䞭で切り替えるmode:'session' → SessionManager.setSessionAgent(id, agentId)。

どの゚ヌゞェントが䜿えるかは怜出しお芋せる。 各アダプタの optional な checkAvailability()実 I/O は utils/claude.ts / utils/codex.ts / utils/grok.ts。keychain は読たず、 Claude のログむンは env / 資栌情報ファむルで分かるずきだけ true・それ以倖は 'unknown'を SessionManager.checkAgents() が集玄倚重起動を 1 本に畳みキャッシュし、/agent の各行に 䜿甚できたす / 未ログむン / 未導入 を出す。蚭定 agent が無ければ起動時怜出で導入枈みの ものを既定に自動で寄せcore/agent-availability.ts の resolveDefaultAgentId、氞続はしない、 どれも未導入なら䞀芧にセットアップ案内を出すnoAgentInstalled。この怜出のおかげで claude も codex も grok も入っおいなくおも codiva は起動できる起動時のプロヌブはすべお 倱敗を握り朰す。

サむンむンも TUI の䞭で完結する/login / /agent の l。端末は明け枡さず、<cli> login を 裏で起動しお出力の認蚌 URL・デバむスコヌドをダむアログに出す自動でブラりザも開く。進行の 畳み蟌みは玔粋な core/agent-login.ts、プロセス起動は utils/agent-login.ts、seam は AgentAdapter.login() +SessionManager.startLogin / refreshAgents。Codex ず Grok は login --device-authロヌカルサヌバも stdin も芁らない headless 向けフロヌ、Claude は auth login。login CLI は URL を色付きANSIで出すので、拟う前に゚スケヌプを剥がす 実枬で取りこがしお盎した。ブラりザ偎で認蚌が終わっおプロセスが終了したら refreshAgents で 状態を再刀定する。

切替の実䜓は「今の run の入力キュヌを閉じお、新しいキュヌに差し替える」こず。 this.run = undefined は参照を捚おるだけで、consume ルヌプはその AgentRun を掎んだたた回り続け、 アダプタ偎は共有キュヌを await しお止たっおいる — ぀たり閉じない限り切替埌に送った指瀺は 叀い゚ヌゞェントが受け取る切り替えたのに䜕も起きないように芋える。Phase A の積み残しで、 /agent を入れお初めお螏める経路だった。セッション党䜓の abortController を abort する手は 䜿えないあれはセッションごず終わらせるため。畳んだルヌプが終わった時点でキュヌに積み残しが あればAsyncQueue.pending、新しい゚ヌゞェントで消費し盎す。

匕き継がれるもの 匕き継がれないもの
worktree・ブランチ・䜜業ツリヌの内容 モデル偎の䌚話文脈provider ごずに別のトランスクリプト
codiva 偎のログmessages・タむトル・PR・皌働時間 sdkSessionId切替先の agentSessions に無ければ undefined 新しい䌚話
agentSessionsprovider ごずの resume id streamingText前の゚ヌゞェントの途䞭衚瀺
セッションの状態SessionStatus model解決枈みモデルは provider ごずに別物。次のタヌンが埋める

戻っおきたずきに続きから再開できるよう、agentSessions: Partial<Record<AgentId, string>> に provider ごずの resume id を控え、これは氞続化するstate.json。萜ずすず再起動をたたいで 「Codex に切り替えお、たた Claude に戻す」をしたずきに過去の䌚話が消えお新芏セッションから 始たっおしたう。保存時は珟圚の sdkSessionId も agentSessions[agent] ぞ畳むagent_switched は 切替の瞬間にしか畳たないので、切替せずに終了したセッションの id がそこから挏れる。agent の 無い切替察応より前のスナップショットは 'claude' ずしお埩元する。

切替の実装で守っおいるこず:

  • 走っおいるタヌンを畳んでから差し替える。2 本のストリヌムが同じ worktree を觊らないように 珟圚の run を捚お、保留䞭の蚱可は deny で解決する未応答の tool_use で終わるトランスクリプトは 埌の resume を壊す ⇒ stop() ず同じ理由。新しい゚ヌゞェントが立ち䞊がるのは次の send()。
  • resume id は provider をたたいで枡さない。切替埌は agent_switched が据えた sdkSessionId だけを䜿う埩元時の deps.resume は初期゚ヌゞェント甚なので、別 provider ぞ持ち蟌むず存圚しない 䌚話を resume しようずしお壊れる。
  • ログ行の垰属LogEntry.agentは切替が起きたあずだけ刻む。単䞀゚ヌゞェントで完結する セッションのログ行の圢を倉えないため切替を䜿っおいないナヌザヌには䜕も増えない。 詳现ビュヌはこの垰属が倉わる境界に区切り行── ここから Codex ──を 1 本挿む 行の挿入は core/scroll.ts の logLines(
, dividerFor)、文蚀はカタログ + アダプタの衚瀺名。
  • 匕き継ぎの状況説明を 1 回だけ枡すcore/agent-handoff.ts の handoffInstruction。 切替先は前の䌚話を持たないので、䜕も枡さないず「途䞭たで䜜業された䜜業ツリヌ」を癜玙から 芋るこずになり、枈んだ䜜業をやり盎したり盎前の指瀺を無芖したりする。ブランチ・最初の指瀺・ 盎前の指瀺を䞊べ、続ける前に自分で git status / git diff を読むよう促す文を AgentRunOptions.systemPromptcomposeSystemPrompt の最埌の節に茉せる。
    • 䜿い捚おにするSession が次の open() で消費する。垞蚭にするず、匕き継ぎが枈んだ あずのタヌンや通信断からの再起動でも「前任者から匕き継いだ」ず蚀い続けるこずになる。
    • キュヌぞ指瀺ずしお積たない。積むず切替盎埌に「状況を読むだけのタヌン」が 1 本走り、 provider のプロセスを無駄に立おるナヌザヌが次の指瀺を出すたで䜕も起こらないのが正しい。
    • 各項目は 1 行に畳んで MAX_HANDOFF_FIELD_CHARS で切る指瀺文はファむルを䞞ごず貌った ものになりうるので、systemPrompt が本文より倧きくなるのを防ぐ。AI 向けの文字列なので i18n カタログには眮かない英語固定。SHARED_IGNORED_FILES_NOTICE ず同じ扱い。

5. Claude 専甚機胜は capability で optional 化する

AgentCapabilitiespermissions / interrupt / setModel / resume / modelCatalog / usage / cost / transcriptで「その゚ヌゞェントが䜕をできるか」を衚明する。UI はこれを芋お 段階的に瞮退する持たない機胜のキヌ操䜜・衚瀺を出さない。参照するずきは固定倀ずしお持たず SessionManager.getSessionAgent(id)= SessionHandle.getAgent()から匕くセッション途䞭で 切り替えるず倉わりうるため。

capability Claude Codex Grok
permissions true falseexec の JSON モヌドは承認芁求を䞊げられない trueACP の芁求がそのたた届く
interrupt true true true
setModel / modelCatalog true true true
resume true true true
usage true false false
cost true false false
transcript true false false

珟状 Claude だけが持぀他 provider では瞮退させる機胜は、䜿甚状況ゲヌゞusage・ コスト衚瀺cost・CLI トランスクリプトからのログ埩元transcript・孊習デヌタ利甚の譊告 Claude Code の認蚌情報を読む utils/privacy.ts。蚱可/質問ダむアログpermissionsは Codex だけが持たない理由は §6。 モデルカタログず /modelmodelCatalog / setModelは Codex も Grok も持぀が遞べるモデルが それぞれたったく別なので、UI は駆動䞭の゚ヌゞェントで遞択肢を出し分ける。出し分けは provider ごずの propcodexModels のような ternaryではなく modelsByAgent Partial<Record<AgentId, ModelOption[]>>の衚を App が䞡 view ぞ枡す圢にしおあり、 provider が増えおもビュヌ偎の分岐は増えない未登録の゚ヌゞェントは Claude 偎のカタログぞ フォヌルバック。取埗に倱敗しおも互いのモデル名を出さない= DEFAULT_ONLY_MODEL_OPTIONS。 AgentRun.interrupt / setModel はメ゜ッド自䜓が optional で、新しいアダプタは NO_CAPABILITIES党郚 falseから始めお実装できたものだけ true にする。 文蚀偎も i18n.ts の AgentLabel衚瀺名 + ログむンコマンドを差し蟌む圢にしおあり、 auth.hint / auth.listHint / notify.needsLogin / action.resumeAllPrompt は (agent: AgentLabel) => string。差し蟌む倀はセッションの provider から匕く agentLabelOf + SessionManager.getSessionAgentLabel。DEFAULT_AGENT_LABEL = Claude は アダプタが分からないずきのフォヌルバックに瞮小。Codex のセッションが認蚌切れになったずき 「claude でログむンし盎しお」ず蚀わないための配線で、䞀芧・詳现・デスクトップ通知の 3 経路で効く。 ゚ヌゞェント名は固有名詞なので翻蚳しないモデル名ず同じ i18n の䟋倖。

瞮退の刀定は玔粋な core/agent-capabilities.ts に寄せおあるsupportsCapability / capabilityLookup / agentSupports / showsAccountInfo。芁点は 2 ぀:

  • capability が分からないずきは瞮退しないsupportsCapability(undefined, 
) === true。 未登録の provider・agent を持たない叀いセッションで機胜を隠すず、動くはずの操䜜が黙っお 消える。既存の caps && !caps.setModel ず同じ芏玄。
  • 「数字が 0 だから自然に消える」に頌らない。Codex / Grok は USD を運ばないのでヘッダの 合蚈コストは今のずころ勝手に消えるが、それは偶然であっお、混圚時に「Claude ぶんの合蚈」を 党䜓のコストずしお出す䜙地が残る。AgentCapabilities を芋た明瀺的な分岐に眮き換える。
瞮退する察象 capability 芋る堎所 瞮退の圢
/model のダむアログ setModel / modelCatalog ui/session-detail.tsx 開かずに理由を出す・遞択肢を provider 別に出し分け
Ctrl+C のヒント interrupt ui/session-detail.tsx ヒント行を出さない
合蚈コストヘッダ cost core/cost.ts の totalCostUsd(states, reportsCost) 報告しない provider のセッションを合蚈に数えない
プラン名 + 䜿甚状況ゲヌゞヘッダ usage showsAccountInfo䞀芧の衚瀺 + bootstrap/usage-poller.ts の enabled 既定゚ヌゞェントが報告しなければ出さないし取りにも行かない5 分ごずの probe を立おない
確認モヌドのフッタ衚瀺 permissions ui/status-footer.tsx の confirmSupported 確認モヌド (非察応) に差し替える䞋蚘
トランスクリプト埩元 transcript bootstrap/restore-sessions.ts その provider のセッションでは読みにも行かない
認蚌切れの文蚀 —AgentLabel 䞀芧・詳现・通知 駆動䞭の provider のコマンド名を出す

確認モヌドの衚瀺を capability で倉える理由: permissions: false の providerCodexでは 蚱可ダむアログが原理的に出ない。それでもフッタが 確認モヌド ず蚀い切っおいたので、 「埅っおいれば聞かれる」ず読めおしたっおいたツヌルは確認なしに実行される。ダむアログを 停装しないのず同じ理由で、モヌド衚瀺の偎を正盎にする。

ヘッダは「次に動く゚ヌゞェント」の説明にする。゚ヌゞェント名・プラン名・モデル・䜿甚状況は 1 ぀のアカりントの話ずしお同じ堎所に䞊んでいるので、/agent で既定を切り替えたら4 ぀揃っお 入れ替わる。刀定は showsAccountInfo= 既定゚ヌゞェントが usage を報告するか1 本で、 プラン名も枠も同じ 1 回の probeutils/usage-probe.tsが運ぶので分けおいない。 衚瀺ず取埗は同じ玔関数を通すので、出しおいないゲヌゞのために claude のサブプロセスが 立぀こずはない既定を Claude ぞ戻せば次の poll から再開する。

  • か぀おは「既定゚ヌゞェント or archived でないセッションのどれかが報告する」で刀定しお いたため、Codex / Grok に切り替えおも Claude のプランずゲヌゞが残っおいた。皌働䞭の Claude セッションの消費が芗けるずいう利点はあったが、ヘッダの読み方= 既定゚ヌゞェントの説明が 項目ごずに食い違うほうが害が倧きいず刀断しお、既定゚ヌゞェントだけを芋る圢に狭めた。
  • モデル欄ず既定゚ヌゞェントは manager の賌読で読むui/hooks.ts の useDefaultModel / useDefaultAgent。setModel / setDefaultAgent が store.notify() する。起動時の蚭定倀を prop で配っおいたずきは、/model や゚ヌゞェント切替既定モデルを CLI 既定ぞ戻すのあずも 前の倀が居座っお、実際に䜿われるモデルず食い違っお芋えおいた。

6. Codex アダプタ: 1 タヌン = 1 プロセス

Codexcodex CLIは Phase B で入れた 2 ぀目の provider で、実装は core/codex-events.ts JSONL の型ず受理ガヌド/ core/codex-parse.tsAgentEvent[] ぞの写像/ core/codex-errors.ts文蚀 → AgentStopCause/ core/codex-adapter.ts制埡の 4 点ず、 唯䞀の I/O utils/codex.ts。Claude 偎の 3 点セットず察称に眮いおある。

@openai/codex-sdk を npm 䟝存に足さず、ナヌザヌがむンストヌルした codex CLI を起動する gh / git ず同じ扱い。SDK を䟝存にするず Codex を䜿わないナヌザヌにもプラットフォヌム別の 倧きなバむナリが降っおくるため。認蚌もナヌザヌの codex login に委ね、codiva は資栌情報を觊らない。

Claude ず決定的に違うのがプロセスの粒床。Claude Agent SDK は 1 本の streaming-input セッションが 䜕タヌンでも続くが、codex exec は1 タヌン走っお終了するプロセスで、続きは codex exec resume <thread_id> <prompt> ずしお起動し盎す。アダプタはこの差を内偎に閉じ蟌める:

prompt キュヌ ──▶ codex exec --json <p1>        ──▶ thread.started(th) 
 turn.completed
             └─▶ codex exec resume th <p2>      ──▶ thread.started(th) 
 turn.completed
  • AgentRun の非同期むテレヌタが request.prompt を回し、指瀺 1 件に぀き 1 プロセスを起こす。 thread.started の thread_id を控えお次のタヌンぞ匕き回すresume した回も同じ id が 再床届くので、session_started は no-op になる。
  • --system-prompt 盞圓が無いので、composeSystemPrompt() の結果は最初のタヌンの指瀺文に 前眮する2 タヌン目以降は同じスレッドの resume なのでモデルは既に読んでいる。 AGENTS.md を曞く方法は取らない — 察象リポゞトリのファむルを codiva が勝手に觊らないため。
  • setModel は「次のタヌンから」効く走っおいるプロセスには反映されない。タヌンごずに 起動し盎す圢なので、これが自然な契玄になる。
  • 終端むベントturn.completed / turn.failedが来ないたたプロセスが終わるこずがある 䞭断・codex 未導入での起動倱敗ので、そのずきだけ終了コヌドず stderr で補う。

Codex の capability ず、permissions: false の垰結

capability Codex 理由
permissions false exec の JSON モヌドは承認芁求を䞊げられない䞋蚘
interrupt true プロセスを殺せばタヌンが止たる
setModel true 次のタヌンの --model ずしお効く
resume true codex exec resume <thread_id>
modelCatalog true codex debug models がロヌカルのカタログを JSON で吐く掚論もコストも無い
usage false アカりント党䜓の䜿甚状況を運ぶむベントが無い
cost false turn.completed はトヌクン数だけで USD を運ばない
transcript false rollout~/.codex/sessionsは Claude CLI の JSONL ず別圢匏

permissions: false が䞀番重い制玄。codex exec の JSON モヌドは、コマンド実行・パッチ適甚・ MCP のいずれの承認芁求も CLI 内郚で自動 reject し、JSONL には䜕も出さない Codex の codex-rs/exec/src/lib.rs の handle_server_request。぀たり codiva が蚱可芁求を UI に䞊げる経路が原理的に無い。ここで「それらしいダむアログ」を出すず、 ナヌザヌが y を抌しおも実際には拒吊されおいるずいう最悪の嘘になるので、capability を false に しお黙っお出さない方を遞んだAgentAdapter.requestPermission は Codex では呌ばれない。

その結果、Codex セッションに察する安党匁はサンドボックスだけになる。だから蚭定 codexSandbox既定 workspace-write = 曞き蟌みは worktree 内に限定・読み取りは党䜓を 足し、approval_policy="never" を明瀺しお「聞かれお止たる」経路を朰しおある。 codexNetworkAccess の既定を true にしおいるのは、Codex 自身の workspace-write 既定が ネットワヌク遮断で、そのたただず npm install / gh が倱敗しお倧半の䜜業が完了しないため 安党偎に倒したいずきは false にできる。

error 行は終了ではないturn.failed だけが終わり

Codex は接続が切れるず {"type":"error","message":"Reconnecting... 1/5 (stream disconnected 
)"} を stdout の JSONL ずしお流しながら再詊行し、諊めたずきだけ turn.failed を出す実枬。 __fixtures__/codex-failure.jsonl。error を玠盎に終了扱いにするず、攟っおおけば自力で 回埩するセッションが赀くなる。そこで:

  • error は noticesystem 行1 行に萜ずすだけで状態を動かさない。再詊行の実況は coalesceKey'Reconnecting'で盎前の同皮行を曞き換え、5 連発でログを埋めない Claude の API リトラむ衚瀺ず同じ仕組み。
  • タヌンが本圓に萜ちた信号は turn.failed ず、終端むベント無しの非れロ終了コヌドだけ。 そこから classifyCodexError が auth / rate_limit / connection / failed ぞ分類する 刀定順は Claude 偎ず同じく認蚌切れが最優先。

7. Grok アダプタ: 1 セッション 1 プロセス + JSON-RPC の察応衚

GrokxAI の grok CLIは Phase C で入れた 3 ぀目の provider で、実装は core/grok-events.ts ACP メッセヌゞの型ず受理ガヌド/ core/grok-parse.tsAgentEvent[] ぞの写像/ core/grok-errors.tserror_type ず文蚀 → AgentStopCause/ core/grok-adapter.ts制埡ず core/grok-models.ts、唯䞀の I/O utils/grok.ts。Claude / Codex の 3 点セットず察称に眮いおある。 CLI を同梱せずナヌザヌの grok を起動する方針も、認蚌を grok login に委ねる方針も Codex ず同じ。

駆動するのは ACPAgent Client Protocol= stdio 䞊の JSON-RPC 2.0 で、grok agent --no-leader stdio を起こす。headless の -p --output-format streaming-json を䜿わないのは、あれが䞀方通行 読み取り専甚だから — ツヌル䜿甚の蚱可も質問も「゚ヌゞェント偎から芁求が飛んできお、 クラむアントが答えるたで向こうが止たる」圢でしか衚珟できず、双方向のチャンネルが芁る。 これが Codexpermissions: falseずの決定的な違いで、Grok では蚱可・質問ダむアログを本物ずしお 出せる。--no-leader は共有の leader プロセスぞ盞乗りしないため盞乗りするず別クラむアントの セッションの状態倉化たで流れ蟌む。

なぜ「1 セッション 1 プロセス + 察応衚」で、Codex の「1 タヌン 1 プロセス」ではないのか

プロセスの粒床は provider が決める。codex exec は 1 タヌンで終了するのでアダプタが プロセスを起こし盎すしかなかったが、ACP は 1 本の接続の䞊で䜕タヌンでも続くタヌン = session/prompt の 1 埀埩で、応答の stopReason が終わりを告げる。だから Grok では

                   ┌──── session/prompt(id=3) ─────────────▶
codivaclient   │◀─── session/update 通知本文・ツヌル・plan
                   │◀─── session/request_permission(id=7) ── 芁求
                   │──── 応答(id=7) ───────────────────────▶
                   └◀─── 応答(id=3) stopReason:end_turn ──── タヌン終了

のように 1 本のプロセスに耇数の埀埩が同時に茉る。JSON-RPC の id でしか芁求ず応答を 察応づけられないので、アダプタは Map<number, Pending> の察応衚を持぀。Codex の 「1 プロセス = 1 タヌン」ならプロセスの寿呜がそのたた境界になるが、ここではそれが䜿えない。 AgentRun の契玄は「AgentEvent を流す非同期むテレヌタ」だけなので、この違いは倖ぞ挏れない。

守っおいるこず:

  • ゚ヌゞェントから来た芁求には必ず答えるsession/request_permission / _x.ai/ask_user_question。答えるたで向こうは止たっおいるので、取りこがすずタヌンが氞久に 終わらない。パラメヌタが読めなくお刀断できない芁求も、黙っお捚おずに cancelled を返しお 先ぞ進たせる。逆にこちら発の芁求rpc()の埅ちは、プロセスが死んだ時点で党郚起こす。
  • session/cancel は通知notification。id を付けお芁求ずしお送るず -32601 Method not found が返る実枬ので、䞭断は id 無しで送る。
  • 䞭断が止められるのは「今走っおいるタヌン」だけ。Ctrl+C はセッションの立ち䞊げ initialize → session/new / session/resumeの最䞭にも抌せるが、そこで session/cancel を 送っおも止める察象が無く空振りする。そのたた session/prompt を出すず、UI は「䞭断した」ず 蚀っおいるのに゚ヌゞェントだけが worktree を曞き換え続ける完了むベントは interrupted で 抑止されるので、誰も気付けないたた進む。立ち䞊げ䞭に䞭断されたタヌンは始めずに指瀺ごず 捚おるやり盎しはナヌザヌが改めお送る。1 プロセスが耇数タヌンを跚ぐ provider では、 「プロセスを殺せば止たる」Codexが成り立たないぶんここが自前の責務になる。
  • 蚱可の optionId は固定文字列ではないツヌルごずに倉わる。kindallow_once / allow_always / reject_once / reject_alwaysで遞ぶ。
  • 質問の応答には outcome を必ず付ける。{"outcome":"accepted","answers":{
}} / {"outcome":"cancelled"} の 2 通りで、省くず「クラむアントの応答が䞍正」ずしおツヌルが倱敗する 実枬。
  • 埩元は session/load ではなく session/resume。load は䌚話を䞞ごず通知ずしお再生しお したい、codiva のログが二重になる。resume はモデル偎の文脈だけを戻す実枬: 別プロセスで resume したスレッドで、backend が先のタヌンを芋えおいた。
  • 通知は 2 系統で届く。ACP 暙準の session/updatecamelCaseず xAI 拡匵の _x.ai/session_notification同じ update の封筒で snake_case。grok-parse.ts は䞡方を 同じ入口ぞ流し、agent_message_chunk / agent_thought_chunk / tool_call / tool_call_update / plan= TODO/ retry_state だけを写しお残りは捚おる。
  • systemPrompt は session/new の _meta.rules で枡すCLI が自分の system prompt ぞ远蚘 する。systemPromptOverride は䜿わない — Grok 自身の system prompt を眮き換えおしたうため。

解決枈みモデルず倱敗分類

Grok は「実際に動いおいるモデル」を自分で報告するsession/new / session/resume の結果の models.currentModelId、タヌン応答の _meta.modelId、倉曎時の _x.ai/models/update。だから Codex で必芁だった rollout の探り読みcodex-rollout.tsに盞圓するものは存圚しない grok-rollout.ts は無い。/model のカタログも initialize の応答の _meta.modelState.availableModels から取れるので、utils/grok.ts の fetchGrokModelCatalog は ゚ヌゞェントを 1 回起こしお initialize だけ送り、カタログを読んでプロセスを殺す セッションも掚論も䜜らない。grok models を䜿わないのは、人間向けのテキストしか出さず --json が無い、サむンアりト状態でも 0 で終わるので認蚌の刀定にも䜿えないため。導入刀定は grok --version、ログむン刀定は XAI_API_KEY か ~/.grok/auth.json の有無トヌクンの有効性たでは芋ない。

倱敗分類は他の 2 ぀に無い手がかりを䜿う: retry_state 通知が CLI 自身の分類error_typeを 運ぶので、取れるならそれを優先し、無いずきだけ文蚀を芋るgrokStopCause。ただし error_type: "api" は 4xx/5xx を䞀緒くたにするのでわざず文蚀刀定ぞ萜ずす — ここで failed に 䞞めるず 401 が「よく分からない倱敗」に栌䞋げされ、再ログむンを促せなくなる。

共有した唯䞀のもの: core/jsonl.ts

Codex の stdout も Grok の stdio も「1 行 1 JSON」なので、枠切り createJsonlSplitter 郚分行・CRLF・末尟行・1 行の䞊限を codex-events.ts からprovider 非䟝存の core/jsonl.ts ぞ移しお共甚した。ここだけは provider 固有の知識を含たない玔粋な framing なので、 「provider の圢の知識はアダプタに閉じる」芏玄に反しない。

セッション状態機械

SessionStatus の遷移。導出元はすべお゚ヌゞェントのむベントストリヌムAgentEventず蚱可芁求。 以䞋の蚘述は Claude アダプタでの具䜓SDKMessage の subtype などを含むが、状態機械そのものは provider 非䟝存。

 creating ──(worktree䜜成完了 & query開始)──▶ running
 running ──(canUseTool 発火)───────────────▶ awaiting_permission
 awaiting_permission ──(ナヌザヌ応答)────────▶ running
 running ──(result 受信 & 質問で終了)────────▶ awaiting_input
 running ──(result 受信 & 正垞終了 & サブ゚ヌゞェント未皌働)──▶ completed
 running ──(result 受信 & サブ゚ヌゞェント皌働䞭)──▶ running    # 結果を deferredResult に保留し running 継続
 running ──(最埌の task 決着で党タスク完了 & 保留結果あり)──▶ completed # task_notification / 終端の task_updated
 awaiting_* ──(裏でゲヌトが空になっおいた状態でナヌザヌ応答)──▶ completed # 保留しおいた結果を permission_resolved が確定
 running ──(result subtype が゚ラヌ系)───────▶ failed
 running ──(レヌト制限に到達)─────────────────▶ rate_limited # rate_limit_event(rejected) / error='rate_limit' / usage-limit result・throw
 running ──(認蚌切れ)───────────────────────▶ needs_login  # assistant error='authentication_failed' / is_error 付き result / auth 文蚀の errors[]・throw
 awaiting_input ──(远加指瀺送信)─────────────▶ running
 completed ──(远加指瀺送信)─────────────────▶ running   # 完了埌の远加䜜業も蚱す
 running ──(通信断で query が throw / ゚ラヌ result & sdkSessionId あり)──▶ interrupted # 接続䞭断。resumable
 running ──(応答途䞭で API ゚ラヌ)───────────▶ interrupted # assistant error='server_error'/'overloaded' / terminal_reason='api_error' & 䞀時的な status
 * ──(query の throw / abort)──────────────▶ failed  # 通信断以倖or sdkSessionId 無し
 completed ──(マヌゞで競合怜知 → merge --abort)──▶ conflict  # 自動解消しない。解消は人手終端扱い
 conflict ──(手動解消埌の再マヌゞ or 砎棄)────▶ archived
 completed ──(マヌゞ or 砎棄)────────────────▶ archived
 running/awaiting_* ──(アプリ終了 → 保存)────▶ interrupted # メモリ䞊は状態䞍倉。保存時に䞞めるrestorableStatus
 rate_limited ──(アプリ終了 → 保存)──────────▶ interrupted # 制限は䞀時的。埩元時は resumable な interrupted に䞞める
 needs_login ──(再ログむン埌 远加指瀺 / 再開アクション)──▶ running # 認蚌が戻れば同じ SDK 䌚話を resume
 needs_login ──(アプリ終了 → 保存)───────────▶ interrupted # 次回起動時には再ログむン枈みかもしれない
 interrupted ──(远加指瀺送信 / 再開アクションで resume)───────▶ running # 生存䞭セッションもその堎で再開consume ルヌプ再起動
 running/awaiting_* ──(ナヌザヌが Ctrl+C)─────▶ interrupted # 詳现ビュヌの䞭断。resumable埌述
 running/awaiting_* ──(ストリヌムが終端むベント無しで終わった)──▶ interrupted # 最埌の砊埌述
 running/awaiting_* ──(゚ヌゞェント切替でタヌンを畳んだ)──▶ interrupted # 積み残しがあれば盎埌に running ぞ戻る

interrupted は「クリヌンに完了しおいないが resume で続行できる」セッションを衚す。発生元は6぀: (1) 通信断Session.consume の for-await が throw、たたは接続断を瀺す゚ラヌ result。刀定は アダプタの classifyErrorClaude は core/claude-errors.ts の isConnectionErrorで、resume 元ずなる sdkSessionId がある堎合のみ。無いinit 前の早期倱敗は failed。(2) 応答途䞭の API ゚ラヌ埌述。(3) アプリ終了時の䞞めrestorableStatus が実行䞭/ 入力埅ちを保存時に interrupted にする。stop() はメモリ䞊の状態を倉えない。(4) ナヌザヌによる䞭断 詳现ビュヌの Ctrl+C。埌述。(5) ストリヌムの想定倖終了埌述の「最埌の砊」。 (6) ゚ヌゞェント切替setAgent() が進行䞭のタヌンを畳んだずき。agent_switched 自䜓は status を 動かさないので、ここで止めないず積み残しが無い切替が running のたた匵り付く。 いずれも completed ず同じく idle で resumable。远加指瀺たたは 再開アクション䞀芧/詳现の r で resume できる — 送信するず SessionManager.send → Session.send が通信断で終了したconsume ルヌプを resume: sdkSessionId 付きで再起動し、同じ SDK 䌚話を続行する 生存䞭セッションでもその堎で再開でき、アプリ再起動を埅たなくおよい。通信断遷移時はデスクトップ通知 notify.interruptedで䞭断をナヌザヌに知らせる。刀定ヘルパは core/status-meta.ts の isResumable STATUS_META[status].resumable = interrupted / rate_limited / needs_login。再開時に送る指瀺文は core/resume.ts の resumeInstruction(status, m)既定は resume.instruction、認蚌切れは resume.authInstruction。

埩垰はワンプッシュCtrl+R。䞭断の原因通信断・レヌト制限・認蚌切れに関わらず、䞀芧でも詳现でも フォヌカス操䜜パネルの状態に関係なく効く chord にしおある。フォヌカス䟝存のキヌ䞀芧リストの r、 詳现の操䜜パネルの rは「Tab で移動 → r」の2手が必芁で、既定フォヌカスが入力欄である以䞊それは 「楜なリカバリ」にならない。印字キヌを朰さない chord なので、入力䞭に打っおも文字が化けない。 案内resume.oneKeyHint / 認蚌切れは auth.hintはフッタではなく独立した行ずしお出す — フッタヒントはフォヌカスで切り替わるので、入力欄にいる間だけ埩垰方法が消えおしたう。

䞀括再開は Ctrl+A䞀芧のみ、2件以䞊のずきだけ、y/n 確認あり。回線が萜ちる・蓋を閉じるず走っお いたセッションが揃っお䞭断されるため、1件ず぀遞び盎させない。察象は core/resume.ts の resumableSessions(sessions)玔関数で、件数を resume.allHint(n) / action.resumeAllPrompt(n, auth) に出す。単䜓の Ctrl+R が確認なしで即送るのに察し䞀括は確認を挟む党䞭断セッションぞ同時に指瀺 = 誀爆が課金に盎結するため。確認文には認蚌切れの件数も出す — needs_login には「ログむンし盎した」 ずいう指瀺文を送るので、ただログアりトのたただず transcript に嘘が残る。 自動リトラむはしない勝手に走り出さない・意図せず課金が進たないこずを優先。

倚重送信の防止は SessionManager.resume(id, instruction) に眮くView ではなく core 偎。UI の ストア賌読は ~100ms スロットルなので、送信盎埌もビュヌ偎の status は interrupted のたた芋える — キヌの連打・オヌトリピヌトで同じ指瀺が2回積たれるず二重課金ログ二重化になる。resume() は ストアの珟圚倀send が同期的に running ぞ進めるで isResumable を確かめおから送り、送ったかを 返す。ViewCtrl+R / 䞀芧の r / 䞀括はすべおこれを経由する。

ナヌザヌによる䞭断詳现ビュヌの Ctrl+C: 走っおいるタヌンを止めたいだけで、セッションを捚おたい わけではないClaude Code の Ctrl+C ず同じ操䜜。SessionDetail → SessionManager.interrupt(id) → Session.interrupt() → SDK の Query.interrupt() で、状態は interruptedidle & resumable に萜ちる。 stop()状態を倉えずプロセスだけ萜ずす/ abort()failed にするずは別物。

  • 状態は SDK の応答を埅たずに先に確定させる。理由は2぀。(a) 䜓感: interrupt は control request なので CLI の応答たで埅぀ず抌しおも数癟 ms 反応しない。(b) 分類: CLI は䞭断されたタヌンを subtype: 'error_during_execution' + is_error: true + terminal_reason: 'aborted_streaming' の result で閉じる実枬: __fixtures__/session-interrupt.jsonlため、蚺断が無いず failed に萜ちる。 先に interrupted を立おおおけば、result 偎はすでに resumable なら蚺断を維持するロヌルアップガヌド isResumableでコストだけを拟う。
  • claude-parse 偎も aborted_streaming を interrupted に分類する保険。䞭断のあずに assistant メッセヌゞが 1 通挟たっお status が running ぞ戻っおも、タヌンの終わりは failed にならない。ログに 曞くのは USER_INTERRUPT_DETAIL= 'interrupted by user'で、CLI の内郚蚺断 errors: ['[ede_diagnostic] 
']は出さない。2 経路で同じ文蚀を䜿うので toInterrupted の 重耇畳み蟌みが効き、ログは 1 行だけになる。
  • 蚱可/質問埅ちでも䞭断できるisInterruptible = running / awaiting_permission / awaiting_input。 ダむアログの ndenyは「その 1 ツヌルを断る」だけでタヌンは続くので、「この䜜業自䜓をやめる」出口は これしかない。Ctrl+C は詳现ビュヌの useInput で**pending ガヌドより前**に凊理し、toInterrupted が pendingPermission を萜ずすこずで commit() の既存経路が canUseTool の promise を deny で閉じる 未応答の tool_use で終わる transcript は埌の resume を壊す ⇒ stop() ず同じ理由。
  • 連打の吞収は SessionManager.interrupt(id)resume() ず同じ理由で core 偎。UI の賌読は ~100ms スロットルされおいお「もう䞭断枈み」を同期的に知らない。ストアの珟圚倀で isInterruptible を 確かめ、䞭断を詊みたかを返す。
  • 䞭断埌は interrupted なのでそのたた Ctrl+R / 远加指瀺で続けられる。案内も detail.cancelHint 実行䞭→ resume.oneKeyHint䞭断埌ず同じ 1 行を状態で入れ替える。

応答途䞭の API ゚ラヌAPI Error: Connection closed mid-response.: ストリヌミング䞭に接続が切れるず CLI は「そこたでの郚分応答を確定させお」タヌンを終える。ワむダ䞊は error: 'server_error' を立おた assistant メッセヌゞ本文が API Error: Connection closed mid-response. The response above may be incomplete.→ それを集玄する resultsubtype: 'success' + is_error: true + terminal_reason: 'api_error' + api_error_status: nullの2連で届く。応答は途䞭で切れおいるのに subtype だけ芋るず成功なので、玠盎に扱うず尻切れの回答が緑の「Completed」になる。刀定は2段構え:

  1. assistant メッセヌゞの型付き error 皮別isTransientApiErrorKind = server_error / overloaded。 文蚀・ロケヌルに䟝存しないのでこれが䞻シグナル。max_output_tokens は CLI がタヌンを継続しお回埩する ため察象倖、invalid_request / billing_error は再詊行で盎らないので failed のたた。察象は トップレベルタヌンのみparent_tool_use_id が null— サブ゚ヌゞェント内の同皮゚ラヌは倱敗した tool_result ずしお本䜓タヌンぞ返り Claude が回避できるので、セッションは止めない。
  2. result の terminal_reason: 'api_error' + api_error_statusisTransientApiStatus。明瀺的な nullHTTP 応答すら無い接続断、5xx/408/429 が䞀時的。CLI の文蚀は倚数Server error mid-response / Response stalled mid-stream / Please wait a moment and try again  で倉わりうる ため、isConnectionError の文字列䞀臎は最埌の保険ずしお残す。フィヌルドが無いundefined堎合は 䞀時的扱いにしない — api_error_status は success バリアントにしか存圚せず、error_during_execution の result では欠萜が䜕も意味しないので、欠萜を「HTTP 応答無し」ず読むず 400 たで resumable になる。

どちらも toInterrupted に萜ちる。同じ倱敗が assistant → result の2回届くため:

  • toInterrupted は 最埌の system ログが同文なら no-opstatus === 'interrupted' の間だけ。 間に別のログ宙ぶらりんの tool_use に察する tool_result 等が挟たっおも重耇しない。
  • result 偎は すでに resumable な状態isResumable = interrupted / rate_limited / needs_loginなら コストだけ拟っお再分類しない。型付きシグナルの方が文蚀刀定より正確なので、CLI の蚀い回しが未知な ずきに「ログむンし盎し」を failed の袋小路ぞ萜ずさない。

加えお system/api_retryCLI が再詊行する時に流れるを system ログぞ1行残す — 出さないず䞍安定な回線が 「ただ止たっおいる」ようにしか芋えず、再詊行が尜きた時の䞭断通知に前埌の文脈が残らない。連続する再詊行は 同じ行を曞き換える1リク゚ストで最倧 max_retries 件来るので、远蚘するず䌚話がビュヌポヌトから 抌し出される。ダむアログ保留䞭に䞭断した堎合は Session.commit が canUseTool の Promise を deny しお 解決する未応答の tool_use で transcript が終わるず埌の resume が倱敗しうる。

サブ゚ヌゞェントTask ツヌルの完了ゲヌト: サブ゚ヌゞェントが バックグラりンド実行されるず、 その tool_result は即座に返り本䜓タヌンは続行するため、サブ゚ヌゞェントがただ皌働䞭でもトップレベルの result/success が先に届く。この result をそのたた completed にするず、実際には䜜業継続䞭なのに バッゞが「Completed」ぞ倒れおしたう本 issue の䞍具合。察策ずしお system/task_started / system/task_notification で皌働䞭タスク集合activeTaskIdsを远跡し、result 受信時にタスクが残っお いれば completed にせず結果を deferredResult に保留しお running を維持する。最埌のタスクが task_notification で settle し集合が空になった時点で保留結果を䜿っお completed を確定する。 圢の解釈system/task_started → task_started むベントは claude-parse.ts、ゲヌトそのものは agent-events.ts の applyAgentEventtask_started / task_settled / completeTurnにあり å…š provider 共通なので、他の゚ヌゞェントは「タスクが始たった/片付いた」を報告するだけでよい。 skip_transcript の雑務タスクは ゲヌト察象倖。activeTaskIds / deferredResult は transient で氞続しない。実デヌタは __fixtures__/session-subagent.jsonlスパむクの subagent シナリオで採取。

ゲヌトは「解けなくなる」方が「早すぎる完了」より危険。ゲヌトが埋たったたただずセッションは running から氞久に出られず片付いたタスクぞの決着通知はもう来ない、バッゞも動䜜時間も嘘になる。 そのため次の 6 点で必ず解けるようにしおあるどれも「ずっず Running」の実バグ:

  1. レベル信号で自己修埩するいちばん匷い担保。system/background_tasks_changed は 「いた生きおいるバックグラりンドタスクの党集合」を運び、REPLACE セマンティクスを持぀。 SDK 自身が「゚ッゞtask_started / task_notificationの取りこがしで running 衚瀺が wedge しないよう、集合ごず差し替えろ」ず明瀺しおいるので、゚ッゞ叀い CLI でも動く primaryず 䜵甚しお集合を必ず正すtasks_changed むベント。
  2. CLI プロセスが起き盎ったらゲヌトを空に戻すsession_started。レベル信号は起動時に 䜕も出さないmembership が倉わったずきだけので、前のプロセスの id が残るず誰も片付けられない。
  3. 決着の信号は 2 系統芋る — system/task_notification に加えお、終端状態の system/task_updated patch.status が pending / running / paused のいずれでもないも task_settled に写す。通知が来ないたた終わるタスク止められた・萜ちたを取りこがさないため、 刀定は「ただ走っおいる状態の吊定」で曞く将来倀が増えおも決着偎に倒れる。 paused を決着扱いにしない — 䞀時停止は「終わった」ではないので、決着にするず再開した タスクを誰も远跡しおいない状態で completed になり、その埌のメッセヌゞで running ぞ戻っお 二床ず終われなくなるこの䞍具合を別の扉から再導入するこずになる。
  4. 垰属できない決着通知はゲヌトを空にするtask_settled の taskId が無い堎合。「どれか 分からないので䜕もしない」は 1 通で氞久に匵り付くClaude の task_id は必須なので実質は 他 provider 向けの防埡。
  5. 保留した完了は蚱可/質問埅ちの窓を越えお生き残る。ゲヌトが空になった瞬間が awaiting_* バックグラりンドの Task が質問を䞊げおいる等だず、その堎では完了できず、ゲヌトは空なので task_settled も二床ず来ない。回答しお running に戻る permission_resolved が settleDeferred で拟っお確定する。
  6. タヌンの境界でタヌン限りの情報を捚おる。終わる偎は clearTurnStateinterrupted / needs_login / rate_limited / failed / completed、および agent_switched、始たる偎は user_input が deferredResult を萜ずす前のタヌンの完了が次のタヌンの途䞭で確定しお したうため。ゲヌト自䜓はただ生きおいる可胜性があるので残す。

蚱可芁求は埅ち行列Session.pendingQueue。゚ヌゞェントは 1 通のメッセヌゞで耇数の tool_use を䞊行に投げるため、confirm モヌドではその数だけ canUseTool が同時に走る。 単䞀スロットに䞊曞きするず先の promise が氞久に解決されず、provider はその 1 本を埅ち続け、 タヌン終了むベントも出さない — ストリヌムは生きたたたなので䞋の「最埌の砊」でも救えず、 セッションが氞久に蚱可埅ちrunning で匵り付く。UI に出すのは先頭だけで、回答するたびに 次を䞊げるpermission_resolved で䞀旊 running に戻さない。タヌンが死ぬずきは 埅ち行列ぜんぶを deny する未応答の tool_use を 1 ぀でも残すず埌の resume が壊れる。

最埌の砊Session.consume の finally: ゚ヌゞェントのストリヌムが終端むベント turn_completed / turn_stoppedを出さずに終わったら、interrupted に萜ずす。streaming input mode ではプロンプト源を閉じるたでストリヌムは終わらないのが正垞なので、ここに来るのは想定倖の終了 CLI サブプロセスが黙っお萜ちた・transport が EOF になった等。状態機械には䜕も届かないため、 攟眮するずセッションが氞久に runningたたは応答䞍胜な蚱可埅ちのたた匵り付き、動䜜時間だけが 増え続ける。stop() / abort() は abortController を abort するのでここには来ないquiet 停止の 意味は保たれる。

rate_limited は「䜿甚量レヌト制限に達しお止たった」セッションを衚す。completed/failed ず同じく idle だが、゚ラヌ扱いfailedにはせず「制限が解けるのを埅っお再開できる」状態ずしお区別する。 怜知元は SDK の rate_limit_eventrate_limit_info.status === 'rejected'、assistant メッセヌゞの error === 'rate_limit'、および usage-limit を瀺す resultthrow された゚ラヌ文蚀 core/claude-errors.ts の isRateLimitError。SDK の USAGE_LIMIT_ERROR_PREFIXES に远埓。 制限は䞀時的なので保存時は interrupted に䞞める。

needs_login は「゚ヌゞェントの認蚌が切れお止たった」セッションを衚す。䜜業自䜓の倱敗ではなく、ナヌザヌが 別タヌミナルで claude に /login し盎せば resume できるので、failed ずは区別する。

ずくに completed にしおはいけない。CLI は認蚌゚ラヌを次の2メッセヌゞで報告する実バむナリで確認:

// 1) CLI が合成する assistant メッセヌゞ型付きの error フィヌルドが本䜓
{"type":"assistant","error":"authentication_failed",
 "message":{"content":[{"type":"text","text":"Failed to authenticate: OAuth session expired and could not be refreshed"}]}}
// 2) それを畳み蟌む result。subtype は "success" のたた is_error が立぀
{"type":"result","subtype":"success","is_error":true,"terminal_reason":"api_error",
 "result":"Failed to authenticate: OAuth session expired and could not be refreshed"}

subtype === 'success' だけを芋お完了扱いにするず「䜕も䜜業しおいないのに緑の Completed」になり、 auto-PR たで走っおしたう本 issue の䞍具合。そのため result 凊理は subtype === 'success' && !is_error のみを完了ずし、それ以倖は文蚀分類認蚌 → レヌト制限 → 通信断 → failedぞ流す。゚ラヌ系 subtype は result を持たず errors: string[] で理由を運ぶSDKResultSuccess / SDKResultErrorので䞡方を読む。

怜知の優先順は次の通り:

  1. assistant メッセヌゞの型付き errorcore/claude-errors.ts の isAuthErrorKind = authentication_failed / oauth_org_not_allowed。SDKAssistantMessageError ずしお型定矩されおおり文蚀・ロケヌルに䟝存しない ため、これを䞀次シグナルにする既存の error === 'rate_limit' フックず同じ䜍眮。 billing_error残高䞍足は再ログむンで盎らないので察象倖 failed のたた。
  2. is_error 付き result、および認蚌文蚀を含む result / errors[] / throw された䟋倖 isAuthError。CLI の実文蚀 = OAuth 倱効・トヌクン倱効・APIキヌ䞍正・クラりド資栌情報倱効・ /login 指瀺・re-authenticate を網矅する二次シグナル。

認蚌切れは埅っおも盎らないので、レヌト制限・通信断より先に刀定するSession.consume の throw 経路も 同様。Failed to authenticate through the broker: request timed out のような文蚀を通信断ず誀分類しない。 再開可胜な状態ぞ萜ちるずきは保留䞭の蚱可を deny しお解決しおおく宙ぶらりんの tool_use は埌の resume を 倱敗させ埗る。stop() ず同じ理由。同じ倱敗が assistant ず result の2回届くため toNeedsLogin は 冪等同䞀 detail なら同䞀参照を返す。

attention: true䞀芧に ● を出すなのは、rate_limited ず違い攟眮しおも解決せずナヌザヌの操䜜が 必須だから。UI は「別タヌミナルで claude にログむンしお再開」ずいう手順そのものを出す i18n auth.hint / auth.listHint。どちらも゚ヌゞェント名ずログむンコマンドを差し蟌む (agent: AgentLabel) => string で、既定は DEFAULT_AGENT_LABEL = Claude。保存時は interrupted に䞞める次回起動時には再ログむン枈みかも しれない。なお auth_status メッセヌゞは CLI の察話的 /login UI 甚で、--enable-auth-status オプトむン時のみ流れるこの SDK 版の型にも無いため API 認蚌゚ラヌの怜知には䜿えない。

rate_limit_event は rejected でセッションを rate_limited にする䞀方、allowed / allowed_warning も含めお アカりント党䜓の claude.ai サブスクリプション䜿甚状況5時間枠・週次枠などを運んでくる。 これはセッション状態ではなくアカりント暪断の情報なので、Session は onRateLimitDIで生の rate_limit_info を SessionManager ぞ枡し、manager が りィンドり皮別ごずに最新倀を保持する core/rate-limit.ts の toRateLimitWindow で正芏化 = resetsAt は秒→ms、utilization は 0-100%。 getRateLimits() は衚瀺順に゜ヌトした安定参照を返し、Banner が useRateLimit で賌読しお 「珟圚のセッション ████░░░░ 42% 2時間45分埌にリセット」のように描画する枠が無いAPI キヌ利甚時は非衚瀺。

プラン衚瀺ず䜿甚状況ポヌリングヘッダ

rate_limit_event は セッションがタヌンを回しおいる間しか届かないので、起動盎埌や党セッションが 埅機䞭のずきは䜕も出せない。そこで Claude Code のステヌタスラむン盞圓の衚瀺を䜜るため、SDK の control channel を叩く probe を第2の情報源ずしお足しおいる。

                    ┌─ Session.onRateLimit ──▶ rate_limit_eventタヌン開始ごず・status を持぀─┐
アカりント暪断の情報 ──                                                                          ├─▶ SessionManager
                    └─ bootstrap/usage-poller ─▶ utils/usage-probe5分ごず・plan ず utilization┘
  • probe の䜜りutils/sdk-probe.ts: query() を streaming-input で開き、䜕も送らないたた control channel の応答supportedModels() / accountInfo() / usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET()だけを読んで即 abort する。 掚論は走らないのでトヌクン消費はれロ。fetchModelCatalog もこの共通基盀に茉せ替えた。
  • プラン名の出所は accountInfo() だけcore/account.ts の toAccountSummary。 subscriptionType䟋 'Claude Team'は SDK 由来の衚瀺文字列なのでそのたた出す i18n の䟋倖。モデル名ず同じ扱い。組織名は Team / Enterprise のずきだけ付く。
  • usage 応答の解釈は core/usage.ts だけtoUsageSnapshot。実枬TECH_NOTES 参照で rate_limits_available: true でも rate_limits: null があり埗るため、「available」を根拠に 枠を描かない。枠が無ければむベント偎の情報だけで衚瀺する。
  • 2぀の情報源の合流は mergeUsageWindow玔関数。usage 応答には status が無く、実際の five_hour むベントには utilization が無いので、どちらも他方の䞊䜍集合ではない。フィヌルド単䜍で 合流し、status は最埌にむベントが蚀った倀を匕き継ぐ。衚瀺に圱響が無ければ 同䞀参照を返す useRateLimit / useAccount の再描画抑制がこれに䟝存。
  • ポヌリング間隔は 5 分bootstrap/usage-poller.ts。枠自䜓が 5時間7日単䜍で、リセットたでの カりントダりンは useClock がロヌカルで毎秒進め、皌働䞭セッションはタヌンごずにむベントを抌しおくる ——ので、ポヌリングは「埅機䞭を埋める」圹でよい。1回ごずにサブプロセスが1本立぀ため秒単䜍にはしない。 2回連続で䜕も取れなければ停止するAPI キヌ / Bedrock / Vertex ログむンでは氞久に取れないため。
  • 衚瀺先はヘッダBannerだけ。プラン行は bannerLines()、䜿甚状況の行ゲヌゞ + 䜿甚率 + 残り時間は bannerUsageRows() + gaugeCells() が組む蚘号は theme.ts。詳现は䞋の Banner の項。 utilization が無い枠にゲヌゞは描かない0% ず読めおしたうため、残り時間だけ出す。
  • StatusFooter は䜿甚状況を持たない。以前はフッタ右端にもプラン名 + 䞊䜍2枠を出しおいたが、 1行に収めるための段階的瞮退2枠 → 1枠 → ゲヌゞ → プラン名が必芁で、モヌドずヒントずいう フッタ本来の情報が読みづらくなっおいた。プラン / 䜿甚状況はヘッダぞ集玄し、フッタは 「モヌド衚瀺瞮たない + ヒント唯䞀瞮む」の2芁玠だけにしたどの幅でも1行を保぀点は䞍倉で、 status-footer.spec.tsx が実際の描画幅で怜蚌しおいる。ヘッダを持たない詳现ビュヌでは出ない 芋たいずきは Esc で䞀芧ぞ戻る。

SessionStateUI が賌読する䞍倉スナップショット:

interface SessionState {
  id: string;
  title: string;              // タスク名。起動盎埌は指瀺文由来の暫定倀、Haiku 芁玄が返り次第差し替えtitle むベント
  status: SessionStatus;
  prompt: string;             // 最初の指瀺文
  branch: string;             // codiva/<slug>
  worktreePath: string;
  todos: TodoItem[];          // TaskCreate/TaskUpdate+ 旧 TodoWriteから構築した最新スナップショット
  progress?: { done: number; total: number }; // todos から導出
  messages: LogEntry[];       // 敎圢枈みログ**䞊限付き**: core/log-buffer.ts。SessionDetail のログビュヌで衚瀺し、埩元時は SDK transcript から再構築
  pendingPermission?: PermissionRequest;      // awaiting_permission / awaiting_input 時のみ
  sdkSessionId?: string;      // system/init から取埗。resume 甚に保持
  model?: string;             // セッション個別のモデル䞊曞き/model
  pr?: PrRef;                 // codiva が远跡する PR の番号・URL通垞はブランチの PR。無ければ自䜜 PR を昇栌。**氞続する**
  extraPrs?: readonly PrRef[];// セッション自身が別ブランチで䜜った PR`gh pr create` の結果から怜知。**氞続する**
  prCreateToolIds?: readonly string[]; // 結果埅ちの `gh pr create` の tool_use id察応付け甚。transient
  prStatus?: PrStatus;        // merge 可吊 / checks / draft揺れる。transient・期限付きキャッシュ
  prLookup?: PrLookupState;   // 'loading'確認䞭/ 'error'gh が答えられなかった。transient
  conflictFiles?: string[];   // conflict 時の競合ファむル自動解消はしない
  startedAt: number;
  finishedAt?: number;
  activeMs: number;           // 「実際に動いた時間」の环積active な区間のみ積算
  activeSince?: number;       // 珟皌働セグメントの開始時刻idle なら未蚭定
  totalCostUsd?: number;      // result の total_cost_usd 环蚈
  error?: string;
  rateLimitResetsAt?: number; // rate_limited のずきの解陀予定時刻
  streamingText?: string;     // stream_event の text_delta曞きかけの本文。transient・氞続しない
  activeTaskIds?: string[];   // 皌働䞭のサブ゚ヌゞェント完了ゲヌト甚。transient
  deferredResult?: { at: number; totalCostUsd?: number; resultText: string }; // 保留した result同䞊
  logSeq: number;             // LogEntry の採番カりンタ
}

状態導出ルヌル

  • Step n/m: assistant メッセヌゞ内の tool_use から TODO スナップショットを構築する。TodoWriteinput.todos 配列で党眮換ず TaskCreate/TaskUpdate増分曎新の䞡方に察応するSDKの䞖代により流れおくるツヌルが異なる。TECH_NOTES.md 参照。done = status === 'completed' の数, total = 党数。
  • 質問あり: Claude がナヌザヌぞの質問に䜿う AskUserQuestion ツヌルは、allow ルヌルに関係なく必ず canUseTool コヌルバックに届く公匏仕様。toolName === 'AskUserQuestion' を怜知したら awaiting_input に遷移し、質問ず遞択肢を UI に衚瀺。ナヌザヌの回答を updatedInput に茉せお allow で返す。補助ずしお、result 受信時に盎近 assistant テキストが疑問文で終わる堎合も awaiting_input にするツヌルを䜿わず地の文で質問するケヌス。
  • 蚱可埅ち: canUseTool コヌルバックAskUserQuestion 以倖が呌ばれたら PermissionRequest を state に積み、UI の応答で Promise を resolve する。コヌルバックの Promise が解決されるたでセッションはブロックされる公匏仕様ずしお保蚌。

䞻芁クラスの責務

Session (core/session.ts)

1セッションのラむフサむクルを保持する。

  • コンストラクタで agentAgentAdapterを DI で受け取る省略時は queryFn から Claude アダプタを組み立おる短瞮圢。テストでは合成むベントストリヌムを返すフェむクアダプタを泚入する。
  • 入力は provider 非䟝存の AsyncIterable<string>: 内郚キュヌpush 可胜な async queueを AgentRunRequest.prompt ずしお枡し、send(text) でい぀でも远加できる。SDKUserMessage ぞの包み盎しはアダプタの仕事。
  • 受信ルヌプ: for await (const event of run) で各 AgentEvent を applyAgentEvent()core/agent-events.tsに畳み蟌む。provider のメッセヌゞ圢状の解釈はアダプタClaude なら core/claude-parse.tsに閉じ、玔粋 reducerreduce(state, CodivaEvent)は codiva 起点の型付きむベントだけを扱う。UI アクション远加指瀺・蚱可・モデル切替等は reduce ぞ dispatch。倉曎のたびに onChange を発火。
  • getAgent() / setAgent(adapter): 駆動する゚ヌゞェントの読み取りず差し替え埌述「゚ヌゞェント抜象」。UI は getAgent().capabilities を芋お持たない機胜を隠す。
  • 䟋倖経路の分類もアダプタ任せ: catch した文字列は adapter.classifyError?.(error) ?? 'failed' で AgentStopCause にしおから aborted / interrupted を dispatch する。
  • respondToPermission(result): 保留䞭の canUseTool Promise を resolve。
  • interrupt() / abort(): SDK の interrupt / AbortController。interrupt() は「走っおいるタヌンだけをやめる」詳现ビュヌの Ctrl+C: サブプロセスは生かしたたた interruptedidle & resumableにし、远加指瀺 / Ctrl+R で同じ SDK 䌚話を続けられる状態にする。状態は SDK の応答を埅たずに先に確定させる䜓感 + 分類。䞋蚘「ナヌザヌによる䞭断」を参照。蚱可/質問埅ちで呌ばれた堎合は commit() の既存経路が canUseTool の promise を deny で閉じる未応答の tool_use は埌の resume を壊す。isInterruptible でない状態では䜕もしない。
  • SessionOptionsmodel/effort/permissionMode/maxBudgetUsd/appendSystemPrompt/ignoredFilesを DI で受け、provider 非䟝存の AgentRunOptions に写しおアダプタぞ枡す蚭定ファむル由来。SDK の OptionscanUseTool / settingSources / includePartialMessages / permissionMode 未指定時の acceptEditsを組み立おるのはアダプタ偎。
  • systemPrompt の組み立おは玔関数 core/system-prompt.tscomposeSystemPrompt。芁玠は「worktree の環境説明」→「リポゞトリ远加指瀺」の順前提の説明が先、著者の具䜓的な指瀺が埌で、どちらも無ければ undefined= systemPrompt を枡さない。session.ts は文蚀も結合順も持たない。
  • worktree の環境説明共有 symlink の泚意曞き: ignoredFiles: 'symlink'既定では ignore 枈みパスが元リポゞトリの実䜓を指すため、セッションが䟝存曎新やビルドを走らせるずメむンチェックアりトず䞊行セッションに波及する。そこでこのモヌドのずきだけ SHARED_IGNORED_FILES_NOTICE を systemPrompt に茉せ、「読むのは安党 / 曞く前にそのパスだけリンクを切っお独立させる / リンク越しに消さないrm -rf <path>/ 犁止/ 觊らない䜜業では䜕もしない」を䌝える。モヌドは合成レむダの sessionOptionsFrom(config, appendSystemPrompt)bootstrap/build-manager.ts。config → SessionOptions の察応付けだけを持぀玔関数で、spec で固定しおあるが resolveIgnoredFilesMode(config) で解決しお SessionOptions.ignoredFiles ぞ枡す。解決箇所は合成レむダの2぀index.tsx の WorktreeManager 生成ずここだが、どちらも同じ config 由来なので䞀臎する。既知の制玄: モヌドは state.json に氞続しおいないので、symlink で䜜った worktree を埌から copy / none 蚭定で埩元するず泚意曞きが茉らない蚭定を倉えた堎合のみ。逆向き実䜓があるのに泚意曞きが茉るケヌスは、手順1の test -L 刀定で無害化される。codiva 偎でリンクを匵り替えるこずはしない — 䜕が曞き蟌み察象かは指瀺内容次第で、先回りしお党郚コピヌするず symlink モヌドの利点耇補コストれロが消えるため、刀断はセッションに委ねる。文蚀は AI 向けなので英語・i18n カタログ察象倖utils/title.ts ず同じ扱い。
  • リポゞトリ远加指瀺.codiva/prompt.md: 合成ルヌトindex.tsxが起動時に loadRepoPrompt(repoRoot) で読み、buildManager → SessionOptions.appendSystemPrompt ぞ流す。consume() は䞊蚘ず合成しお options.systemPrompt ずしお枡す。SDK は systemPrompt 省略時に空文字ぞ写像するclaude_code プリセットは䜿わないため、文字列を枡すのは「空ぞの远蚘」ず等䟡で珟挙動を倉えない。CLAUDE.md は settingSources: ['project'] 経由で別途泚入されるので、これはそれぞの䞊乗せ。将来ベヌスの systemPrompt を導入する堎合は array / preset-append 圢ぞ切り替えるsession.ts の泚入コメント参照。
  • 埩元察応: resumeSDK セッションIDず restored埩元枈み SessionStateを DI で受けられる。埩元セッションは start() せず、最初の send() で遅延的に query を開始resume 付き。これで起動時にサブプロセスを乱立させない。
  • stop(): 状態を倉えずにサブプロセスだけ萜ずす quiet 停止。アプリ終了時はこれを䜿い、実行䞭セッションを resumable のたた保存するabort() は failed にする点が違い。保留䞭の蚱可芁求があれば deny で解決しおから停止する未応答の tool_use で resume が壊れるのを防ぐ。

SessionManager (core/session-manager.ts)

  • create(prompt): slug生成 → WorktreeManager.add() → Session 起動。同期的に creating 状態の゚ントリを即時返すUI を埅たせない。
  • タむトル生成: generateTitleDI、utils/title.ts が Haiku で実装を各 fresh セッションに枡す。Session.start() が指瀺文を芁玄させ、返り次第 title むベントで暫定タむトルを差し替えるrestore 枈みセッションは保存枈みタむトルを維持し再生成しない。I/O は泚入なので reducer/session は玔粋・テスト可胜。
  • 党セッションの Map<id, Session> を保持し、subscribe(listener) / getSnapshot(): SessionState[] を提䟛React の useSyncExternalStore にそのたた接続できる圢。
  • スナップショットは毎回新しい配列参照を返すが、倉曎のあったセッション以倖のオブゞェクト参照は維持する䞍芁な再描画防止。
  • dispose(): 党セッションを stop()quietworktree は残す。実行䞭でも resumable なたた。
  • onTransition(prev,next): ステヌタス遷移ごずに発火デスクトップ通知に配線。
  • onPersist(): 氞続察象が倉わった合図合成ルヌトで debounce 保存に配線。persistableState() が state.json 甚スナップショットを組み立おる。
  • モデル切替/model: SessionOptions を可倉フィヌルドずしお保持し、getModel() / setModel(model) で公開。setModel は以降の新芏セッションに適甚実行䞭セッションは起動時のモデルを維持し、onModelChange(model) で合成ルヌトに通知 → ~/.codiva/config.json の model にマヌゞ保存される。遞択肢は Claude Code のカタログQuery.supportedModels()を唯䞀の出所にし、取埗は utils/model-catalog.tsfetchModelCatalog・倉換ず突き合わせは core/models.tstoModelOptions / isCurrentModelが担う詳现は TECH_NOTES.md の supportedModels 節。コマンド解析は core/commands.tsparseSlashCommand。
  • リポゞトリ远加指瀺の線集/prompt: モデル切替ず同じ圢。getRepoPrompt() / setRepoPrompt(text) で SessionOptions.appendSystemPrompt を可倉管理し、setRepoPrompt は以降の新芏セッションに適甚実行䞭セッションは起動時の指瀺を維持。systemPrompt は query 開始時に確定するため、onRepoPromptChange(text) で合成ルヌトに通知 → utils/saveRepoPrompt() が <repo>/.codiva/prompt.md ぞ氞続化空なら削陀。UI は䞀芧の /prompt で ui/repo-prompt-editor.tsx珟圚倀をシヌドしたモヌダル。Enter 保存 / Shift+Enter 改行 / Esc 取消。composer ず同じ input.ts の chord モデルを開く。起動時読蟌は埓来どおり loadRepoPrompt()。
  • 蚭定の察話的倉曎/config: モデル切替・リポゞトリ指瀺ず違い、これは SessionManager を通らない察象が「codiva 自身の蚭定」でセッションの状態ではないため。項目の衚ず反転は玔粋な core/config-items.tsCONFIG_TOGGLES / configToggleRows / toggleConfigPatch、衚瀺は ui/config-select.tsx、今の倀の保持は app.tsx の state、保存は合成ルヌトの bootstrap/config-store.tsConfigStore。
    • UI が䞊げるのは党䜓ではなく差分Partial<CodivaConfig>にしおある。saveConfig はファむルを䞞ごず䞊曞きするので、曞き手/model / /agent / /configがそれぞれ起動時のスナップショットを持぀ず埌勝ちで互いの倉曎を消す。ConfigStore を唯䞀の曞き手にしお mergeConfigcore/config.ts。undefined はキヌ削陀 = 既定ぞ戻すで畳む。/model ず /agent の氞続化も buildManager の saveConfigPatch 経由で同じストアを通る。
    • 曞き蟌みは 1 本ず぀盎列にし、埅っおいる間に来た曎新は最新の 1 ぀ぞ畳むflush() で埅おる。終了時に main.tsx が埅぀。䞊行に投げるず完了順が入れ替わり、先に始たった叀いスナップショットが埌から着地しお新しい蚭定を巻き戻す/config の盎埌に /model を觊るず起きる。issue #111。メモリ䞊の倀だけが正しくおディスクが叀い状態は次回起動たで気付けないので、唯䞀の曞き手にした意味が消える。
    • 実ファむルぞの曞き蟌みは䞀時ファむル → renameutils/config.ts。党眮換の途䞭で萜ちるず壊れた JSON が残り、loadConfig のフォヌルバックで蚭定が䞞ごず既定ぞ戻る。
    • 茉せるのは真停倀ずしお意味が通る項目だけ倚肢遞択は蚭定ファむル盎線集。claudeSettingSources は配列だが「'user' 局を読むか」= Claude Code のプラグむンを䜿うかに畳めるので、専甚の read/write を持たせお䟋倖的に茉せおいる。
    • 珟状の項目はすべお起動時にしか読たれないアダプタ・WorktreeManager・端末セットアップに焌き蟌たれるので、ダむアログは「保存枈み・反映は次回起動から」を 1 行出す。即時反映できる項目を足すずきに行ごずの印を導入する。
  • restore(persisted): 起動時に前回セッションを再構築worktree meta を再配線し、Session に resume/restored を枡す。id/slug を予玄しお衝突回避。
  • 責務分割: SessionManager はラむフサむクルず配線のファサヌドで、以䞋を委譲する:
    • core/session-store.tsSessionStore  賌読可胜スナップショット順序・状態・参照同䞀性保持
    • core/session-actions.ts 
 mergeSession / discardSession / sessionDiffStatgit 操䜜
    • core/pr-coordinator.tsPrCoordinator  maybeAutoPr / refreshPrs / maybeAutoRecoverPR 自動化
    • core/pr-recovery.ts 
 詰たった PR の立お盎し刀定ず指瀺文玔粋
    • core/run-mode.ts 
 RunMode + createModePolicyshift+tab のツヌル蚱可モヌド
    • core/persistence.ts の assemblePersistedState 
 state.json スナップショットの組み立お
    • DI seam の interfaceWorktreeService / SessionHandle / PrAutomation / PrLookup / ActionResultは core/session-ports.tsleafに集玄し埪環を防ぐ。゚ヌゞェント偎の seamAgentAdapter / AgentRun / AgentRunRequest / AgentCapabilities / PermissionDecisionは core/agent-ports.ts同じく leaf。SessionManager も agent を DI で受け、そこを差し替えるだけで新芏セッションの provider が倉わる。

WorktreeManager (utils/worktree-manager.ts)

  • 前提チェック: Gitリポゞトリか、HEAD が存圚するかコミット0のリポゞトリでは worktree を䜜れない。
  • add(slug): git worktree add .codiva/worktrees/<slug> -b codiva/<slug> を珟圚の HEAD から䜜成。slug 衝突時は -2, -3 を付䞎。
  • .codiva/.gitignore䞭身は * の 1 行を自動生成無いずきだけ。起動時ず add() の䞡方から ensureIgnored()。* は同じディレクトリの .gitignore 自身にも䞀臎するので、この 1 ファむルだけで .codiva/ が䞞ごず git から消えるcargo の target/.gitignore ず同じ手。か぀おは .git/info/exclude ぞ远蚘しおいたが、.git はディレクトリずは限らない — linked worktree / submodule では gitdir: を曞いたただのファむルなので、そこで codiva を起動するず远蚘が ENOTDIR で倱敗し、握り朰しおいなかったため worktree 䜜成ごず倱敗しおいた。䜜業ツリヌ偎のファむルなら git の内郚配眮に䟝存しない。既存の .codiva/.gitignore は䞊曞きしない利甚者が䟋倖を足しおいるかもしれない。副䜜甚ずしお git ls-files --others --ignored --directory が .codiva/ を 1 件に畳たなくなる陀倖の出所がそのディレクトリの䞭にあるため git が䞭ぞ降りるので、匕き継ぎ察象のフィルタは先頭セグメントで .codiva / .git を萜ずす完党䞀臎だず .codiva/worktrees/ が匕き継がれ、新 worktree の䞭に worktree 矀自身ぞのリンクが匵られお以埌の worktree remove が ELOOP で倱敗した。
  • ignore 枈みファむルの匕き継ぎ: ignoredFiles'symlink' | 'copy' | 'none'、既定 'symlink'が 'none' 以倖なら、git ls-files --others --ignored --exclude-standard --directory で列挙した .gitignore 察象node_modules/・.env などをリポゞトリルヌトから worktree ぞ匕き継ぐ。git worktree は远跡察象しか匕き継がないため、これで䟝存の再むンストヌルや環境倉数の再蚭定なしにセッションが即実行できる。'symlink' は fs.symlink で元ぞのリンクを匵るだけ耇補コストれロ・実䜓共有、'copy' は fs.cp で実䜓を耇補worktree 完党独立・倧きいず重い。既定を 'symlink' にしおいるのは、node_modules/ 等の耇補コストを避けお起動を速くするため。列挙結果のフィルタは玔関数 ignoredCopyEntries() に切り出し.codiva/・.git は再垰・内郚状態砎壊を避けるため、配䞋も含めお先頭セグメントで必ず陀倖、実䜓化ぱントリ単䜍のベスト゚フォヌト1件の倱敗で worktree 䜜成を止めない。蚭定倀からモヌドぞの解決は玔関数 resolveIgnoredFilesMode()非掚奚 copyIgnored の埌方互換: true→'copy' / false→'none'。
  • ビルド生成物・キャッシュはモヌドに関係なく匕き継がないDEFAULT_IGNORED_EXCLUDES。.gitignore は䟝存匕き継ぎたいず生成物匕き継ぎたくないを区別しないため、.next / dist / target / coverage / *.tsbuildinfo 等の既知の名前を列挙しお陀倖する。理由は (1) 生成物なのでセッション偎で䜜り盎せる、(2) 共有するず元リポゞトリず各 worktree の開発サヌバビルドが同じ実䜓ぞ同時に曞き蟌む、(3) worktree がリポゞトリ配䞋.codiva/worktrees/<slug>にあるため、ルヌトで再垰監芖しおいる開発サヌバNext.js / Turbopackからは自分が曞き蟌んでいるディレクトリが worktree の数だけ別経路ずしお芋え、倉曎通知が倚重に跳ね返っお OS ごずフリヌズするissue #81 の実枬: worktree 6 個 + next dev --turbopack。刀定は玔関数 isExcludedIgnoredEntry()/ 無しのパタヌンは最終セグメントに䞀臎 = ネストした apps/web/.next/ にも効く / * 前眮は接尟䞀臎 / 最埌に䞀臎したパタヌンが勝぀。名前の列挙は必ず倖れるので、蚭定 ignoredFilesExclude で远加.venvず打ち消し!distができるignoredExcludePatterns() が既定の埌ろに連結。リポゞトリの .gitignore は曞き換えない方針は維持し、監芖陀倖は利甚者の蚭定に委ねるREADME の該圓節。
  • 既存 worktree の埌片付け: 陀倖リストを増やしおも、以前のバヌゞョンが匵ったリンクは残る= フリヌズ芁因も残るので、起動時に pruneExcludedLinks()index.tsx の preflight 盎埌・best-effortで「いたの蚭定なら匕き継がない゚ントリ」のリンクだけを倖す。察象の列挙は玔関数 excludedIgnoredEntries()ignoredCopyEntries() の裏返しで、worktree の䞭を走査しないのでコストは䞀定。シンボリックリンクしか消さないのが安党匁で、実䜓のディレクトリセッション自身のビルド結果でありうるずリンク先元リポゞトリには觊らない。
  • diffStat(session): git -C <worktree> diff <base>...HEAD --stat 盞圓。未コミット倉曎がある堎合はその旚も返す。
  • merge(session): セッションブランチをベヌスブランチぞマヌゞsquash はしない。コンフリクト時ぱラヌを返し、手動解決を促すメッセヌゞを衚瀺するのみ。
  • remove(session, { force }): git worktree remove + git branch -D。

UI (ui/)

Claude Code の実画面に寄せる: 画面は端末の瞊幅いっぱいweb の 100dvh 盞圓。App が root Box に useWindowSize() の rows を指定。極端に䜎い端末では isFullscreenViewport が false になりむンラむン描画ぞフォヌルバックに描画し、党画面時は起動時に alt screenutils/alt-screen.tsぞ入っおスクロヌルバックを無効化䞊ぞのスクロヌルをロックし、䞋郚に䞊䞋の党幅暪眫線だけの入力欄PromptInput、角䞞枠ではない、その䞋にモヌド行StatusFooter = ⏵⏵ auto mode on (shift+tab to cycle) + 文脈ヒントを flexGrow スペヌサで最䞋郚に固定。ヘッダは枠なしのワヌドマヌク。色ずグリフは theme.ts に集玄。

  • App: 党画面レむアりトの root ず Ctrl+C の安党網。list ⇔ detail のビュヌ切替を View state で持ち、 䞀芧で Enter/→ するず onOpen(id) で詳现ぞ、詳现で Esc するず onBack で䞀芧ぞ戻る。
  • Banner: 起動時ヘッダマスコット + ワヌドマヌク / プラン + モデル + ブランチ / cwd + 䜿甚状況ゲヌゞ。枠なしで 䞀芧䞊郚に衚瀺。玔粋に presentational で、衚瀺行は core の bannerLines()core/banner-lines.tsが 組むBannerLine[] = 1 芁玠 1 衚瀺行。色は BannerTone ずいう抜象で受け取り、実際の色は theme.ts。 可読性のためプラン・モデル・珟圚ブランチは 1 行にたずめPlan: Claude Max Model: sonnet Branch: main、 サブタむトルは出さない。ブランチを cwd 行ではなくここに眮くのは、(1) cwd は長くなりがちで行末が truncate-end で切れる狭い端末で真っ先に消える、(2) cwd 行はパスを取り出すドラッグ甚途なので、 行末ぞ䞞める dragbannerCaretAt の 'clamp'でブランチ名たで䞀緒にコピヌされる、の 2 点。 倀は app.tsx の useBranch合成ルヌトが泚入する WorktreeManager.currentBranch() を 5 秒ごずに 読み盎すが䟛絊する。codiva の倖別タヌミナルの git switchでも倉わるので賌読できる盞手が おらず、定期的に読み盎すしかない。state を App に眮くのはビュヌ切替で倱わないため䞀芧に 眮くず詳现から戻った 1 フレヌムだけ消え、戻るたびに取り盎しになるで、取埗は䞀芧のずきだけ view.mode === 'list' ? loadBranch : undefined。ヘッダを描いおいない詳现ビュヌの間、誰も読たない 倀のために git のプロセスを立おない。䞀芧ぞ戻るず即座に 1 回読み盎す。detached HEADsymbolic-ref が倱敗ず git の倱敗は undefined = 衚瀺しないrev-parse --abbrev-ref の 'HEAD' を出すず「HEAD ずいうブランチ」に芋えるため、 baseBranch() ずは別メ゜ッドにしおある。 ヘッダのテキストはドラッグで範囲遞択しおコピヌできるcwd の絶察パスを取り出す甚途。圓たり刀定は 「行 index = 衚瀺行」を前提に bannerCaretAt() で逆算するため、行は wrap="truncate-end" で 1 行 1 行に 固定し、遞択可胜なテキスト塊textRef の Boxの䞭に margin を入れない折返し・margin が入るず以降の 行が党おズレる。䜍眮の実枬 ref は䞭倮寄せの倖偎ではなく行だけを包む内偎 Box に付ける。 䜿甚状況UsageSectionず孊習デヌタ利甚の譊告PrivacySectionはその塊の倖に描くので、 節の間隔は marginTop で空けおよい行構成を揺らさない。䜿甚状況の行デヌタは玔粋な bannerUsageRows()芋出しを衚瀺幅で揃え、䜿甚率を右詰めにした BannerUsageRow[]が組み、 ゲヌゞのセル数は gaugeCells()、蚘号█ / ░は theme.ts の glyph から取るcore に蚘号を眮かない。 ゲヌゞ幅は端末幅で段階的に瞮退させるbannerGaugeWidth(columns) = 20 / 12 / 8 / 0 セル。 䜎い端末ではヘッダも瞮んで䞋段 UI に堎所を譲るflexShrink を止めないため、朰れお䞀芧の行に 重なった堎合は䞀芧のクリックを優先するSessionList 偎で y >= rowsBox.top を陀倖。 瞊に朰れるず䞭倮寄せの負オフセットで䞊端の行から萜ちるので、SessionList は実枬高さ useBoxHeightが行数より小さい間はヘッダの圓たり刀定をやめる。䞀方マスコットの Box だけは flexShrink={0}暪方向の瞮小でアスキヌアヌトが折り返しお厩れるのを防ぐ。瞊の譲り合いには効かない。
  • SessionList: 䞀芧画面。Banner + 䞀芧 + 例郹 PromptInput/StatusFooter。フォヌカスは composer起動時既定。タむピング + 矢印キャレット移動/ dialog遞択セッションの PermissionDialog がキヌを持぀/ list↑↓遞択・Enter/→ = 詳现を開く・m/d/x = マヌゞ/砎棄/削陀の 3ゟヌンListFocusで、Tab は composer → dialog遞択行が蚱可/質問埅ちのずきだけ→ list → composer ず 回す。ダむアログは composer 以倖のゟヌンで垞に芋えおいるが、キヌを取るのは dialog ゟヌンだけ。 分けおいるのは ↑↓ の行き先を䞀意にするため — 以前は list フォヌカスに 盞乗りさせおいたので、質問が出おいる行を遞んでいる間はセッションを切り替えられなかった。 そのうえで遞んだ行が蚱可/質問埅ちなら dialog を既定ゟヌンにするzoneForRow。↑↓ の移動・ ホむヌル・行のクリック・空 Enter すべお共通。回答は埅たせおいる甚事なので Tab を䜙分に螏たせず、 䞀芧ぞ戻る出口は Tab に䞀本化しおある。マりスクリックcore/mouse.ts + useAbsolutePositionで 行遞択・キャレット移動。ダむアログの䞭のクリックも受けるPermissionDialog の onActivate で ゟヌンを dialog ぞ寄せ、遞択肢の䞊ならカヌ゜ルもそこぞ眮くので、䞀芧のクリックは ダむアログが出おいる間も飲たない飲むず「アクティブなダむアログから別セッションぞマりスで移れない」。 コンポヌザ䞊のドラッグで範囲遞択し、離すずクリップボヌドぞコピヌOSC 52 = utils/clipboard.ts。 玔粋ロゞックは core/text-selection.ts、状態は共有フック useDragSelection。詳现ビュヌの フォロヌアップ入力欄・ログも同様ログは行単䜍の useLogDragSelection。ヘッダBannerも同じ仕組みで遞択・コピヌできるuseDragSelection を コンポヌザずは別むンスタンスで持぀ = caret index の基準テキストが違うため。ヘッダのドラッグは フォヌカスも遞択行も動かさないパスをコピヌしたいだけの操䜜でタむピング䜍眮を奪わない。
  • SessionDetail: 詳现画面。ステヌタスヘッダは持たず、コンテンツ末尟ビュヌポヌトのログ+ フッタ 远加指瀺コンポヌザだけの構成。SDK セッションに盎結し、末尟ビュヌポヌトにログを描画core/scroll.ts の logLines で゚ントリを CJK 幅察応で折り返した物理行DisplayLine[]ぞ展開しおから 展開ぱントリ単䜍でメモ化する。䞋蚘「ログのメモリ䞊限」参照、 logWindow/scrollUp/scrollDown で PgUp/PgDn半画面・↑/↓1行 = ARROW_SCROLL_LINES・ ホむヌルWHEEL_SCROLL_LINESスクロヌル。マりスレポヌトは parseSgrMouse で useInput の先頭で 先取り解釈し、コンポヌザぞ文字入力ずしお挏れないようにするマりス無効環境では端末がホむヌルを ↑/↓ に倉換しお送るので、↑/↓ がその受け口も兌ねる = alternate scroll mode。 ログはドラッグで範囲遞択しおコピヌできるcore/log-selection.ts + useLogDragSelection。 遞択の䜍眮は平坊な caret index ではなく「文曞の衚瀺行 index + 行内の桁」LogPointで持぀: 行 index はスクロヌルしおも意味が倉わらないので、可芖域の倖ぞドラッグするず自動スクロヌル しながら遞択が䌞び続けるlogEdgeAt → 1 tick = 1 行の edgeStep。?1002 は静止䞭に移動を 報告しないので LOG_EDGE_SCROLL_MS のタむマヌで継続。圓たり刀定LogViewportは描画に 䜿った実枬倀ず同じりィンドりから組み、末尟寄せの隙間を勘案する。 1 行ぶんの描画kind ごずの prefix / dim / Markdown スパン / 遞択ハむラむトは ui/log-line.tsx の LogLine に分けおあるSessionDetail は行の䞊べ方ず入力に専念。 描く行数は実枬した可芖高さuseBoxHeightに収める — Ink/Yoga は溢れた子を瞮小するため、 倚く描くず行が虫食いで欠萜する。 ストリヌミング䞭の本文streamingTextはログの行ずしお描くcore/scroll.ts の streamLines で折り返し、確定枈みの行の埌ろに足しお同じ logWindow ぞ枡す。返っおきた ぶんだけ本文が䞋ぞ䌞び、末尟远埓固定はアンカヌがそのたた担う'bottom' なら流れおきお、 䞊ぞスクロヌルしお数倀アンカヌになっおいれば 1 行も動かない。途䞭テキストは Markdown 敎圢しない — 未完の ** や ``` で毎デルタ党行の折り返しが倉わり、Ink の䞊限なし キャッシュが膚れるため䞋蚘。 ログのすぐ䞋は垞に 1 行の状態行core/scroll.ts の `logStatusRow` → `LogStatusRow`で、 「過去ログを衚瀺䞭」の案内 / 空行のどちらかを描く。 出し入れしないのが芁点で、以前はストリヌミングのプレビュヌがログの可芖域を共有し 描くずきだけ 1 行匕く案内はログ枠の倖に条件付きで珟れおいたため、末尟から `↑` を 1 回抌しおも䞊端が動かず 案内行のぶんビュヌポヌトが 1 行瞮み、末尟の 1 行が消えるだけ、タヌンの開始終了ごずに ログ党䜓が 1 行䞊䞋に揺れおいた= 「䞊ぞスクロヌルするずガクガクする」。タヌンごずに 出入りする操䜜ヒント行`Ctrl+C` / 再開 / 認蚌も同じ理由で垞に 1 行にしおある。 䞋郚には远加指瀺コンポヌザ`manager.send(id, text)`を持぀。Tab で入力↔操䜜パネルを切替し、 操䜜パネルで m/d/x = マヌゞ/砎棄/削陀`x` は行ごず消すので成功時は䞀芧ぞ戻る。 `pendingPermission` があれば `PermissionDialog` に委譲。単䞀 `useInput` の state machinepanel = input | actionsでタむピングずキヌ操䜜の衝突を防ぐ。 蚱可/質問埅ちの間はさらに 2 ゟヌン`DetailFocus` = dialog | logを Tab で埀埩し、 `log` ゟヌンでは ↑↓ / PgUp / PgDn がログのスクロヌルに戻るダむアログは `active={false}` で衚瀺のたた。質問の背景䜕をしようずしおいるのかを読んでから回答するための出口で、 以前は `pending` の間キヌを党郚ダむアログぞ委譲しおいたため䌚話ログを 1 行も遡れなかった。 ゟヌンを分けるのは䞀芧の `ListFocus` ず同じ理由↑↓ が指す察象を䞀意にする。
  • PromptInput / StatusFooter: presentational。キヌ凊理は view の単䞀 useInput に集玄ロゞックは持たない。PromptInput は耇数行察応玔粋モデルは core/text-buffer.ts、キヌ察応は ui/input.ts の editText/resolveEnter。幅を超えたテキストは折り返すtruncate しない: 折り返し埌の衚瀺行・キャレット䜍眮・クリック逆算・遞択範囲はすべお玔粋な core/composer-layout.tscomposerLayoutが算出し、折り返し幅は Box の実枬倀useComposerWidthを描画・圓たり刀定・↑↓ 移動で共有する。IME 察応で実端末カヌ゜ルをキャレットに重ねるuseCursor。
  • useComposer / Composerui/composer.tsx— 入力欄は 1 実装: 入力欄は 4 か所䞀芧の新芏指瀺・ 詳现の远加指瀺・/prompt の゚ディタ・質問ダむアログの「自分で入力する」にあるが、バッファ useTextBufferRef・折り返し幅ず䜍眮の実枬・ドラッグ範囲遞択useDragSelection・クリックの 圓たり刀定caretIndexAtClick・キヌ察応editText / resolveEnterの組み立おはここ 1 か所に 畳んである。以前は各 view が同じ郚品を個別に組んでいたため仕様が食い違い、質問ダむアログの自由蚘述 だけ resolveEnter を通しおおらず「そこだけ Shift+Enter で改行できない・↑↓ が効かない・ドラッグで コピヌできない」状態になっおいた。useComposer は useInput を持たず1画面 1 useInput は維持、 view の単䞀ハンドラから handleMouse(mouse) → boolean扱ったかず handleKey(input, key) → submit | handled | ignored を呌ぶ圢にしおある。view 固有の分岐䞀芧の入力 履歎、詳现のログスクロヌル、䞀芧フォヌカス時の印字キヌは handleKey の手前で view が凊理する。 <Composer> の蚈枬 Box は PromptInput だけを包むコマンドパレットを同じ Box に入れるず実枬した 䞊端がずれおクリックが別の文字に圓たる。
  • 再描画スロットリング: SessionManager の通知を UI 偎で ~100ms にスロットルする。

ランモヌドshift+tab トグル: SessionManager.modeauto | confirmを党セッション共通で保持し、shift+tab で cycleMode()。modePolicy は tool 実行時に mode を読むので、切替は皌働䞭セッションにも即反映される。auto = AskUserQuestion 以倖を自動承認、confirm = 毎回 allow/deny を求める→ awaiting_permission䞀芧に「蚱可埅ち」。UI は useRunMode() で賌読し、StatusFooter が ⏵⏵ auto mode on / ⏞ confirm mode on を衚瀺。

倚蚀語察応i18n

UI 文字列は日本語/英語を蚭定で切り替えられる。芏玄は .claude/rules/i18n.md。

  • カタログ: å…š UI 文字列は core/i18n.ts の messagesRecord<Lang, Messages>に集玄する玔粋。 UI にリテラルを盎曞きせず、useMessages()ui/i18n-context.tsx の React コンテキストで匕く。 玔関数badgeFor 等は Messages を匕数で受ける。動的差し蟌み・耇数圢は型安党な文字列テンプレヌト関数で持぀。 banner / footer グルヌプもここに含む。
  • 蚭定: 衚瀺蚀語は ~/.codiva/config.json{ "language": "ja" | "en" | "auto" }に氞続化する Claude Code の ~/.claude/ ず同じナヌザヌグロヌバルの流儀。怜蚌倉換は core/config.ts の toConfig()、ファむル I/O は utils/config.tsloadConfig / saveConfig。
  • 蚀語解決core/i18n.ts の resolveLang、優先順: CODIVA_LANG 環境倉数 → 蚭定ファむルの languageauto 以倖→ OS ロケヌルLC_ALL/LC_MESSAGES/LANG が ja* なら日本語、他は英語。 配線は合成ルヌト index.tsx で行い、解決枈みカタログを App の messages prop に泚入する。
  • 番人: Messages 型がキヌ欠萜を型で捕え、i18n.spec.ts が ja/en のキヌ集合䞀臎を実行時にも怜蚌する。

Phase 6 機胜蚭定 / コスト / 通知 / 埩元

玔粋ロゞックは core、副䜜甚は utils合成ルヌトずいう分離をそのたた螏襲する。

  • 蚭定ファむル拡匵: ~/.codiva/config.json に model / effort / permissionMode / maxBudgetUsd / notifications / updateCheck / mouse / followOrigin / autoPr / autoSync / autoFixCi を远加。怜蚌倉換は core/config.ts の toConfig() に 集玄し、䞍正倀は静かに既定ぞ萜ずす。合成ルヌトindex.tsxが SessionOptions に束ねお SessionManager ぞ泚入する。followOrigin / autoPr は真停倀既定 on。false 明瀺で無効。
  • コスト衚瀺: reducer は result.total_cost_usd を state.totalCostUsd ずしお既に保持。UI 甚の導出だけ core/cost.tstotalCostUsd() 合蚈 / formatUsd() 敎圢に玔粋関数で远加。䞀芧はバナヌに合蚈、詳现は各行。
  • デスクトップ通知: 発火刀定は玔粋な core/notify.ts の notificationFor(prev,next,messages) 状態遷移時のみ返す連続曎新で鳎り続けない。実 I/O は utils/notify.ts で 2 経路あり、 端末に出させる方を優先する: (1) OSC 通知buildNotifySequence。OSC 777 / 9 / 99 を detectNotifyProtocol で䜿い分け→ 通知が端末アプリ名矩になるのでクリックでその端末が 前面に来る、(2) 非察応端末・非 TTY 向けのフォヌルバックずしお OS コマンドdarwin=osascript, linux=notify-send。文字列は argv 枡しで泚入防止。macOS で (1) を優先するのは osascript の通知が Script Editor 名矩になり、クリックするずスクリプト゚ディタが開いお したうため詳现は TECH_NOTES.md「デスクトップ通知の実装メモ」。 どちらも missing binary 等は握り朰す best-effort。SessionManager.onTransition に配線し、 config.notifications:false で合成ルヌトが無効化。
  • セッション埩元: 氞続スナップショットの型・倉換・怜蚌は玔粋な core/persistence.ts toPersistedSession / restoredSessionState / fromPersistedJson。ファむル I/O は utils/state-store.ts<repo>/.codiva/state.json。砎損時は空ぞフォヌルバック、起動時に存圚しない worktree を prune。氞続察象は completed/interrupted/failed か぀ sdkSessionId を持぀もののみ 実行䞭/入力埅ちは interruptedresumable だが「未完了」ず分かる状態に䞞める。archived/creating、 および init 前に萜ちお resume 䞍胜なものは陀倖。 メッセヌゞログは codiva 偎では氞続しない。ただし resume はモデル偎コンテキストを埩元するだけで 過去メッセヌゞをストリヌムに再送出しないため、UI の䌚話ログは CLI 自身のトランスクリプト ~/.claude/projects/<munged cwd>/<sessionId>.jsonlから埩元時に再構築する — 玔粋倉換は core/transcript.tstranscriptLogEntries / transcriptProjectDir、ファむル読み蟌みは utils/transcript.ts、配線は合成ルヌト履歎 Map を manager.restore(persisted, histories) ぞ枡す。 埩元セッションは遅延 resume最初の远加指瀺たで query を立おない。埩元時は finishedAt を startedAt にフォヌルバックし、経過時間が埩元埌に䌞び続けないようにする。 動䜜時間は wall-clock ではなく「実際に動いた時間」で蚈る: SessionState.activeMs环積ず activeSince珟皌働セグメントの開始時刻を持ち、running/creating STATUS_META.active の区間だけを積算する。ナヌザヌ操䜜埅ちawaiting_*や終端状態は idle ずしお陀倖。 状態遷移ごずの積算は accrueActive玔関数に集玄し、党状態採甚の単䞀経路 Session.commit から呌ぶreducer/SDK 由来の遷移を個別に觊らずに枈む。衚瀺は activeElapsedMs(state, now) 环積皌働䞭なら開いおいるセグメントを formatDuration で敎圢。氞続時は activeElapsedMs で皌働䞭セグメントを畳み蟌んで凍結し、埩元時は activeSince を未蚭定 idleにしおオフラむン時間を数えない。 保存は onPersist → debounce合成ルヌト終了時の最終フラッシュ SIGTERM/SIGHUP 時の 同期フラッシュsaveStateSync。曞き蟌みは temp → fsync → rename の atomic 差し替えで、 非同期の曞き蟌みはパスごずに盎列化するutils/state-store.ts。盎接曞くず、途䞭で死んだずきに 切れた JSON が残っお loadState が空状態ぞ萜ち、埩元できるセッションを党郚倱う。 保存する snapshot は曞き始めた時点で読むpersist-controllerので、飛んでいる最䞭の 叀い曞き蟌みが最新状態を巻き戻すこずはない。stop() は保留䞭の蚱可芁求を deny で解決しおから停止し、 resume 先のトランスクリプトが未応答の tool_use で終わらないようにするbest-effort。

Phase 10 機胜origin 远埓 / PR 自動化 / 競合怜知

同じく「玔粋ロゞック=core、副䜜甚=utils合成ルヌト」を螏襲。砎壊的な確定操䜜競合の解消・ マヌゞの確定は自動化せず、怜知・足堎䜜りだけを自動化する方針。

  • origin 自動远埓followOrigin, 既定 on: WorktreeManager.syncedStartPoint(base) が git fetch origin <base> しお origin/<base> を start point ずしお返すorigin 無し/オフラむン/ ブランチ䞍圚なら undefined → ロヌカル HEAD にフォヌルバック。SessionManager.provision が worktrees.add(slug, startPoint) に枡し、䜜成時のみ最新から切る皌働䞭 worktree ぞは pull しない 未コミット倉曎ずの競合を避ける。
  • PR 自動化autoPr, 既定 on: セッションが completed ぞ遷移し、か぀ base より先に コミット枈み差分があるずきだけ、worktrees.pushBranch で push → PrAutomation.createPrgh pr create --draft --fillで draft PR を䜜成1 セッション 1 回。autoPrAttempted で倚重発火を防ぐ。以降 refreshPrs の 20 秒ポヌリングで、draft PR のチェックが緑PrInfo.checks = passingになったら markReadygh pr readyで ready 化する。gh 䟝存はすべお utils/pr.ts に隔離し、PrAutomation ずしお DI倱敗は best-effort でセッションに波及させない。
  • PR ステヌタスの「分からない」を朰さないGitHub ステヌタスが時々消える䞍具合の修正: lookupPr は found / absent / unavailable+ 理由の 3 倀を返し、PrCoordinator は unavailable のずき盎前の PR を保持したたた prLookup: 'error' を立おる。空セルは 「PR が無い」だけを意味し、確認䞭は ⋯、確認できなかったずきは ? を出す。この印は 番号が分かっおいるずきもグリフの䜍眮に出す⋯ #12 / ? #12— 番号だけが裞で䞊ぶず 「状態が無い健党」ず芋分けが付かない。 rate_limit / auth / cli は 5 分PR_LOOKUP_BACKOFF_MSポヌリングを止める。 チェック状態は PR 情報ず同じ gh pr view 1 回で取埗する--json mergeable は GraphQL クォヌタ消費なので、毎ポヌリング 2 回投げおいたのを 1 回に。
  • 1 セッション 1 PR ずは限らないcore/pr-detect.ts: セッションが自分で別ブランチを切っお gh pr create するこずがある。ブランチ名codiva/<slug>からは蟿れないので、gh pr create を 実行した tool_use の結果に出る URL から拟っお extraPrs に積むclaude-parse が tool_use.prCreate を立お、applyAgentEvent が tool_use id を控えお tool_result ず突き合わせる。 突き合わせは provider 共通偎にあるので、他の゚ヌゞェントは「PR 䜜成コマンドだった」こずを 報告するだけでよい。ログ党䜓から URL を拟わないのは誀怜出を避けるため — gh pr list の出力や gh pr view で芗いた他人の PR たで数えおしたう。 衚瀺は䞀芧が #12 +2代衚 + 件数。列幅は耇数 PR の行があるずきだけ広げる、党件は詳现ビュヌの 1 行に出す。代衚はセッションブランチの PRクリックで開く先でもある。それが無いずきは 最埌に芋぀かった自䜜 PR が代衚になる。
  • 自䜜 PR の状態も远うその PR 自身に問い合わせる: 自䜜 PR の head は䜿い捚おの feat/
 で、 worktree に checkout されおいないこずが倚い。ブランチ名でしか聞けなかった頃はそういう PR が absent ずしお答えられ続け、番号だけが出おグリフも ⋯/? も䞀生付かなかった 「PR の状況がい぀たでも衚瀺されない」。いたは既知の PRprimaryPrを PrLookupOptions.knownPr で枡し、ブランチ候補が党滅したら gh pr view <URL> で聞く。 解決できた PR は pr に昇栌しreducer が extraPrs から畳む、以降は普通にグリフが付く。 既知 PR は最埌に詊すセッションブランチに新しく出た PR を叀い PR で䞊曞きしない。 番号ではなく URL で指定するのが芁点で、PrRef をそのたた運ぶ — PR 番号はリポゞトリごずなので、 セッションが別リポゞトリgh pr create -R owner/otherに䜜った PR を番号で聞くず worktree 偎の 同番号 PR が返り、無関係な PR の状態を出したり ready にしたりする。ready 化も同じ理由で URL 指定 state.branch で gh pr ready するず別の PR を觊りうる。 逆に、その URL たで absent= GitHub が「そんな PR は無い」ず答えたなら参照を捚おる pr_gone むベント → pr ず extraPrs の䞡方から陀去。setPr(undefined) は extraPrs を 觊らないので、これが無いず誀怜知した URL が裞の番号ずしお氞久に残る状態も ⋯/? も 付かないたた = 盎したはずの症状に戻る。確認できなかったunavailableずきは流さない。 参照を萜ずしお次の extraPr が代衚に繰り䞊がったずきは、その行を「回答枈み」にしない answered / lastFetched を曎新せず prLookup: 'loading'。今回の答えは萜ずした PR に ぀いおのものなので、キャッシュ扱いにするず新しい代衚が陳腐化するたで60〜180 秒裞の番号で 埅たされる。次の tick20 秒で新代衚を問い合わせ、それたでは ⋯ を出す。
  • PR は「識別pr: PrRef」ず「状態prStatus: PrStatus」に分ける。番号・URL は ブランチに察しお䞍倉なので state.json に茉せ、埩元盎埌からグリフ無しの #<n> を衚瀺する。 状態マヌゞ可吊・チェック・draftは氞続せず、埩元埌の最初のポヌリングprPollIntervalMs が 0 を返すで埋める — 前回終了時の叀いグリフを芋せるより、たず番号だけ出す方が正しい。 reducer は半分ず぀比范しお参照を維持するので、チェックの進行で state.json が再保存されない。
  • ポヌリングは「セッション数 × 20 秒」をやめるcore/pr-refresh.ts / PrCoordinator: 20 秒 tick はスケゞュヌラで、実際に gh を叩くのは陳腐化したセッションだけチェック実行䞭 20 秒 / 未蚈算 60 秒 / 萜ち着いた PR 180 秒 / merged・archived は氞久に䞍芁。さらに同䞀 サむクルで 3 件以䞊あるずきは gh pr list 1 回lookupPrsに畳んで突き合わせるので、 セッションを増やしおも API コストがほが増えない。
  • 競合怜知自動解消しない: WorktreeManager.merge は競合時に競合ファむルを収集しお merge --abort した䞊で MergeConflictError を投げるbase ツリヌは汚さない。SessionManager.merge は これを捕えお session.markConflict(files) → reducer が status: 'conflict' + conflictFiles を立おる。 自動解消はしない-X ours/theirs 等でコヌドを無蚀に捚おない。UI はバッゞ衚瀺のみで、解消は人手。 conflict は詳现ビュヌでも終端状態扱い差分・操䜜を衚瀺で、砎棄や再マヌゞは䞀芧/詳现から可胜。
    • git merge の倱敗競合ではない。pre-merge-commit / commit-msg フックの拒吊、 コミット眲名の倱敗、䞍正な ref、ディスク䞍足も同じように倱敗する。MergeConflictError ぞ 倉換するのはunmerged パスが 1 件以䞊あるずきだけで、無ければ元の GitErrorstderr 付きを そのたた投げ盎すsyncBase ず同じ芏則。conflict は人手でしか抜けられない終端バッゞなので、 そこぞ䞞めるず原因の唯䞀の手がかりである stderr を捚おた䞊でセッションを詰たせる。 merge --abort は MERGE_HEAD があるずきだけ撃぀index に觊れおいない倱敗に abort すべき ものは無い。

詰たった PR の立お盎しコンフリクト取り蟌み / CI 修正

Phase 10 で䜜った怜知prStatus.mergeStatus === 'conflicting' / checks === 'failing'は グリフを描くだけで終わっおいた。それを行動に繋げるのがこの局。方針は Phase 10 ず同じで、 codiva 自身が決定的にできるこずは codiva がやり無課金、刀断が芁るずころだけセッションぞ枡す。

  • 刀定は玔粋core/pr-recovery.ts。2 段に分かれおいるのが芁点:

    • prStuckKind(state) 
 PR だけを芋た詰たり方sync = 競合 / ci = 赀いチェック。 競合を CI より優先するベヌスを取り蟌めばチェックは回り盎すので、先に CI を盎しおも無駄。

    • recoveryKindFor(state) 
 䞊に「セッションが手を止めおいる」isTerminalStatus か぀ archived でないを掛けたもの。走行䞭に指瀺を割り蟌たせおも、そのタヌンの䜜業ず競合するだけ。

    • stuckKinds(state) 
 該圓する詰たり方を党郚競合ず CI 倱敗は同時に起きる。

    • prRecovered(state) 
 PR が確かに健党になったか緑 or マヌゞ枈み。

    終わった PR は詰たっおいない。mergeStatus === 'merged' ず 'closed'マヌゞされずに クロヌズはどちらも stuckKinds が空を返す。閉じた PR を競合ず同じに扱うず、autoSync が 誰も取り蟌む気の無いブランチにベヌスを取り蟌んで push し、autoFixCi が赀いチェックの修正を 1 タヌン課金しお䟝頌しおしたう。閉じたのは人間の刀断なので、codiva からは䜕も提案しない 再開したければ GitHub で reopen する → 次のポヌリングで通垞の状態に戻る。 衚瀺も専甚グリフ⊘・畳んだ灰色にしお、赀い ✗= 盎せず混同させない。

    分けたのは自動化の詊行回数を正しく数えるため。玠朎に「詰たっおいないならリセット」ず するず 2 段階で壊れる:

    1. 「今は走っおいるから察象倖」ず「もう詰たっおいない」を同じ関数で衚すず、指瀺を送った 盎埌= 走行䞭にリセットされる → 完了 → ただ赀い → たた送る、の無限ルヌプ。
    2. それを盎しおも、push 盎埌の PR は必ず checks: 'pending' / mergeStatus: 'unknown' を 経由する= 詰たっおはいないので、そこでリセットされる → 赀い → 䟝頌 → pending → 赀い、で䞊限が無意味になる。実際に倚いのは「䟝頌したが盎せなかった」ほうなので、これを 塞がないず意味が無い。

    なので返金は prRecovered緑を芋たずきだけ。詊すかどうかは recoveryKindFor、 どの皮類を詊すかは stuckKinds を有効なフラグず突き合わせお先頭から遞ぶautoFixCi だけ有効な人の「競合しおいお、か぀赀い」PR で、優先床 1 䜍の sync が無効だからず 諊めおしたわないように。

  • 取り蟌みは WorktreeManager.syncBase(wt, base)utils。merge() ず向きも cwd も逆で、 worktree の䞭で origin/<base>fetch 倱敗時はロヌカル <base>を取り蟌む。返り倀は SyncBaseResult の 4 倀で、投げない:

    結果 意味 次の手
    upToDate 既にベヌスを含む 䜕もしない
    updated クリヌンにマヌゞできた pushBranch しお終わりセッションを起こさない
    dirty 未コミット倉曎があるのでマヌゞしおいない 䜜業の持ち䞻セッションにたずめお任せる
    conflict 競合した merge --abort せず競合を残す → セッションに解決させる

    merge() が abort するのは共有されるベヌスツリヌを汚さないためで、syncBase の worktree は 1 セッション専甚だから残すほうが盎せる。どちらも -X ours 盞圓の自動解消はしない芏玄。 现かいが効く刀断が 3 ぀:

    • 未远跡ファむルは dirty にしない--untracked-files=no。git merge は未远跡ファむルが あっおも普通に通るので、゚ヌゞェントの走り曞き 1 個で無課金の経路を捚おおタヌンを䜿うのは損。
    • 既に merge 途䞭MERGE_HEAD ありなら conflict を返す。dirty 刀定に萜ずすず 「コミットか stash しおから取り蟌め」ずいう実行䞍胜な指瀺を送っおしたう。
    • detached HEAD は拒吊する。マヌゞコミットは HEAD に茉るがブランチ ref は動かないので、 push は no-op、PR は詰たったたた、なのに「取り蟌んで push したした」ず報告しおしたう。
  • CI 修正は远加の API を䜿わない。gh pr view --json 
statusCheckRollup の payload から 萜ちたチェック名ず detailsUrl を拟うutils/pr.ts の toFailingChecks → PrStatus.failingChecks。 MAX_FAILING_CHECKS 件で打ち切り。ログの取埗gh run view --log-failedず修正はセッションが行う。 failingChecks は checks === 'failing' のずきだけ茉せ、reducer は内容比范する 毎ポヌリング新しい配列になるので参照比范だず必ず「倉わった」こずになり、prStatus の 参照維持が壊れる。

  • 実行は SessionManager.recover(id, kind?)git ず send の䞡方を觊れる唯䞀の局。kind 省略時は recoveryKindFor が決め、明瀺するず /sync / /fix-ci ずしおポヌリング前でも効く。 セッションぞ送る指瀺文は i18n カタログm.recover.*から匕く — ログにナヌザヌ発話ずしお 残るので resume.instruction ず同じ扱いmessages 未泚入なら recover は no-op。

  • 自動化は PrCoordinator が「い぀」だけを決める。autoSync / autoFixCi既定 off。 䟝頌が発生した時点でタヌンが回る = 課金で有効化し、実行は DI された recover を呌ぶ。 1 セッション・1 皮類あたり MAX_AUTO_RECOVERY_ATTEMPTS2回で打ち切る — トリガヌが むベントではなく状態なので、「䟝頌したのに push されない」ず毎ポヌリング投げ続けおしたう。

  • UI は共有フック useRecoveryui/hooks.ts。䞀芧は /sync・/fix-ci遞択行ず Ctrl+F = /recover党件、y/n 確認、詳现は /sync・/fix-ciそのセッション。 Ctrl+F をフォヌカス暪断の chord にするのは Ctrl+R / Ctrl+A ず同じ理由。䞀括は 逐次実行する同䞀リポゞトリの worktree 矀なので git の index/ref ロックで朰し合う。

    • recovery.busy を「党キヌを飲む」busy に混ぜない。䞀括は N 件ぶんの git を盎列に 回すので数分に及びうる。党キヌを飲むず Ctrl+C を拟わないexitOnCtrlC: falseこの TUI では /exit すら打おず操䜜䞍胜になる/update の installing で螏んだ眠ず同じ。 塞ぐのは「もう䞀床立お盎しを始める」入口だけにしお、実行䞭は独立した行で知らせる。
    • 䞀括の結果は実際に成功した件数で報告する。党郚倱敗gh 未認蚌などしたのに 「N 件を実行したした」ず緑で出さない — 最初の゚ラヌぱラヌ欄ぞ回す。
  • worktree に觊るのはセッションが手を止めおいるずきだけ。この門は recoveryKindFor では なく SessionManager.recover 偎にある — 明瀺 kind を枡す /sync / /fix-ci にも効かせる 必芁があるからClaude が線集䞭の worktree で git merge を回すず曞き蟌みず競合する。 匟いたずきは { kind: 'busy' } を返しお「䜜業䞭です」ず出す。

孊習デヌタ利甚groveの譊告

codiva は䞊列セッションで倧量のコヌドを Claude ぞ流すため、claude.ai の「Help improve our AI models」 = モデル孊習ぞのデヌタ提䟛。Anthropic 内郚名 groveが ON のたた気付かずに䜿い続けるのを防ぐ。 譊告するだけで、codiva 偎の挙動は倉えない勝手に無効化はしない = ナヌザヌのアカりント蚭定を アプリが曞き換えない。

  • 刀定は 2 段構えutils/privacy.ts の fetchTrainingOptIn。安いほうから詊す: 0. 認蚌方匏の門番: API キヌ / Bedrock / Vertex / 独自 ANTHROPIC_BASE_URL のずきは claude.ai の 蚭定ず無関係か぀ API 利甚は孊習察象倖なので、キャッシュも読たずに 'unknown'。 過去に claude.ai ぞログむンした残骞で誀譊告しないため、この門番が最初に来る。
    1. ~/.claude.json の groveConfigCache[accountUuid]Claude Code が曞くキャッシュ。ネットワヌクも 認蚌情報も䞍芁。7 日より叀い倀は䜿わない。キヌは oauthAccount.accountUuid ず䞀臎させ、 アカりントが読めないずきに限り単䞀゚ントリを流甚する切替埌に前アカりントの倀を䜿わない。
    2. GET https://api.anthropic.com/api/claude_code_groveKeychain Claude Code-credentialsmacOS たたは ~/.claude/.credentials.json の OAuth トヌクンを䜿う。
  • キャッシュの信頌は非察称: 'off' はそのたた採甚しおここで終える安いが、'on' は必ず 問い合わせで確認する。claude.ai 偎で OFF にしおもこのキャッシュは曞き換わらないため、 信甚するず「蚀われた通り切ったのに譊告が出続ける」こずになる。確認が取れなかったずきだけ キャッシュの 'on' に据え眮く。
  • 刀定は 'on' | 'off' | 'unknown'core/privacy.ts。譊告は 'on' ず確定したずきだけ出す。 レスポンスの domain_excluded === true意味は未怜蚌も 'unknown' に倒す。
  • 起動をブロックしない / 終了を止めない: 合成ルヌトindex.tsxが render 前に投げ、 useTrainingOptIn が解決したらバナヌに泚意行が増える。終了時は AbortController で打ち切る。 securityKeychain呌び出しには signal ず timeout2 秒を必ず枡す — 生きた子プロセスが むベントルヌプを掎むず、終了しおもシェルのプロンプトが返らなくなる。abort 枈みシグナルでは addEventListener('abort') が発火しないので、問い合わせ前に signal.aborted を先手チェックする。
  • 倱敗はすべお 'unknown'非公開゚ンドポむントなので仕様倉曎で壊れうる。壊れたずきは 「黙る」= 誀った譊告を出さない。蚭定 privacyWarning: false で刀定自䜓を走らせない。
  • 描くのは bannerLines の行ではなく、その倖の PrivacySection。ヘッダのドラッグ遞択は bannerText(lines) ぞの caret index で、圓たり刀定が「行 index = 衚瀺行」を前提にしおいる core/banner-lines.ts の bannerCaretAt。譊告はコピヌ察象ではないうえ ⚠ は theme.ts が 持぀蚘号なので、遞択可胜なテキスト塊textRef の Boxの倖に眮いお行構成を揺らさない。

゚ンドポむントの実枬User-Agent 芁件などは TECH_NOTES.md を参照。

アップデヌト通知 / /update

npm 配信された自分自身の曎新を怜知しお知らせ、安党に確定できる経路のずきだけ適甚する。 「怜知は自動、確定操䜜は人手」ずいう PR 自動化ず同じ方針。

  • 玔粋ロゞックcore/update.ts: semver の precedence 比范compareVersions / isUpdateAvailable。prerelease 芏則たで実装、結果 unionUpdateCheck = available / up-to-date / unavailableぞの倉換resolveUpdateCheck、むンストヌル経路 InstallKind = global / local / npx / unknownから曎新コマンドを組む updateCommandForargv 配列で返す/ updateCommandLine、自己曎新の可吊 canSelfUpdate。 DI 境界 UpdateServiceinitial / check() / install()ずダむアログ状態 UpdateViewState もここ。
  • I/Outils/update.ts: fetchLatestVersion が https://registry.npmjs.org/<pkg>/latest を 1 回だけ匕く党 packument 21KB ではなく 2.3KB。認蚌䞍芁。3 秒でタむムアりトし throw しない。 タむマヌは unref、倖郚 AbortSignal でも打ち切れる。installKindFor は packageRoot / execPath / cwd / platform を匕数で受けるパス比范のみnotifyCommand(spec, platform) ず同じ方針でテスト可胜。runUpdate が execFileシェルなしで npm install を実行し、倱敗は stderr の最終行を ok: false で返す。createUpdateService がこれらを束ねる。
  • 経路刀定の安党偎: npx / dlx / bunx のキャッシュパス芁玠の完党䞀臎で刀定。郚分䞀臎だず bunx-tools のような無関係なディレクトリを誀怜出するは npx、volta / asdf 配䞋・Windows・ それ以倖の刀別䞍胜は unknown。実行するのは global だけcanSelfUpdateで、unknown / local / npx は実行すべきコマンドの提瀺に留める。静的刀定が unknown のずきだけ npm root -g を 1 本起こしお global ぞ栌䞊げするhomebrew の Cellar・npm config set prefix・ pnpm/yarn global を拟うため。暙準的な配眮ではサブプロセス 0 本。npm install -g の cwd は ホヌムに固定する察象リポゞトリの .npmrc に宛先を曞き換えられないため。
  • 配線: 合成ルヌトindex.tsxが起動時に createUpdateService を䜜り、await せずに initial を投げるmodelCatalog ず同じ扱い。終了時に updateAbort.abort()。App は useUpdateCheck(updater?.initial) で state に解決し、available のずきだけ bannerLines の updateLatest ぞ枡す最新・未確認ではヘッダに行を増やさない。 ヘッダの文字組みは玔粋な core/banner-lines.ts、色は accent トヌン →ui/theme.ts の theme.accent.tsx に生の色名を曞かない。/update は useCommandRunner の update ハンドラ → 毎回 check() し盎し、UpdateDialog を出す。
  • キヌ凊理: UpdateDialog は presentational で useInput を持たない。キヌは䞀芧ビュヌの 単䞀ハンドラが confirm / confirmResumeAll ず同じ䜍眮で凊理する曎新可胜なら y/n、 それ以倖は任意キヌで閉じる。非同期の決着は䞖代カりンタで無効化し、閉じた埌・開き盎した 埌に stale な結果でダむアログが蘇らないようにする。
  • モヌダルの盞互排他は必須: PermissionDialog は自前の useInput を持ち、Ink は 1 ぀の入力 チャンクをマりント䞭の党ハンドラぞ配る。同時に出おいるず曎新確認の y が未読のツヌル実行の 蚱可も兌ねおしたうこのビュヌがキヌを飲んでも盞手は独立に反応する。pending の導出に !update を入れお構造的に犁じ、さらにモヌダル䞭はマりスレポヌトも飲む クリックで focus が list に移るず蚱可ダむアログが立぀経路を塞ぐ。
  • 実行䞭に操䜜䞍胜にしない: installing äž­ã‚‚ Esc は通す。codiva は Ctrl+C を拟わず exitOnCtrlC: false終了は /exit だけなので、党キヌを飲むず npm install が終わるたで 最長 INSTALL_TIMEOUT_MS のあいだ䜕もできなくなる。Esc はダむアログを閉じるだけで npm は続行する。
  • 蚭定: updateCheck既定 on。false で起動時の通信を完党に止める/update は unavailable を返すだけになる。

クラッシュ時の埌始末端末の埩旧 / クラッシュログ

TUI は alt screen + マりスレポヌト?1002/?1006で動くため、異垞終了は 2 ぀の被害を同時に出す。

  1. 端末が壊れたたた残る。マりス捕捉が有効なたただずスクロヌルのたびに端末が \x1b[<64;
M を送り、シェルには倧量の文字が入力されたように芋える。
  2. 理由が残らない。䟋倖のスタックは stderr ぞ出るが、alt screen を抜けた瞬間に画面ごず消える ナヌザヌには「突然タヌミナルに戻った」ずしか芋えない。

察策は 3 局。どの局も他の局の代わりにはならない䞋に行くほど匷い死に方に察応する。

å±€ 実装 効く死に方
teardowntoggleEscape の process.on('exit') + setupTerminal().teardown utils/alt-screen.ts / utils/mouse.ts / utils/terminal-mode.ts 正垞終了・process.exit・捕捉できた䟋倖
クラッシュハンドラ bootstrap/crash-handler.ts uncaughtException / unhandledRejection
起動時の自動修埩 + --reset-terminal setupTerminal() 冒頭の disableMouseReports() / core/cli.ts 匷制終了OOM の abort・SIGKILL・segfault。JS が䞀切走らない
  • クラッシュハンドラの順序は 端末埩元 → 状態 flush → レポヌト組み立お → 通垞バッファぞ出力 → ログ曞き出し → exit(1)。端末を先に戻すのは、以降の出力をスクロヌルバックに残すため。
  • ログは ~/.codiva/logs/crash-<ISO時刻>-<pid>.log20 件でロヌテヌション。曞き蟌みは 同期盎埌に process が消えるので非同期では間に合わない。敎圢・ファむル名・ ロヌテヌション刀定は玔粋な core/crash.ts、I/O は utils/crash-log.ts。
  • 蚺断情報にメモリ䜿甚量ずセッションのステヌタス内蚳を含める。1 セッション = claude サブプロセス 1 本最倧 ~1GiBなので、OOM 仮説の裏取りにはこの 2 ぀が芁る。
  • V8 のヒヌプ枯枇は JS ハンドラで拟えないabort で即死する。この経路だけは Node の 蚺断レポヌトprocess.report.reportOnFatalErrorに任せ、report.*.json を同じ ~/.codiva/logs/ ぞ出す。reportOnSignal / reportOnUncaughtException は自前ハンドラず 二重になるので off。
  • シグナルSIGTERM / SIGHUPで殺された堎合も kind: signal で蚘録する。「萜ちた」ず 「kill された端末を閉じた等」を埌から切り分けるため。
  • 蚭定 crashLog: false でファむル出力自前レポヌト + 蚺断レポヌトを止められる。 理由の衚瀺ず端末の埩元は蚭定に関係なく行う。

ログのメモリ䞊限OOM 察策

実際に FATAL ERROR: Ineffective mark-compacts near heap limit で萜ちた。原因は 「保持しすぎ」ず「確保しすぎ」の 2 ぀で、察策も 2 ぀に分かれる。

問題 察策 堎所
SessionState.messages が無制限に䌞び、远蚘ごずに党䜓コピヌO(n²) 件数 MAX_LOG_ENTRIES ず合蚈文字数 MAX_LOG_CHARS先に瞛られた方で叀い方から萜ずす+ 1 件あたり MAX_LOG_ENTRY_CHARS  を付けお切る core/log-buffer.ts の pushLogEntry
詳现ビュヌが曎新ごずにログ党䜓を折り返し + Markdown 再パヌス ゚ントリ単䜍のメモ化幅ずプレフィックスが同じなら再利甚+ 保持行数の䞊限 MAX_CACHED_ROWSLRU core/scroll.ts の logLines
ツヌル結果の巚倧ペむロヌド10MB の Read / Bashを平坊化 → 党行 split 読む 200 文字だけ材質化asStringHead。tool_use の入力Bash の heredoc 等も先に切る core/claude-parse.ts
streamingText に 1 メッセヌゞ党䜓を溜め、毎フレヌム党䜓を split 末尟 MAX_STREAM_PREVIEW_CHARS だけ保持描くのは最埌の 1 行 core/log-buffer.ts の clipStreamText
埩元時に党セッションのトランスクリプト各数 MBを同時読み蟌み 1 本ず぀読む倉換埌すぐ回収される+ 読みながら畳むHistory+ capLogEntries bootstrap/restore-sessions.ts / core/transcript.ts

前提ずしお **ログは䌚話の「蚘録」ではなく「衚瀺」**である。正本は CLI のトランスクリプト ~/.claude/projects/ 、埩元は core/transcript.tsなので、叀い行を萜ずしおも読み返す手段は残る。

件数だけでは䜕も瞛れない1 件は 1 文字でも MAX_LOG_ENTRY_CHARS でもよいので、件数 × 1 件䞊限 = 4000 䞇文字。描画コストは文字数に比䟋し、しかも展開埌の行DisplayLine + スパンは元テキストの 数倍を占めるので、文字数の予算ずキャッシュの行数の予算の 2 ぀が実際の䞊限になっおいる。 MAX_CACHED_ROWS は soft budget で、描画䞭のログの行は远い出さない自分が次に䜿う行を捚おお 毎フレヌム再展開するのを避けるため。したがっお保持量の実効䞊限は 「開いおいるログ 1 本MAX_LOG_CHARS で瞛られる+ MAX_CACHED_ROWS」。

䞍倉条件:

  • 远蚘の経路は pushLogEntry だけ。[...state.messages, entry] を新しく曞かない appendLog ず applyAgentEvent の远蚘はすべおここを通す。䟋倖は notice の coalesce = 曞き換え = 末尟 1 件の差し替えで、件数を増やさないので䞊限に関係しない。
  • seq は振り盎さない。描画キヌが <seq>:<行> なので、トリムしおも既存行のキヌは倉わらない = React の再マりントが起きない。ただし埌述のずおり行 index は倉わる。
  • logLines の返す行は read-only。メモ化で耇数フレヌムに共有されるため、 呌び出し偎で曞き換えないselectionSlices のように必ず新しい配列を䜜る。

既知のトレヌドオフ: スクロヌル䜍眮ScrollAnchor の数倀ず遞択䜍眮LogPointは 文曞先頭からの衚瀺行 index なので、䞊限に達したログが叀い行を萜ずすずその分だけ意味がズレる 䞊ぞスクロヌルしお読んでいる最䞭に新しい行が来るず、ビュヌが萜ちた行数ぶん新しい方ぞ動く。 起きるのは「䞊限に達した」か぀「スクロヌル䞭」か぀「远蚘が続いおいる」の同時成立時だけで、 萜ちる= 党郚読めなくなるよりは軜い副䜜甚ずしお受け入れおいる。ただし遞択は捚おる SessionDetail が先頭゚ントリの seq の倉化を怜知しおクリアする— 觊っおいない行がコピヌされる のは副䜜甚ずしお重すぎるため。盎すなら行 index ではなく DisplayLine.key<seq>:<行>。 トリムでも远蚘でも䞍倉を基準にする必芁がある。

React の dev ビルドずヒヌプ枯枇描画ごずに氞久保持される

䞊の「ログのメモリ䞊限」を入れた埌も、ナヌザヌ環境で再び OOM で 3 回萜ちた ~/.codiva/logs/report.*.json = Node の蚺断レポヌト。old_space が 4.2GB で large_object_space は 55MB だけ = 小さいオブゞェクトが倧量に生存。前回ずは別の原因で、 今回は「確保しすぎ」ではなく玔粋な保持挏れだった。

ヒヌプスナップショットの䞊䜍は PerformanceMeasure × 60,003= 20,000 描画 × 3ず Components ⚛ / Changed Props / Scheduler ⚛ ずいった文字列だった。正䜓は React 19.2 の Performance Tracks既報: ink#869 / facebook/react#35761。 どちらも「Node の performance バッファが回収されない」こずが結論で、察策も NODE_ENV=production。 codiva では蚺断レポヌトから独立に同じ結論に至った:

  • react-reconciler は dev ビルドのモゞュヌル評䟡時に supportsUserTimingconsole.timeStamp ず performance.measure があるかを確定する。 Node には䞡方あるので必ず有効になる。
  • 以埌レンダヌごずに performance.measure() を 3 本積む。Node の user timing は 呌んだ偎が捚おるたで保持され続けるブラりザの devtools が消費する前提の API なので、 長時間動く Node プロセスでは単玔なリヌクになる。
  • dist/index.js は bin から node で盎に起動され NODE_ENV は未蚭定 = 利甚者は必ず dev ビルドだった。
条件空 Box を 8,000 回再描画 氞久保持 perf ゚ントリ 所芁
dev ビルド埓来 2,230 B/フレヌム 24,003 ä»¶ 414ms
NODE_ENV=production 117 B/フレヌム 0 ä»¶ 166ms
dev + 定期 clearMeasures() 174 B/フレヌム 1 ä»¶ 406ms

ストア賌読は ~100ms スロットルなので描画は玄 10/秒 ⇒ 箄 86MB/時。既定のヒヌプ䞊限 ~4GB に 半日〜1 日で到達する。描画内容ずは無関係なので、ログの䞊限では止められなかった。

察策は 2 段:

  1. src/index.tsx を起動シムにする本筋。process.env.NODE_ENV ??= 'production' を ./main の動的 import より前に眮く。ESM の static import は巻き䞊げられお本文より先に 評䟡されるため、シムに static import を 1 本足すだけで無効化されるtsup の banner も、 シバンの env -S も間に合わない。埌者は node <path> 盎叩き = mise 経由の起動で シバンを通らないので特に圓おにならない。tests/entry-shim.test.ts が番人。
  2. bootstrap/perf-timeline.ts が 30 秒ごずにタむムラむンを掃陀する保険。 NODE_ENV=development で起動したずきや、将来 React / Node が別の圢で user timing を 積み始めたずきに効く。保持量の䞊限が「30 秒ぶん」になる。

副䜜甚ずしお dist が 2 ファむルシム + チャンクになった。bin が指すのは dist/index.js の たたで、パッケヌゞルヌトの解決packageRootFromも「package.json の 1 ぀䞋」ずいう前提を 保っおいる。

子プロセスぞ枡す環境倉数NODE_ENV を挏らさない

process.env ぞの代入は spawn した子プロセス党郚に継承される。゚ヌゞェントのシェルは codiva の子なので、䞊の NODE_ENV=production をそのたた継がせるず、セッション内で叩く npm install / npm ci が --omit=dev ず解釈され devDependencies が黙っお入らない 型定矩もテストランナヌも欠けた状態になり、出るのは「䟝存が無い」ではなく TS7016 のような 型゚ラヌなので、原因が npm だず気付けない。issue #103。NODE_ENV を芋るツヌルはほかにも あるので、npm 固有の回避--include=devではなく env を挏らさない方向で盎す。

  • 起動シムは自分で立おたずきだけ CODIVA_NODE_ENV_INJECTED=1 を眮く??= が既存倀を 尊重するのず同じ理由で、ナヌザヌが明瀺した NODE_ENV は子にもそのたた枡す。
  • core/child-env.ts の childEnv(env)玔粋が、その目印を芋お NODE_ENV ず目印自身を 萜ずしたコピヌを返す。実 process.env ぞの適甚は utils/child-env.ts の childProcessEnv()。
  • codiva が起こすプロセスには必ずこれを枡すcodex exec / grok agent stdio / git / gh / login CLI / 通知・URL オヌプン / npm。Claude は SDK 経由なので utils/claude-query.ts の claudeQueryOptions.env を被せた queryに入口を 1 本化した。Options.env は process.env ずマヌゞされず䞞ごず眮き換えるので、枡すのは垞に党䜓のコピヌにする。
  • 番人は src/utils/child-env.spec.tsnode:child_process を import する utils は childProcessEnv を通す / SDK の query を倀ずしお import しおよいのは claude-query.ts だけず tests/entry-shim.test.ts目印の代入が ??= より前にある。

残っおいる䞊流の問題Ink のキャッシュ

Ink 7.1.1 は measure-text.js ず wrap-text.js で䞊限のないモゞュヌルレベルキャッシュ キヌ = テキスト党文を持ち、解攟経路が無い。玄 100 文字の行 1 本で玄 1.7KB、 4,000 文字の <Text> 1 描画で玄 17.8KB が氞久に残る。codiva 偎でできるのは 毎フレヌム倉わる長い文字列を枡さないこずなので、ストリヌミング䞭の本文は streamLines(text, width, 
) で行に折り返しおから 1 行ず぀枡す。末尟に足すだけなら 確定した行の文字列は倉わらないのでキャッシュに圓たり続け、毎デルタで新しく積たれるのは 曞きかけの最終行 1 本だけになる途䞭テキストを Markdown 敎圢したり、行の途䞭で頭を 切ったりするずこの性質が壊れお党行が毎フレヌム別のキヌになる。 䞊限そのものは ink 偎の修正が必芁なので issue で報告しおいる ink#986。

蚭蚈刀断

刀断 理由
埩元は「メタ + SDK resume」で、ログは氞続しない state.json を小さく保぀。䌚話履歎は SDK の resume が持぀ので二重管理しない。埩元盎埌はアむドル衚瀺、远加指瀺で継続
埩元セッションは遅延 resume起動時に起こさない セッション毎に ~1GiB のサブプロセスを起動時に乱立させない。觊られたものだけ起こす
終了は abort() ではなく stop()quiet 実行䞭セッションを failed にせず resumable のたた保存するためquit ず「1件砎棄」を区別
「砎棄d」ず「削陀x / /remove / /clear」を分ける 砎棄は worktree を消しお行を archived ずしお残す䜜業の蚘録が芋える。だがブランチに叀い PR が付いおいるず、その行は䞀括立お盎しCtrl+F = recoverableSessionsの候補ずしお毎回挙がり続ける。削陀は store から行ごず萜ずすので、蚘録も䞀括操䜜の察象も同時に消える。/clear も worktree/ブランチを残さない残すず「消したのにディスクに残る」ぶんが芋えない負債になる
削陀で worktree の陀去に倱敗したら行を残す ディスクにディレクトリが残っおいるのに䞀芧から消すず、存圚するものが芋えなくなる。゚ラヌを出しお行を残し、/clear は成功した件数だけ数える
通知の発火刀定は玔粋関数・遷移時のみ テスト可胜にし、ストリヌミングの連続曎新で鳎り続けるのを防ぐ。OS I/O は utils に隔離し best-effort
蚭定怜蚌は toConfig() に集玄・䞍正倀は既定ぞ 蚭定ミスで TUI をクラッシュさせない。SDK union は実行時リテラルで怜蚌型が倉われば型゚ラヌ
分離手段は git worktree 同䞀リポゞトリの䞊列䜜業では最軜量。ブランチがそのたた成果物になる。Docker 等はMVPではオヌバヌキル
競合は「怜知のみ」で自動解消しない 汎甚的に安党なマヌゞ競合の自動解消は存圚しない-X ours/theirs はコヌドを無蚀に捚おる。可芖化conflict バッゞに留め、解消は人手に委ねる
PR は draft で䜜り、緑になっおから ready 化 チェックは PR が無いず走らない鶏卵。完成前に push→draft で足堎を䜜り、gh pr checks が緑になった時点で ready ぞ。確定操䜜は自動でも“レビュヌ可胜”状態たでに留める
origin 远埓は䜜成時のみ皌働䞭は pull しない 皌働䞭 worktree ぞ取り蟌むず未コミット倉曎ず競合し埗る。䜜成時に origin/<base> から切る安党な郚分集合に限定
PR 自動化は PrAutomation ずしお DI・best-effort gh 未導入/未認蚌/オフラむンでもセッションを壊さない。core は gh を盎接知らず、utils/pr.ts に隔離
自己曎新は経路を刀定できたずきだけ実行する npm i -g は npx キャッシュ・volta 配䞋・別 prefix では宛先が違い、環境を壊す。刀定䞍胜unknownならコマンドの提瀺だけに留め、誀爆のコストを「自動化されない」に限定する
曎新チェックは latest の 1 リク゚スト・3 秒で打ち切り・await しない 起動を絶察にブロックしない。オフラむン/レゞストリ障害でも unavailable に萜ちるだけで TUI を壊さない。党 packument21KBではなく /latest2.3KBを匕く
「最新だった」ず「確認できなかった」を型で区別する オフラむンを「最新です」ず衚瀺するず嘘になる。UpdateCheck の union で UI が取り違えられないようにする
UI 文字列はカタログ集玄 + 蚭定で蚀語切替 日本語/英語の利甚者が混圚する。ハヌドコヌドを排し、远加蚀語も Lang/messages 拡匵だけで枈む
セッション = SDK query() 1本サブプロセス1本 SDK の蚭蚈単䜍に玠盎。プロセス分離により1セッションのクラッシュが他に波及しない
streaming input を垞甚単発 prompt を䜿わない 远加指瀺F-6ず質問ぞの回答F-7を同䞀機構で実珟でき、セッションを開いたたた維持できる
コアず UI の分離 + queryFn の DI SDK もネットワヌクも䞍芁なナニットテストを可胜にするN-3 の 80% カバレッゞはこれが前提
worktree は .codiva/worktrees/ 配䞋、ignore は .codiva/.gitignore*で自己完結 察象リポゞトリのファむルも .git/ の䞭も汚染しない。.git がファむルの環境linked worktree / submoduleでも動く
アプリ終了時に worktree を消さない N-4䜜業内容の保党。明瀺的な削陀操䜜のみで消す

リスクず察応

リスク 察応
SDK メッセヌゞ圢匏の想定違い Phase 1 のスパむクで実メッセヌゞを JSONL 収集し、reducer のテストフィクスチャに䜿う想定で曞かない
倧量ストリヌムで Ink 再描画が重い 䞀芧はステヌタス行のみ描画ログは詳现ビュヌでのみ、末尟ビュヌポヌトにクリップ+ 賌読スロットリング
質問怜出の誀刀定 MVP はヒュヌリスティック + 詳现ビュヌで远加指瀺を送っお察話を続けられるので誀刀定の実害は小さい。スパむク結果で改善
䞊列セッションのAPIコスト Backlog でコスト衚瀺を远加。MVP では result メッセヌゞの usage をログに残すのみ
ナヌザヌのメむンworktreeが dirty worktree は HEAD から切るため圱響なし。起動時チェックで譊告のみ衚瀺