dsh-session-guard 0.1.2 → 0.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -1,106 +1,177 @@
1
- <p align="center">
2
- <strong>ピーク自動セッションゲート:週末モード + ピーク自動一時停止 + セッション級凍結 + バックエンド自動リトライ</strong>
3
- </p>
4
- <p align="center">
5
- <a href="README.en.md">English</a> · <a href="README.md">中文</a> · <strong>日本語</strong> · <a href="README.ko.md">한국어</a>
6
- </p>
7
- <p align="center">
8
- <a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-263146?style=flat-square"></a>
9
- <img src="https://camo.githubusercontent.com/2c11fb2e0e14bb9985c5acbe61123a7441c5ee63aa27fa6e04e2a707ebfd6022/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6473682d2d706c7567696e2d72656164792d3437384342463f6c6f676f3d646565707365656b266c6f676f436f6c6f723d7768697465" alt="dsh-plugin" style="max-width: 100%;">
10
- <img alt="Public beta" src="https://img.shields.io/badge/status-public%20beta-7da1de?style=flat-square">
11
- </p>
12
-
13
- # dsh-session-guard
14
-
15
- - [English README](./README.en.md)
16
- - [中文 README](./README.md)
17
- - [日本語 README](./README.ja.md)
18
- - [한국어 README](./README.ko.md)
19
- - [Installation guide](./INSTALL.md)
20
- - [中文安装指南](./INSTALL.zh.md)
21
- - [日本語インストールガイド](./INSTALL.ja.md)
22
- - [한국어 설치 안내](./INSTALL.ko.md)
23
- - [Changelog](./CHANGELOG.md)
24
- - [日本語 changelog](./CHANGELOG.ja.md)
25
- - [한국어 changelog](./CHANGELOG.ko.md)
26
-
27
- > **互換性について:** v0.1.1 には日本語(`ja`)と韓国語(`ko`)の辞書が含まれていますが、現在の公式 DSH リリースは `LocaleRuntime` 経由で `zh` と `en` のみを提供しています。純正 DSH で `ja` または `ko` を選択すると `locale "<id>" is not registered` で失敗します。公式 DSH が对应的 locale ID を追加するまで利用できません。上級ユーザーは DSH フォークを保守して更新してください。
28
-
29
- > ピーク課金時間帯に実行中のセッションを自動一時停止し、オフピーク/週末に自動再開。input-traffic の凍結ボタンと連携して**セッション級**ロックを実現。バックエンド**自動リトライ**は凍結/ゲート期間中は譲歩。カスタムセッションゲート(`agent.cancel keepInbox + goals.pause + session/event 安全境界 + followup 再開`)に基づき、dsh-task-control に依存しません。
30
-
31
- `dsh plugin` コマンドで组装 + バンドルパッチで装配する cordis プラグイン。dsh ソース変更も PR も不要。
32
-
33
- > 💡 **推奨理由**:DeepSeek は 2026-08-17 から**峰谷課金**を開始しました。ピーク時間帯の単価はオフピークの 2 倍。本プラグインはピーク時に実行セッションを自動一時停止、オフピーク時に自動再開し、長時間セッションの費用を最大 **50%** 削減。手動凍結(input-traffic ボタン経由)でセッションごとの精密制御が可能。
34
-
35
- ## 機能
36
-
37
- - **週末モード**:`Intl.DateTimeFormat` で週末を正しく識別(タイムゾーン正確)→ 週末は峰谷を無視して自由実行。
38
- - **ピーク自動一時停止(グローバル)**:ピーク入場時(かつ非週末)、全 running ルートセッションを自動一時停止。退峰時自動再開——**グローバルスイッチ、手動不要**。
39
- - **セッション級凍結/再開**:`sessionGuard` 冗余ポート + `POST /session-guard/rpc`、input-traffic 凍結ボタンでセッションごと透伝。`/pause /resume /cancel` 手動コマンドも提供。
40
- - **バックエンド自動リトライ(D9)**:turn/end 瞬時失敗(error/429/max-tokens)はアダプティブバックオフで自動再試行。永久失敗は停止。**凍結/ゲート期間中は譲歩**、セッションゲートを迂回しません。
41
- - **fail-open**:カスタムセッションゲート利用不可、session-guard 未インストール、設定サービス欠如——すべて静的降格、依存でクラッシュしません。
42
-
43
- ## インストール
44
-
45
- ```bash
46
- dsh plugin --profile web add github:<owner>/dsh-session-guard
47
- ```
48
-
49
- インストール後 dsh web を再起動し、ページをリフレッシュ。
50
-
51
- ## 設定(設定 → プラグイン → session-guard)
52
-
53
- | スイッチ | デフォルト | 説明 |
54
- |---|---|---|
55
- | `enabled` | on | **ピーク自動一時停止**:ピーク時間帯に実行セッションを自動一時停止 |
56
- | `offPeakAutoResume` | on | **オフピーク自動再開**:オフピーク時に一時停止セッションを自動再開 |
57
- | `weekendMode` | on | **週末モード**:週末を認識→週末は自動一時停止しない |
58
- | `queueFallback` | on | カスタムセッションゲート利用不可時にロック待機キューにフォールバック(fail-open) |
59
- | `retryEnabled` | off | **自動リトライ(バックエンド)**:瞬時失敗自動再試行(デフォルトオフ、保守的) |
60
-
61
- 追加設定:
62
-
63
- - `timezone`(デフォルト Asia/Shanghai)——**週末判定**とバッジ表示に使用。**峰谷判定には影響しない**(峰谷は常に北京時間);
64
- - `peakWindows`(デフォルト 09:00–12:00 / 14:00–18:00)——北京時間(UTC+8)の峰谷ウィンドウ。DeepSeek 公式課金と一致;
65
- - `pauseMode`(`safe`/`force`)、`pauseReason`(`wait`/`stop`);
66
- - リトライパラメータ:`retryText`、`retryGraceMs`、`retryCooldownMs`、`retryBackoffFactor`、`retryBackoffMaxMs`、`retryMaxConsecutive`。
67
-
68
- ## 動作
69
-
70
- ### ピーク自動ゲート(グローバル)
71
-
72
- - **ピーク入り**(かつ非週末):全 running ルートセッションに `gate.stopNextTurn` を呼び出し——カスタムセッションゲートで真の一時停止(推論を中断せず、安全境界で一時停止)、`queueFallback` でロック待機キューにフォールバック;
73
- - **退峰 / 週末**:`gate.resume` **全**セッション(自動再開、手動不要)——`offPeakAutoResume` スイッチで制御;
74
- - **峰谷タイムゾーン**:常に北京時間(`Asia/Shanghai`)を使用。DeepSeek 公式課金基準に一致。`timezone` 設定の影響を受けません;
75
- - 状態機械:単一インスタンス `NORMAL ↔ PAUSED_PEAK`(`scheduler.js`)、単一 30s tick で駆動。
76
-
77
- ### タイムゾーン処理
78
-
79
- - **峰谷判定**:常に **北京時間(UTC+8)** を使用(`BILLING_TIMEZONE = 'Asia/Shanghai'`)。DeepSeek 公式課金基準。`timezone` 設定で変更不可(ハードコード);
80
- - **週末判定**:ユーザー設定の `timezone`(例:`Asia/Tokyo`、`Asia/Seoul`)を使用。「週末」はローカル概念のため;
81
- - `Intl.DateTimeFormat` でタイムゾーン投影。無効な IANA タイムゾーン名は `RangeError` で fail-open し `Asia/Shanghai` にフォールバック;
82
- - 峰谷ウィンドウは**左閉右開** `[start, end)`。深夜跨ぎウィンドウ(例:`22:00–06:00`)対応。
83
-
84
- ### status-badge(フロントエンド表示)
85
-
86
- コンポーザー入力エリア右側に**読み取り専用**のステータスバッジを表示:
87
-
88
- | 階層 | ラベル | CSS クラス | 意味 |
89
- |---|---|---|---|
90
- | `peak` | 高峰 | `sg-peak` | 平日ピーク時間帯、セッション自動一時停止中 |
91
- | `off-peak` | 谷時 | `sg-off` | オフピーク時間帯、セッション通常稼働 |
92
- | `weekend` | 週末 | `sg-weekend` | 週末(週末モード有効時)、峰谷無視 |
93
-
94
- - 15 秒ごとに `GET /session-guard/status` をポーリング;
95
- - fail-open:ルート到達不可・ネットワークエラー・`enabled` オフ時→バッジ非表示;
96
- - **input-traffic に依存しない**:session-guard クライアントコードが単独で描画。input-traffic は凍結ボタンのみ担当;
97
-
98
- ### input-traffic との連携
99
-
100
- - input-traffic の**凍結ボタン**は `sessionGuard.stopNextTurn`(RPC、セッション経由)経由でサーバーサイドに伝達;
101
- - input-traffic は**凍結強化のみ**(キュー凍結/解凍 + composer ブロック)、リトライは本プラグインのバックエンドが処理;
102
- - 両方「セッション分離」セマンティスを共有:input-traffic 凍結キューは sessionId で分離、session-guard RPC も sessionId で分離。
103
-
104
- ## ライセンス
105
-
106
- MIT — [LICENSE](LICENSE) 参照。
1
+ <p align="center">
2
+ <strong>ピーク自動セッションゲート:週末モード + ピーク自動一時停止 + 公式ソース二次判定 + セッション級凍結 + バックエンド自動リトライ</strong>
3
+ </p>
4
+ <p align="center">
5
+ <a href="README.en.md">English</a> · <a href="README.md">中文</a> · <strong>日本語</strong> · <a href="README.ko.md">한국어</a>
6
+ </p>
7
+ <p align="center">
8
+ <a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-263146?style=flat-square"></a>
9
+ <img src="https://camo.githubusercontent.com/2c11fb2e0e14bb9985c5acbe61123a7441c5ee63aa27fa6e04e2a707ebfd6022/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6473682d2d706c7567696e2d72656164792d3437384342463f6c6f676f3d646565707365656b266c6f676f436f6c6f723d7768697465" alt="dsh-plugin" style="max-width: 100%;">
10
+ <img alt="Public beta" src="https://img.shields.io/badge/status-public%20beta-7da1de?style=flat-square">
11
+ </p>
12
+
13
+ # dsh-session-guard
14
+
15
+ - [English README](./README.en.md)
16
+ - [中文 README](./README.md)
17
+ - [日本語 README](./README.ja.md)
18
+ - [한국어 README](./README.ko.md)
19
+ - [Installation guide](./INSTALL.md)
20
+ - [中文安装指南](./INSTALL.zh.md)
21
+ - [日本語インストールガイド](./INSTALL.ja.md)
22
+ - [한국어 설치 안내](./INSTALL.ko.md)
23
+ - [Changelog](./CHANGELOG.md)
24
+ - [日本語 changelog](./CHANGELOG.ja.md)
25
+ - [한국어 changelog](./CHANGELOG.ko.md)
26
+
27
+ > **互換性について:** v0.1.1 には日本語(`ja`)と韓国語(`ko`)の辞書が含まれていますが、現在の公式 DSH リリースは `LocaleRuntime` 経由で `zh` と `en` のみを提供しています。純正 DSH で `ja` または `ko` を選択すると `locale "<id>" is not registered` で失敗します。公式 DSH が对应的 locale ID を追加するまで利用できません。上級ユーザーは DSH フォークを保守して更新してください。
28
+
29
+ > ピーク課金時間帯に実行中のセッションを自動一時停止し、オフピーク/週末に自動再開。input-traffic の凍結ボタンと連携して**セッション級**ロックを実現。バックエンド**自動リトライ**は凍結/ゲート期間中は譲歩。カスタムセッションゲート(`agent.cancel keepInbox + goals.pause + session/event 安全境界 + followup 再開`)に基づき、dsh-task-control に依存しません。
30
+
31
+ `dsh plugin` コマンドで组装 + バンドルパッチで装配する cordis プラグイン。dsh ソース変更も PR も不要。
32
+
33
+ > 💡 **推奨理由**:DeepSeek は 2026-08-17 から**峰谷課金**を開始しました。ピーク時間帯の単価はオフピークの 2 倍。本プラグインはピーク時に実行セッションを自動一時停止、オフピーク時に自動再開し、長時間セッションの費用を最大 **50%** 削減。手動凍結(input-traffic ボタン経由)でセッションごとの精密制御が可能。
34
+
35
+ ## 機能
36
+
37
+ - **週末モード**:`Intl.DateTimeFormat` で週末を正しく識別(タイムゾーン正確)→ 週末は峰谷を無視して自由実行。
38
+ - **ピーク自動一時停止(グローバル)**:ピーク入場時(かつ非週末)、全 running ルートセッションを自動一時停止。退峰時自動再開——**グローバルスイッチ、手動不要**。
39
+ - **公式ソース二次判定(`providerGuard`)**:ピーク時は**リクエスト先が DeepSeek 公式ソースの場合のみ**遮断。ローカル/第三者 provider(例 `local-35b`)は通常どおり実行。判定順 = 明示 id リスト → `baseURL` エンドポイント → catalog 既定エンドポイント → 内蔵 id。
40
+ - **リクエスト級バックストップ + 延後キュー**:ピーク入場後に起動したセッション、途中で公式ソースへ切り替えたセッションは `agent/request` ガードが捕捉(既定 `hold`:リクエストを保留しエラーなし、退峰時に自動解放)。
41
+ - **セッション級凍結/再開**:`sessionGuard` 冗余ポート + `POST /session-guard/rpc`、input-traffic 凍結ボタンでセッションごと透伝。`/pause /resume /cancel` 手動コマンドも提供。
42
+ - **バックエンド自動リトライ(D9)**:turn/end 瞬時失敗(error/429/max-tokens)はアダプティブバックオフで自動再試行。永久失敗は停止。**凍結/ゲート期間中は譲歩**、セッションゲートを迂回しません。
43
+ - **fail-open**:カスタムセッションゲート利用不可、session-guard 未インストール、設定サービス欠如——すべて静的降格、依存でクラッシュしません。
44
+
45
+ ## インストール
46
+
47
+ ```bash
48
+ dsh plugin --profile web add github:<owner>/dsh-session-guard
49
+ ```
50
+
51
+ インストール後 dsh web を再起動し、ページをリフレッシュ。
52
+
53
+ ## 設定(設定 → プラグイン → session-guard)
54
+
55
+ | スイッチ | デフォルト | 説明 |
56
+ |---|---|---|
57
+ | `enabled` | on | **ピーク自動一時停止**:ピーク時間帯に実行セッションを自動一時停止 |
58
+ | `stepLevelPause` | on | **step 級ゲート**:ピーク時、次の step のモデルリクエスト**前**にゲートを閉じる(ターン級より早く・より節約)。オフでターン級一時停止にフォールバック |
59
+ | `providerGuard` | on | **公式ソース二次判定**:ピーク時は DeepSeek 公式ソースのみ遮断、ローカル/第三者 provider は通常実行 |
60
+ | `guardSubagents` | on | **サブエージェントも対象**:サブエージェントのリクエストも課金対象、既定で遮断 |
61
+ | `offPeakAutoResume` | on | **オフピーク自動再開**:オフピーク時に一時停止セッションを自動再開 |
62
+ | `weekendMode` | on | **週末モード**:週末を認識→週末は自動一時停止しない |
63
+ | `deferredResume` | on | **ピーク後の自動再開**:オフにすると延後したリクエスト/セッションは手動 `/resume` まで停止 |
64
+ | `queueFallback` | on | カスタムセッションゲート利用不可時にロック待機キューにフォールバック(fail-open) |
65
+ | `retryEnabled` | off | **自動リトライ(バックエンド)**:瞬時失敗自動再試行(デフォルトオフ、保守的) |
66
+
67
+ 追加設定:
68
+
69
+ - `timezone`(デフォルト Asia/Shanghai)——**週末判定**とバッジ表示に使用。**峰谷判定には影響しない**(峰谷は常に北京時間);
70
+ - `peakWindows`(デフォルト 09:00–12:00 / 14:00–18:00)——北京時間(UTC+8)の峰谷ウィンドウ。DeepSeek 公式課金と一致;
71
+ - `pauseMode`(`safe`/`force`)、`pauseReason`(`wait`/`stop`);
72
+ - `stepGateTimeoutMs`(既定 300000)——step ゲートの保留タイムアウト。期限切れでゲートを解放し**ターン級一時停止へ昇格**(デッドロック回避、「5 分ごとに 1 step」のトークン滴漏も回避);
73
+ - 公式ソース判定:`officialProviders`(公式 provider id を追加、カンマ区切り、最優先)、`officialBaseURLs`(公式エンドポイント host、既定 `api.deepseek.com`);
74
+ - 延後キュー:`deferredMode`(`hold` / `error`)、`deferredResumeText`、`deferredMaxHoldMs`(保留上限、既定 6h、超過で error);
75
+ - リトライパラメータ:`retryText`、`retryGraceMs`、`retryCooldownMs`、`retryBackoffFactor`、`retryBackoffMaxMs`、`retryMaxConsecutive`。
76
+
77
+ ## 動作
78
+
79
+ ### ピーク自動ゲート(グローバル)
80
+
81
+ - **ピーク入り**(かつ非週末):`stepLevelPause` が有効なら**ターンを即中断しない**——セッションは次の `agent/pre-step` 境界まで走り、そこで step ゲートが閉じます(下記)。無効なら全 running ルートセッションに `gate.stopNextTurn`(カスタムセッションゲート、`queueFallback` でロック待機キューにフォールバック);
82
+ - **退峰 / 週末**:まず保留中の step を `releaseAll` で解放(ターンはその場で継続)、次に `gate.resume` **全**セッション(`offPeakAutoResume` で制御);
83
+ - **峰谷タイムゾーン**:常に北京時間(`Asia/Shanghai`)を使用。DeepSeek 公式課金基準に一致。`timezone` 設定の影響を受けません;
84
+ - 状態機械:単一インスタンス `NORMAL ↔ PAUSED_PEAK`(`scheduler.js`)、単一 30s tick で駆動。
85
+
86
+ ### step 級ゲート(v0.2.0、節約の要)
87
+
88
+ `agent/pre-step` waterfall に接続し、**次の step のモデルリクエストが発生する前**にターンを保留します。
89
+
90
+ - **ゲート条件**(すべて満たす):`enabled` + `stepLevelPause` + `step > 1` + ピーク(北京時間、非週末)+ 対象 provider が公式(`providerGuard`、オフなら全部)+ リクエスト級 hold されていない + このピーク期間で手動スキップされていない;
91
+ - **`step > 1` の理由**:ターン最初の step はリクエスト級ガードが担当するため、二重ゲートにならない;
92
+ - **解放経路**:①「⏸ 一時停止中(再開)」ボタン / `POST /session-guard/rpc {action:'stepResume'}` / `/resume` → 現在の step を通し、**このピーク期間はもうゲートしない**;② 退峰 → 全解放、ターンはその場で継続(**followup 不要**);③ 凍結ボタン / `/pause` / `/cancel` → ゲート解放してターン級一時停止へ;④ `signal` abort → 解放;
93
+ - **タイムアウト昇格**:`stepGateTimeoutMs`(既定 5 分)超過でゲート解放 + **ターン級 force 一時停止へ昇格**、退峰で復帰(デッドロックなし・トークン滴漏なし);
94
+ - **状態**:`GET /session-guard/state?session=<id>` が `paused: { step, turn }` と `stepGate: { held, since, bypass }` を返す。サービス側 `state().paused` は**互換のため真偽値のまま**、step 状態は `pausedStep`;
95
+ - **永続化しない**:保留はプロセス内 Promise、再起動で消滅(幽霊状態を避ける)。
96
+
97
+ #### 「一時停止 / 再開」ボタン(session-guard が提供)
98
+
99
+ コンポーザー右側の「一時停止」ボタン(slot `conversation.input.right`、id `session-guard-pause`、order 20 — input-traffic の「❄ 凍結して追加」の左):
100
+
101
+ - 未一時停止 → 「一時停止」、**クリック可**:`stepPause` を呼び、**次の step のモデルリクエスト前**にセッションを停止(現在の step は中断しない。step 1 も対象で、峰谷 / provider の制限を受けない);
102
+ - 一時停止中 → 「再開」、`stepResume` を呼び現在の step を放行、このピーク期間はもうゲートしない;
103
+ - **イベント push**:`GET /session-guard/events?session=<id>`(SSE)が step ゲート状態の変化を**即時**通知——ピークで自動的に閉じたらボタンは即「再開」に変わります。10 秒ごとの `/session-guard/state` ポーリングはフォールバック(SSE 不通でも収束);
104
+ - 同じ行の input-traffic ボタンと見た目を揃えています(高さ 24px / 角丸 6px / 12px フォント / 同じ CSS トークン)。
105
+
106
+ ### 公式ソース判定(providerGuard)
107
+
108
+ ピーク時は無差別停止ではなく、まず「このリクエストが実際に向かうルートが DeepSeek 公式ソースか」を判定します。
109
+
110
+ | 優先 | 根拠 | `matchedBy` | 例 |
111
+ |---|---|---|---|
112
+ | 1 | `officialProviders` 明示 id リスト | `explicit` | 自前ゲートウェイを公式として宣言 |
113
+ | 2 | リアルタイム `baseURL` の host | `endpoint` | `deepseek-official` を中継へ変更 → **遮断しない** |
114
+ | 3 | catalog 内蔵の既定エンドポイント | `endpoint-default` | pi-ai の `deepseek` ルートは既定で公式 API → **遮断する** |
115
+ | 4 | 内蔵 id リスト(`deepseek-official`) | `route-id` | エンドポイントが読めない場合のフォールバック |
116
+ | 5 | その他 | `unknown` | 非公式、通過 |
117
+
118
+ - **エンドポイントが id より優先**:`deepseek-official` の `baseURL` を中継へ向けても誤遮断しません。逆に pi-ai 内蔵 `deepseek` ルートは漏れません。
119
+ - **エンドポイント取得元**:`ctx.get('llm').listConfigurableProviders()` → ディレクトリ項目 → `ctx.settings.get(settingsNs)` → `settingsPath` で `baseURL`(非機密フィールドのみ、`apiKeyEnv` の値は読みません)。毎リクエスト再計算・キャッシュなし → provider 設定の変更が即時反映。
120
+ - **非公式へ切替で自動復帰**:ピーク入場で停止したセッションをローカル/第三者 provider へ切り替えると(0.1.2+ の `model/selection` イベント)、自動的に再開します(`deferredResume` の制約下)。本プラグインが入峰時に停止したセッションだけが対象で、手動 `/pause` は上書きしません。0.1.1 にはこのイベントがないため「次回リクエストまたは手動 `/resume`」に降格。
121
+ - **取得できない場合**:`llm` サービス欠如、名前空間構造の変化、非文字列フィールド——すべて id / 内蔵エンドポイント判定へ降格し `matchedBy` を記録、**例外は投げません**。
122
+ - **誤判定の調査**:`GET /session-guard/provider?provider=<id>` が `{ official, matchedBy, endpoint }` を返します。
123
+
124
+ ### リクエスト級ガードと延後キュー
125
+
126
+ - **なぜリクエスト級か**:30s tick は `NORMAL → PAUSED_PEAK` 遷移時に `running` だったセッションのみ処理します。ピーク入場後に起動したセッションや途中で公式ソースへ切り替えたセッションは漏れます。`agent/request` waterfall は**毎リクエスト**通ります。
127
+ - **`next()` の戻り値で判定**:モデル選択ミドルウェアが waterfall 内で provider/model を上書きするため、`await next()` の後に判定します。
128
+ - **`hold` モード(既定)**:リクエストを保留——**送信もエラーもなし**、退峰の瞬間に自動解放(`msUntilOffPeak` の精密タイマー、30s tick はバックストップ);abort で通常どおり中断。
129
+ - **`error` モード**:識別可能な `PEAK_DEFERRED` エラーを投げ延後キューに記録、退峰時に `deferredResumeText` で再開(`deferredResume` オフなら自動再開しない)。
130
+ - **上限**:`deferredMaxHoldMs`(既定 6h)で、無期限の保留を error に変換。
131
+ - **相互排他**:保留中はセッションゲートの一時停止を**併用しません**(一時停止は安全境界を待ちますが、保留中のリクエストはそこへ到達できません)。ピーク入場時、既に保留中のセッションはスキップされます。
132
+ - **永続化なし**:延後キューはプロセス内 promise、再起動で消えます。
133
+
134
+ ### 境界(対象外)
135
+
136
+ - **provider の付け替えはしません**(遮断のみ)。
137
+ - **compaction は `agent/request` を通りません**:セッション停止中は発生しません。ピーク中に手動で圧縮すると公式ソースへ行く可能性があります(`ctx.llm.stream` 層は介入しません)。
138
+ - **0.1.1 には `model/selection` イベントがありません**:非公式ソースへ切り替えた後の自動復帰は「次回リクエストまたは手動 `/resume`」に降格(0.1.2+ では即時)。
139
+ - **npm 依存の追加なし**、資格情報の読み取りなし、`dsh-llm-retry` の 429 / トランスポート再試行は不変。
140
+
141
+ ### タイムゾーン処理
142
+
143
+ - **峰谷判定**:常に **北京時間(UTC+8)** を使用(`BILLING_TIMEZONE = 'Asia/Shanghai'`)。DeepSeek 公式課金基準。`timezone` 設定で変更不可(ハードコード);
144
+ - **週末判定**:ユーザー設定の `timezone`(例:`Asia/Tokyo`、`Asia/Seoul`)を使用。「週末」はローカル概念のため;
145
+ - `Intl.DateTimeFormat` でタイムゾーン投影。無効な IANA タイムゾーン名は `RangeError` で fail-open し `Asia/Shanghai` にフォールバック;
146
+ - 峰谷ウィンドウは**左閉右開** `[start, end)`。深夜跨ぎウィンドウ(例:`22:00–06:00`)対応。
147
+
148
+ ### status-badge(フロントエンド表示)
149
+
150
+ コンポーザー入力エリア右側に**読み取り専用**のステータスバッジを表示:
151
+
152
+ | 階層 | ラベル | CSS クラス | 意味 |
153
+ |---|---|---|---|
154
+ | `peak`(二次判定 on) | 高峰·拦官方 | `sg-peak` | ピーク時間帯、DeepSeek 公式ソースのみ遮断 |
155
+ | `peak`(二次判定 off) | 高峰·全部暂停 | `sg-peak` | ピーク時間帯、全セッション停止 |
156
+ | `off-peak` | 谷時 | `sg-off` | オフピーク時間帯、セッション通常稼働 |
157
+ | `weekend` | 週末 | `sg-weekend` | 週末(週末モード有効時)、峰谷無視 |
158
+
159
+ - 15 秒ごとに `GET /session-guard/status` をポーリング(`phase` / `providerGuard` / `held` / `deferred` / `stepHeld`);
160
+ - fail-open:ルート到達不可・ネットワークエラー・`enabled` オフ時→バッジ非表示;
161
+ - **input-traffic に依存しない**:session-guard クライアントコードが単独で描画。input-traffic は凍結ボタンのみ担当;
162
+
163
+ ### input-traffic との連携
164
+
165
+ - input-traffic の**「❄ 凍結して追加」ボタン**は `sessionGuard.stopNextTurn`(RPC、セッション経由)で伝達し、**まず step ゲートを解放**します(そうしないとターン級一時停止が永遠に来ない安全境界を待ち、相互待機になります);
166
+ ### input-traffic との役割分担:「止める」側と「並べる」側
167
+
168
+ **DSH のキュー意味論(2 本のキュー)**:`next-step` = 「次の step 境界を待つ入力」(次の `agent/pre-step` で**ツール結果と同じレベル**の step として同一 turn 内で消費)、`next-turn` = 「独立したターンを待つプロンプト」(現在のターン終了後に**新しい turn** として消費)。`Inbox.claim()` は**必ず `next-step` を先に全部取り**、新ターンを開く境界でのみ `next-turn` を **1 件**追加で取ります。
169
+
170
+ - **session-guard = 止める**:いつ進めるかだけを決め、**キューの内容・順序には触れません**。step ゲート(`agent/pre-step`、次の step のモデルリクエスト前)、ターン級一時停止(`agent.cancel({keepInbox:true})` + `goals.pause`、**キューは保持**)、リクエスト級 hold(`agent/request`)。
171
+ - **input-traffic = 並べる**:ユーザー入力がどのキューに、どの段階で入り、いつ消費されるかだけを決めます(三档:赤=interrupt は `cancel()` 後に `steer`、黄=`steer`(→ `next-step`)、緑=`next-turn` に待機)。凍結 = `queued`+`steering` 行を段階ごと退避 + composer ブロック + `sessionGuard.stopNextTurn`;再開 = ブロック解除 → `sessionGuard.resume` → 段階順に再投入。
172
+ - **接点の 2 つの不変条件**:① 凍結は本プラグインに**先に step ゲートを解放**させる(でないとターン級一時停止が永遠に来ない安全境界を待ち、相互待機になる);② step ゲート保留中は `preStep()` が waterfall 前に `inbox.claim()` 済みなので、新しい入力は取られた分の後ろに並ぶ(`keepInbox` はターン級のみ)。
173
+ - ボタン:本プラグインの「一時停止 / 再開」(order 20)と input-traffic の「❄ 凍結して追加 / 再開して追加」(order 30)は**並列表示・相互に置き換えなし**。
174
+
175
+ ## ライセンス
176
+
177
+ MIT — [LICENSE](LICENSE) 参照。