Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/rules/sdk-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
- その provider に無い機能は `AgentCapabilities` で表明する(`permissions` / `interrupt` /
`setModel` / `resume` / `modelCatalog` / `usage` / `cost` / `transcript`)。UI は capability を
見て縮退する。**判定は純粋な `core/agent-capabilities.ts` を通す**(`supportsCapability` /
`capabilityLookup` / `agentSupports` / `showsAccountUsage`)。守ること 2 つ:
`capabilityLookup` / `agentSupports` / `showsAccountInfo`)。守ること 2 つ:
- **capability が分からないときは縮退しない**(未登録の provider・`agent` を持たない古い
セッションで機能を隠すと、動くはずの操作が黙って消える)。
- **「値が 0 だから自然に消える」に頼らない**。コスト・使用状況・トランスクリプト復元は
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ CI(`.github/workflows/ci.yml`)は `lint → typecheck → test → build`。
| セッションの状態・遷移 | `core/types.ts`(union)/ `core/status-meta.ts`(性質の表)/ `core/status-reducer.ts`(純粋 reducer) |
| 別のエージェントに対応させる | `core/agent-ports.ts`(`AgentAdapter` / `AgentCapabilities` / `PermissionDecision` = DI 境界)/ `core/agent-events.ts`(`AgentEvent` の語彙 + 全 provider 共通の畳み込み `applyAgentEvent`)/ `core/claude-adapter.ts`・`core/claude-parse.ts`・`core/claude-errors.ts`(Claude 実装の 3 点セット)/ `core/codex-adapter.ts`・`core/codex-parse.ts`・`core/codex-errors.ts` + `core/codex-events.ts`(JSONL の型)・`core/codex-models.ts`・`core/codex-rollout.ts`(rollout から解決済みモデル)・`utils/codex.ts`(`codex exec` の起動 = 唯一の I/O)/ `core/grok-adapter.ts`・`core/grok-parse.ts`・`core/grok-errors.ts` + `core/grok-events.ts`(ACP メッセージの型)・`core/grok-models.ts`・`utils/grok.ts`(`grok agent stdio` の起動 = 唯一の I/O)/ 行区切り JSON の枠切りは provider 非依存の `core/jsonl.ts`(Codex / Grok 共用)/ アダプタの登録は `bootstrap/build-manager.ts` の `buildAgents` |
| SDK メッセージの解釈 | `core/claude-parse.ts` **のみ**(`parseClaudeMessage`: SDKMessage → `AgentEvent[]`)+ `core/__fixtures__/*.jsonl`。Codex は `core/codex-parse.ts`(`parseCodexEvent`: `codex exec --json` の JSONL → `AgentEvent[]`)+ `core/__fixtures__/codex-*.jsonl`。Grok は `core/grok-parse.ts`(`createGrokParser`: ACP = JSON-RPC over stdio の通知 → `AgentEvent[]`)+ `core/__fixtures__/grok-*.jsonl` |
| capability による UI 縮退(コスト・使用状況・確認モード・ログ復元) | `core/agent-capabilities.ts`(`supportsCapability` = **不明なら縮退しない** / `capabilityLookup` / `agentSupports` / `showsAccountUsage`・純粋)/ `core/cost.ts` の `totalCostUsd(states, reportsCost)` / `bootstrap/usage-poller.ts` の `enabled` / `bootstrap/restore-sessions.ts` / `ui/status-footer.tsx` の `confirmSupported` |
| どのセッションが何で走っているかの表示 | `core/agent-display.ts`(`sessionAgentId` / `usesMultipleAgents`)/ `core/layout.ts` の `showsAgentColumn`(混在時だけ列を出す)/ `core/banner-lines.ts` の `agent`(ヘッダ = 既定)/ `core/scroll.ts` の `logLines(…, dividerFor)`(ログの切替区切り)/ `m.detail.followupPlaceholder(agent)`(詳細の入力欄) |
| capability による UI 縮退(コスト・プラン/使用状況・確認モード・ログ復元) | `core/agent-capabilities.ts`(`supportsCapability` = **不明なら縮退しない** / `capabilityLookup` / `agentSupports` / `showsAccountInfo` = プラン + 使用状況は**既定エージェント**で出し分け・純粋)/ `core/cost.ts` の `totalCostUsd(states, reportsCost)` / `bootstrap/usage-poller.ts` の `enabled` / `bootstrap/restore-sessions.ts` / `ui/status-footer.tsx` の `confirmSupported` |
| どのセッションが何で走っているかの表示 | `core/agent-display.ts`(`sessionAgentId` / `usesMultipleAgents`)/ `core/layout.ts` の `showsAgentColumn`(混在時だけ列を出す)/ `core/banner-lines.ts` の `agent`(ヘッダ = 既定。エージェント名・プラン・モデル・使用状況は `ui/hooks.ts` の `useDefaultAgent` / `useDefaultModel` を購読して**揃って**切り替わる)/ `core/scroll.ts` の `logLines(…, dividerFor)`(ログの切替区切り)/ `m.detail.followupPlaceholder(agent)`(詳細の入力欄) |
| エージェント切替時の引き継ぎ | `core/agent-handoff.ts`(`handoffInstruction` / `lastUserInstruction`・英語固定 = AI 向け文字列)/ `core/system-prompt.ts` の `handoff` 節 / `core/session.ts` の `setAgent`(**使い捨て**で次の `open()` が消費) |
| エージェントの切替(`/agent`)| `core/session-manager.ts`(一覧=既定: `getDefaultAgentId` / `setDefaultAgent`・詳細=切替: `listAgents` / `getSessionAgent` / `setSessionAgent`)/ `ui/agent-select.tsx`(`mode:'default'`=一覧 / `'session'`=詳細)/ `core/status-reducer.ts` の `agent_switched` |
| エージェントの導入・ログイン検出 | `core/agent-ports.ts` の `AgentAdapter.checkAvailability` / `AgentAvailability` / `core/agent-availability.ts`(`resolveDefaultAgentId` / `noAgentInstalled`・純粋)/ `utils/claude.ts` の `detectClaudeAvailability`・`utils/codex.ts` の `detectCodexAvailability`・`utils/grok.ts` の `detectGrokAvailability`(実 I/O)/ `SessionManager.checkAgents`(集約・キャッシュ)/ `ui/hooks.ts` の `useAgentAvailability` |
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ Codiva v0.3.1 3 セッション
- 使用率(`%`)は Claude が返さないプランもあります。その場合はゲージを出さず残り時間だけを表示します
(0% と誤読させないため)。
- API キー / Bedrock / Vertex 利用時はサブスク制限が無いため、この表示は出ません。
- **ヘッダは「次に動くエージェント」の説明**です。`/agent` で既定を Codex / Grok に切り替えると、
エージェント名・プラン・モデル・使用状況が揃って切り替わります(どちらもプランや使用状況を
報告しないので、その間はプラン行とゲージが消え、モデルは `モデル: CLI 既定` に戻ります)。
claude.ai への 5 分ごとの自動取得もその間は止まり、既定を Claude へ戻すと再開します。
- 画面下部のステータスバーはモード表示(`⏵⏵ 自動モード`)と操作ヒントだけです。プラン / 使用状況は
ヘッダに集約しています(詳細ビューで見たいときは Esc で一覧へ戻ります)。

Expand Down Expand Up @@ -317,15 +321,15 @@ Codiva v0.3.1 3 セッション
**Codex セッションの制約**(Claude セッションとの違い):

- **ツール使用の許可を尋ねません。** `codex exec` の JSON 出力モードは承認要求を CLI 内部で自動的に拒否してしまい、codiva 側へ上げる手段がありません。そこで codiva は「それらしい許可ダイアログ」を出さず、**サンドボックスを唯一の安全弁**にしています(設定 `codexSandbox`。既定の `workspace-write` では書き込みがセッションの worktree 内に限定されます)。`質問あり` の状態にもなりません。
- **コストを表示しません。** Codex はターン終了時にトークン数しか返さず、金額もアカウント全体の使用状況も運びません。ヘッダの合計コストには Codex のセッションを数えません(Claude ぶんだけの金額を「全体」として出さないため)。使用状況ゲージは Claude のアカウントの枠なので、**Codex / Grok だけで作業している間はヘッダに出ません**(取得もしません)。
- **コストを表示しません。** Codex はターン終了時にトークン数しか返さず、金額もアカウント全体の使用状況も運びません。ヘッダの合計コストには Codex のセッションを数えません(Claude ぶんだけの金額を「全体」として出さないため)。プラン表示と使用状況ゲージは Claude のアカウントの話なので、**`/agent` で既定を Codex にしている間はヘッダに出ません**(取得もしません)。
- **フッタのモード表示が `確認モード (非対応)` になります。** 許可を尋ねられないので、`確認モード` のままだと「待っていれば聞かれる」と読めてしまうためです(`shift+tab` の切替そのものは効きます)。
- **再起動後にログが復元されません**(セッションの続きを再開すること自体はできます)。ログの再構築は Claude CLI の記録ファイルを読む仕組みで、Codex の記録は形式が異なるためです。
- `/model` の選択肢は Codex 側のモデル一覧(`codex debug models`)になります。一覧を取得できない環境では「デフォルト」だけになります(推測でモデル名を並べません)。`/agent` で provider を切り替えると、互換性のない切替前のモデル指定は CLI 既定へ戻ります。Codex は実行イベントにモデル名を含めないため、`/model` で明示したモデル名をセッション一覧に表示します。

**Grok セッションの制約**(Claude セッションとの違い):

- **ツール使用の許可と質問はそのまま届きます。** Codex と違い、Grok は許可要求(`許可待ち`)と質問(`質問あり`)を codiva の双方向のやり取りで上げてくるので、いつもどおりダイアログで応答できます。
- **コストを表示しません。** Grok はターンの終わりにトークン数しか返さず、金額もアカウント全体の使用状況も運びません。ヘッダの合計コストには Grok のセッションを数えず、使用状況ゲージ(Claude のアカウントの枠)も Grok だけで作業している間は出ません
- **コストを表示しません。** Grok はターンの終わりにトークン数しか返さず、金額もアカウント全体の使用状況も運びません。ヘッダの合計コストには Grok のセッションを数えず、プラン表示と使用状況ゲージ(Claude のアカウントの話)も既定を Grok にしている間は出ません
- **再起動後にログが復元されません**(セッションの続きを再開すること自体はできます)。ログの再構築は Claude CLI の記録ファイルを読む仕組みで、Grok の記録は形式が異なるためです。
- `/model` の選択肢は Grok 側のモデル一覧になります。一覧を取得できない環境では「デフォルト」だけになります(推測でモデル名を並べません)。`/agent` で provider を切り替えると、互換性のない切替前のモデル指定は CLI 既定へ戻ります。Codex と違い Grok は**実際に動いているモデル名を自分で教えてくれる**ので、`/model` で明示していなくてもセッション一覧にモデル名が出ます。

Expand Down
27 changes: 18 additions & 9 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ codiva/
│ │ ├── 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 縮退の判定(不明なら縮退しない・showsAccountUsage
│ │ ├── agent-capabilities.ts # capability による UI 縮退の判定(不明なら縮退しない・showsAccountInfo
│ │ ├── agent-display.ts # 「どのセッションが何で走っているか」の判定(sessionAgentId / usesMultipleAgents)
│ │ ├── agent-handoff.ts # 切替先へ渡す状況説明(英語固定・systemPrompt に 1 回だけ載る)
│ │ ├── claude-adapter.ts # Claude 用 AgentAdapter(query() の組み立て・canUseTool の写像)
Expand Down Expand Up @@ -319,7 +319,7 @@ provider が増えてもビュー側の分岐は増えない(未登録のエ
エージェント名は固有名詞なので翻訳しない(モデル名と同じ i18n の例外)。

縮退の判定は**純粋な `core/agent-capabilities.ts`** に寄せてある(`supportsCapability` /
`capabilityLookup` / `agentSupports` / `showsAccountUsage`)。要点は 2 つ:
`capabilityLookup` / `agentSupports` / `showsAccountInfo`)。要点は 2 つ:

- **capability が分からないときは縮退しない**(`supportsCapability(undefined, …) === true`)。
未登録の provider・`agent` を持たない古いセッションで機能を隠すと、動くはずの操作が黙って
Expand All @@ -333,7 +333,7 @@ provider が増えてもビュー側の分岐は増えない(未登録のエ
| `/model` のダイアログ | `setModel` / `modelCatalog` | `ui/session-detail.tsx` | 開かずに理由を出す・選択肢を provider 別に出し分け |
| `Ctrl+C` のヒント | `interrupt` | `ui/session-detail.tsx` | ヒント行を出さない |
| 合計コスト(ヘッダ) | `cost` | `core/cost.ts` の `totalCostUsd(states, reportsCost)` | 報告しない provider のセッションを合計に数えない |
| 使用状況ゲージ(ヘッダ) | `usage` | `showsAccountUsage`(一覧の表示 + `bootstrap/usage-poller.ts` の `enabled`) | 使っていなければ**出さないし取りにも行かない**(5 分ごとの probe を立てない) |
| プラン名 + 使用状況ゲージ(ヘッダ) | `usage` | `showsAccountInfo`(一覧の表示 + `bootstrap/usage-poller.ts` の `enabled`) | **既定エージェント**が報告しなければ**出さないし取りにも行かない**(5 分ごとの probe を立てない) |
| 確認モードのフッタ表示 | `permissions` | `ui/status-footer.tsx` の `confirmSupported` | `確認モード (非対応)` に差し替える(下記) |
| トランスクリプト復元 | `transcript` | `bootstrap/restore-sessions.ts` | その provider のセッションでは読みにも行かない |
| 認証切れの文言 | —(`AgentLabel`) | 一覧・詳細・通知 | 駆動中の provider のコマンド名を出す |
Expand All @@ -343,12 +343,21 @@ provider が増えてもビュー側の分岐は増えない(未登録のエ
「待っていれば聞かれる」と読めてしまっていた(ツールは確認なしに実行される)。ダイアログを
偽装しないのと同じ理由で、**モード表示の側を正直にする**。

**使用状況ゲージを消す判定**(`showsAccountUsage`)は「新規セッションの既定エージェント、または
`archived` でないセッションのどれかが `usage` を報告する」。ゲージが表しているのは
その provider のアカウントの消費で、Codex / Grok だけで作業している人には読みようがない
(`archived` を数えないのは、乗り換えた人のヘッダにマージ済みのセッション 1 件で残り続けるのを
避けるため)。**表示と取得は同じ純関数を通す**ので、出していないゲージのために
`claude` のサブプロセスが立つことはない。
**ヘッダは「次に動くエージェント」の説明**にする。エージェント名・プラン名・モデル・使用状況は
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 プロセス

Expand Down
12 changes: 7 additions & 5 deletions docs/TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1313,13 +1313,15 @@ zsh: abort codiva
`agent.noneInstalled` の 1 行を出す(`noAgentInstalled` が全件未導入で確定したときだけ)
- [x] **残りの capability 縮退**: 判定を純粋な `core/agent-capabilities.ts` に集約
(`supportsCapability` = **不明なら縮退しない** / `capabilityLookup` / `agentSupports` /
`showsAccountUsage`)。「数字が 0 だから自然に消える」偶然に頼るのをやめ、明示的な分岐にした:
`showsAccountInfo`)。「数字が 0 だから自然に消える」偶然に頼るのをやめ、明示的な分岐にした:
- `cost`: `totalCostUsd(states, reportsCost)` が報告しない provider を合計から外す
(混在時に「Claude ぶんの合計」を全体として出さない)
- `usage`: 一覧が `showsAccountUsage`(既定エージェント or `archived` でないセッションの
どれかが `usage` を報告するか)でゲージを出し分け、**同じ純関数**を
`bootstrap/usage-poller.ts` の `enabled` にも渡す = 出さないゲージのために 5 分ごとの
`claude` probe を立てない
- `usage`: 一覧が `showsAccountInfo`(**既定エージェント**が `usage` を報告するか)で
プラン名とゲージを出し分け、**同じ純関数**を `bootstrap/usage-poller.ts` の `enabled`
にも渡す = 出さないゲージのために 5 分ごとの `claude` probe を立てない
(当初は「既定 or `archived` でないセッションのどれか」で判定していたが、それだと
`/agent` で Codex へ切り替えても Claude のプランと枠が残り、ヘッダの読み方が項目ごとに
食い違ったので**既定エージェントだけ**に狭めた)
- `permissions`: フッタの確認モード表示を `confirmSupported` で `確認モード (非対応)` に
差し替える(Codex では原理的に聞かれないのに「確認モード」と言い切っていた =
「待っていれば聞かれる」と読める嘘)。ダイアログ自体は偽装も抑止もしない
Expand Down
Loading
Loading