# 機能設計書 44-SSE イベントストリーム

## 概要

本ドキュメントは、OpenCodeにおけるSSE（Server-Sent Events）イベントストリーム機能の設計を記述する。リアルタイムイベント配信により、クライアントがサーバー側の状態変化を即座に受信できる仕組みを提供する。

### 本機能の処理概要

SSEイベントストリーム機能は、HTTP APIサーバーの `/event` エンドポイントを通じて、サーバー側で発生する各種イベント（セッション更新、メッセージ追加、ツール実行結果等）をリアルタイムでクライアントに配信する。Hono の streamSSE 機能を使用し、永続的な接続を維持しながらイベントをプッシュ配信する。

**業務上の目的・背景**：AIエージェントの処理はしばしば長時間にわたり、その間に複数のイベント（トークン生成、ツール呼び出し、エラー発生等）が発生する。SSEにより、クライアントはポーリングなしでこれらのイベントをリアルタイムに受信でき、ユーザーにスムーズなフィードバックを提供できる。また、Web UIやデスクトップアプリでの状態同期が効率的に行える。

**機能の利用シーン**：
- AIエージェントの応答ストリーミング表示
- セッション状態の変更通知
- ツール実行結果のリアルタイム表示
- 複数クライアント間での状態同期
- MCP接続状態の変更通知

**主要な処理内容**：
1. `/event` エンドポイントへの接続確立
2. 接続成功イベント（server.connected）の送信
3. Bus.subscribeAllによる全イベントの購読
4. イベントのJSON形式でのSSE配信
5. ハートビート送信（10秒間隔）
6. インスタンス終了時の自動切断

**関連システム・外部連携**：
- Hono streamSSE: SSE配信機能
- Bus: イベントバス（publish/subscribe）
- BusEvent: イベント型定義

**権限による制御**：HTTP APIサーバーと同じ認証・CORS制限が適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | セッション画面 | 主機能 | メッセージタイムラインのリアルタイム更新 |
| 26 | セッション | 主機能 | TUIでのセッション状態同期 |
| 25 | ホーム | 補助機能 | MCPサーバー状態の表示 |

## 機能種別

リアルタイム通信 / イベント配信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| directory | string | No | 対象ディレクトリ（クエリまたはヘッダー） | 有効なパス |

### 入力データソース

- HTTPリクエスト（GET /event）
- Bus経由で発行される全イベント

## 出力仕様

### 出力データ形式

```
data: {"type":"server.connected","properties":{}}

data: {"type":"session.updated","properties":{...}}

data: {"type":"server.heartbeat","properties":{}}
```

### イベント型定義

```typescript
BusEvent = {
  type: string,      // イベント種別
  properties: object // イベント固有データ
}
```

### 主要イベント種別

| イベント種別 | 説明 |
|-------------|------|
| server.connected | 接続成功通知 |
| server.heartbeat | ハートビート（10秒間隔） |
| server.instance.disposed | インスタンス終了 |
| session.created | セッション作成 |
| session.updated | セッション更新 |
| message.created | メッセージ作成 |
| message.updated | メッセージ更新 |

### 出力先

- HTTPレスポンス（text/event-stream）

## 処理フロー

### 処理シーケンス

```
1. 接続確立
   ├─ GET /event リクエスト受信
   ├─ X-Accel-Buffering: no ヘッダー設定
   ├─ X-Content-Type-Options: nosniff ヘッダー設定
   └─ streamSSE でストリーム開始

2. 初期イベント送信
   └─ server.connected イベント送信

3. イベント購読開始
   └─ Bus.subscribeAll で全イベントを購読

4. ハートビート開始
   └─ 10秒間隔で server.heartbeat 送信

5. イベント配信ループ
   ├─ Bus からイベント受信
   ├─ JSON形式でSSE送信
   └─ server.instance.disposed 受信時は切断

6. 接続終了処理
   ├─ ハートビートタイマー停止
   ├─ Bus購読解除
   └─ ログ出力
```

### フローチャート

```mermaid
flowchart TD
    A[GET /event 受信] --> B[ヘッダー設定]
    B --> C[streamSSE開始]
    C --> D[server.connected送信]
    D --> E[Bus.subscribeAll]
    E --> F[ハートビートタイマー開始]
    F --> G{イベント受信?}
    G -->|Yes| H[JSON形式でSSE送信]
    H --> I{instance.disposed?}
    I -->|Yes| J[ストリーム終了]
    I -->|No| G
    G -->|切断検知| K[クリーンアップ]
    K --> L[タイマー停止]
    L --> M[購読解除]
    M --> N[ログ出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | 初回接続通知 | 接続確立時に server.connected を必ず送信 | 接続確立時 |
| BR-44-02 | ハートビート間隔 | 10秒間隔でハートビートを送信 | 接続維持中 |
| BR-44-03 | 自動切断 | server.instance.disposed 受信時にストリーム終了 | イベント受信時 |
| BR-44-04 | バッファリング無効 | X-Accel-Buffering: no でプロキシバッファリング防止 | 接続確立時 |

### 計算ロジック

ハートビート間隔：10,000ミリ秒（10秒）

## データベース操作仕様

本機能はデータベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 接続切断 | クライアントが切断 | onAbort でクリーンアップ実行 |
| - | 書き込み失敗 | ストリーム書き込みエラー | 切断として処理 |

### リトライ仕様

サーバー側でのリトライは実装されていない。クライアント側で再接続を実装する必要がある。

## トランザクション仕様

イベント配信はトランザクション管理なし。各イベントは独立して配信される。

## パフォーマンス要件

- 永続接続のため、接続数に応じたリソース消費
- ハートビートで接続維持確認
- イベントは即座に配信（遅延なし）

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

- HTTP APIサーバーと同じ認証が適用
- CORS設定による不正オリジンからのアクセス防止
- ディレクトリスコープでのイベント分離

## 備考

- ログエンドポイント（/log）からのリクエストはログ出力をスキップ
- streamSSE はHonoのストリーミング機能を使用
- X-Content-Type-Options: nosniff でMIMEタイプスニッフィング防止

---

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

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

### 推奨読解順序

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

イベントデータの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | bus-event.ts | `packages/opencode/src/bus/bus-event.ts` | BusEvent型定義を確認 |

**読解のコツ**: BusEvent.define() でイベント種別と properties スキーマを定義。payloads() で全イベント型のユニオンを取得。

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

SSEエンドポイントの実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | server.ts | `packages/opencode/src/server/server.ts` | /event エンドポイント（485-541行目） |

**主要処理フロー**:
1. **485-501行目**: エンドポイント定義（describeRoute）
2. **502行目**: ログ出力「event connected」
3. **503-505行目**: プロキシバッファリング防止ヘッダー設定
4. **506-512行目**: server.connected 初期イベント送信
5. **513-519行目**: Bus.subscribeAll でイベント購読
6. **523-530行目**: ハートビート setInterval（10秒）
7. **532-539行目**: onAbort でクリーンアップ

#### Step 3: イベントバスを理解する

イベントの発行と購読メカニズムを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/opencode/src/bus/index.ts` | Bus.subscribeAll 実装 |
| 3-2 | bus-event.ts | `packages/opencode/src/bus/bus-event.ts` | イベント定義パターン |

**主要処理フロー**:
- **Bus.publish**: イベントの発行
- **Bus.subscribe**: 特定イベントの購読
- **Bus.subscribeAll**: 全イベントの購読

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

```
GET /event
    │
    └─ streamSSE(c, async (stream) => {...})
           │
           ├─ stream.writeSSE({data: server.connected})
           │
           ├─ Bus.subscribeAll(async (event) => {...})
           │      │
           │      └─ stream.writeSSE({data: JSON.stringify(event)})
           │
           ├─ setInterval(() => {...}, 10_000)
           │      │
           │      └─ stream.writeSSE({data: server.heartbeat})
           │
           └─ stream.onAbort(() => {...})
                  │
                  ├─ clearInterval(heartbeat)
                  └─ unsub()
```

### データフロー図

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

GET /event
       │
       ▼
streamSSE開始 ───▶ server.connected
       │
       ▼
Bus.subscribeAll
       │
       ▼
イベント受信 ───▶ JSON変換 ───▶ SSE送信
       │
       ▼
10秒間隔 ───▶ server.heartbeat
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| server.ts | `packages/opencode/src/server/server.ts` | ソース | SSEエンドポイント実装（485-541行目） |
| index.ts | `packages/opencode/src/bus/index.ts` | ソース | イベントバス実装 |
| bus-event.ts | `packages/opencode/src/bus/bus-event.ts` | ソース | イベント型定義 |
| session/index.ts | `packages/opencode/src/session/index.ts` | ソース | セッションイベント発行元 |
| mcp/index.ts | `packages/opencode/src/mcp/index.ts` | ソース | MCPイベント発行元 |
