# 通知設計書 3-Permission required

## 概要

本ドキュメントは、OpenCodeアプリケーションにおける「Permission required（権限要求）」システム通知の設計を記述します。セッションで権限が必要になった際に、ユーザーに対してデスクトップシステム通知およびアプリ内トースト通知を表示する機能です。

### 本通知の処理概要

**業務上の目的・背景**：AIエージェントはファイル編集、コマンド実行などの操作を行う際にユーザーの許可を必要とします。ユーザーが他のアプリケーションで作業中の場合、権限要求を見逃すとエージェントの処理が停滞します。この通知により、ユーザーは即座に権限要求に気づき、適切な判断を下すことができます。

**通知の送信タイミング**：SDKイベント `permission.asked` が発生した時に送信されます。これはエージェントがツールの使用許可を求めていることを示します。ただし、自動承認設定（autoResponds）が有効で自動応答される場合はスキップされます。

**通知の受信者**：現在OpenCodeアプリケーションを使用しているユーザー。ただし、設定画面で `settings.notifications.permissions` がオフの場合はシステム通知は送信されません。アプリ内トーストは常に表示されます（ただし現在のセッション表示中を除く）。

**通知内容の概要**：通知タイトル「Permission required」と、「{セッションタイトル} in {プロジェクト名} needs permission」という説明が表示されます。「Go to session」アクションで該当セッションに遷移できます。

**期待されるアクション**：ユーザーは通知を受け取った後、該当セッションに移動して権限要求を確認し、許可または拒否の判断を行うことが期待されます。

## 通知種別

- デスクトップシステム通知（プラットフォームネイティブ通知）
- アプリ内トースト通知（persistent: 永続的）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（イベント駆動） |
| 優先度 | 高 |
| リトライ | なし |

### 送信先決定ロジック

1. `permission.asked` イベントを受信
2. 自動承認（autoResponds）が適用されるかチェック
3. 自動承認される場合は送信スキップ
4. クールダウン期間（5秒）内の重複リクエストはスキップ
5. サウンド設定が有効ならサウンド再生
6. システム通知設定が有効ならplatform.notify呼び出し
7. 現在のセッション・子セッションを表示中でなければトースト表示

## 通知テンプレート

### システム通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | `notification.permission.title`（"Permission required"） |
| 説明 | `notification.permission.description`（"{sessionTitle} in {projectName} needs permission"） |
| リンク先 | `/{base64EncodedDirectory}/session/{sessionID}` |

### アプリ内トースト通知の場合

| 項目 | 内容 |
|-----|------|
| アイコン | "checklist" |
| タイトル | `notification.permission.title`（"Permission required"） |
| 説明 | `notification.permission.description` |
| 永続性 | persistent: true（手動dismissまで表示） |
| アクション1 | "Go to session" - セッションに遷移 |
| アクション2 | "Dismiss" - トーストを閉じる |

### 本文テンプレート

```
タイトル: Permission required
説明: {sessionTitle} in {projectName} needs permission
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルは使用されません |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| sessionTitle | セッションのタイトル | session情報 または "New session" | Yes |
| projectName | プロジェクト名 | getFilename(directory) | Yes |
| sessionID | セッションの一意識別子 | イベントプロパティ | Yes |
| directory | プロジェクトディレクトリパス | イベント名 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SDKイベント | `permission.asked` | autoRespondsでない かつ クールダウン期間外 | 権限要求時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| autoResponds = true | 自動承認設定により自動応答される場合 |
| クールダウン期間内 | 同一セッションで5秒以内に同じイベントがあった場合 |
| 現在表示中のセッション | ユーザーが該当セッションを閲覧中の場合（トーストのみスキップ） |
| 親セッション表示中 | ユーザーが該当セッションの親を閲覧中の場合（トーストのみスキップ） |
| settings.notifications.permissions = false | システム通知のみスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[permission.asked イベント受信] --> B{autoResponds?}
    B -->|Yes| C[送信スキップ]
    B -->|No| D{クールダウン期間内?}
    D -->|Yes| C
    D -->|No| E[クールダウン記録]
    E --> F{サウンド設定有効?}
    F -->|Yes| G[サウンド再生]
    F -->|No| H{システム通知設定有効?}
    G --> H
    H -->|Yes| I[platform.notify呼び出し]
    H -->|No| J{現在のセッション?}
    I --> J
    J -->|Yes| C
    J -->|No| K[トースト表示]
    K --> L[終了]
    C --> L
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| session | セッション情報の取得 | SDK経由でアクセス |
| settings | 通知設定の確認 | ローカルストレージ |
| permission | 自動承認設定の確認 | permissionコンテキスト |

### テーブル別参照項目詳細

#### session

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | セッション識別 | sessionID一致 |
| title | 通知説明文 | - |
| parentID | 親セッション判定 | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| toastBySession | SET | トーストID管理用Map |
| alertedAtBySession | SET | クールダウン管理用Map |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| セッション取得失敗 | セッションが存在しない | デフォルト値"New session"を使用 |
| 通知API失敗 | プラットフォームAPI呼び出し失敗 | エラーをキャッチして無視 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| クールダウン | 同一セッションで5秒間 |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（24時間配信可能）

## セキュリティ考慮事項

- セッションタイトルにはユーザーが入力した内容が含まれる可能性あり
- プロジェクト名はファイルパスから取得されるため、機密性の低い情報
- 自動承認設定の状態によって通知の有無が変わるため、設定の整合性に注意

## 備考

- アプリ内トーストは persistent: true で手動dismissまで表示され続ける
- 同一セッションへの複数通知は、前のトーストをdismissしてから新しいトーストを表示
- クールダウン機構により、短時間での連続通知を防止

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

通知管理用のMap構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | toastBySession, alertedAtBySession Map（336-337行目）で管理構造を理解 |

**読解のコツ**: Mapを使った重複管理とクールダウン機構に注目。

#### Step 2: エントリーポイントを理解する

イベントリスナーと条件分岐を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | layout.tsx | `packages/app/src/pages/layout.tsx` | useSDKNotificationToasts関数（334-440行目）全体を理解 |
| 2-2 | layout.tsx | `packages/app/src/pages/layout.tsx` | permission.asked/question.askedの条件分岐（361-401行目） |

**主要処理フロー**:
1. **348-360行目**: イベントタイプの判定とworktree.ready/failedの処理
2. **361-366行目**: permission.askedまたはquestion.askedの判定
3. **369行目**: autoRespondsチェック
4. **383-395行目**: クールダウン、サウンド、システム通知の処理
5. **407-425行目**: トースト表示

#### Step 3: 自動承認ロジックを理解する

permission.autoRespondsの動作を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | permission.tsx | `packages/app/src/context/permission.tsx` | autoResponds関数の実装を確認 |

#### Step 4: i18nキーを理解する

通知テキストの多言語対応を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | en.ts | `packages/app/src/i18n/en.ts` | notification.permission.title（475行目）を確認 |
| 4-2 | en.ts | `packages/app/src/i18n/en.ts` | notification.permission.description（476行目）を確認 |

### プログラム呼び出し階層図

```
globalSDK.event.listen (layout.tsx:348)
    │
    ├─ permission.asked / question.asked イベント
    │      │
    │      ├─ permission.autoResponds チェック (layout.tsx:369)
    │      │
    │      ├─ クールダウン管理 (layout.tsx:383-386)
    │      │
    │      ├─ playSound (layout.tsx:389-391)
    │      │
    │      ├─ platform.notify (layout.tsx:392-394)
    │      │
    │      └─ showToast (layout.tsx:409-425)
    │             ├─ アクション: navigate(href)
    │             └─ アクション: dismiss
    │
    └─ dismissSessionAlert (layout.tsx:340-346)
```

### データフロー図

```
[入力]                    [処理]                              [出力]

SDKイベント               Layout - useSDKNotificationToasts
permission.asked  ────▶  autoResponds チェック  ──────────▶  システム通知
  │                      クールダウン チェック                アプリ内トースト
  │                           │
  ▼                           ▼
sessionID               toastBySession Map 管理
directory               alertedAtBySession クールダウン管理
                        showToast(persistent: true)
                        platform.notify()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| layout.tsx | `packages/app/src/pages/layout.tsx` | ソース | メイン処理・トースト管理 |
| permission.tsx | `packages/app/src/context/permission.tsx` | ソース | 自動承認ロジック |
| platform.tsx | `packages/app/src/context/platform.tsx` | ソース | システム通知API |
| settings.tsx | `packages/app/src/context/settings.tsx` | ソース | 通知設定管理 |
| toast.tsx | `packages/ui/src/components/toast.tsx` | ソース | トーストコンポーネント |
| en.ts | `packages/app/src/i18n/en.ts` | 設定 | 翻訳キー定義 |
