@cocorograph/hub-agent 0.8.43 → 0.8.44
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/package.json +1 -1
- package/src/claude-stream-bridge.mjs +139 -12
- package/src/main.mjs +73 -21
- package/src/ws-client.mjs +5 -0
package/package.json
CHANGED
|
@@ -439,6 +439,7 @@ export class ClaudeStreamSession {
|
|
|
439
439
|
onPermissionCancel,
|
|
440
440
|
onExit,
|
|
441
441
|
onError,
|
|
442
|
+
onOptions,
|
|
442
443
|
onReap,
|
|
443
444
|
onTurnSettled,
|
|
444
445
|
}) {
|
|
@@ -481,6 +482,7 @@ export class ClaudeStreamSession {
|
|
|
481
482
|
this.onPermissionCancel = onPermissionCancel
|
|
482
483
|
this.onExit = onExit
|
|
483
484
|
this.onError = onError
|
|
485
|
+
this.onOptions = onOptions
|
|
484
486
|
/** ターン完走後に遅延クローズする際、manager にセッション撤去を依頼するコールバック */
|
|
485
487
|
this.onReap = onReap
|
|
486
488
|
/** RC-4: result を届けずにターンが終わった (abort/異常終了) ときに呼ぶコールバック。
|
|
@@ -551,6 +553,11 @@ export class ClaudeStreamSession {
|
|
|
551
553
|
/** この session オブジェクトの世代 (T06603)。上記 nextQueueGeneration の説明を参照。
|
|
552
554
|
* **同一プロセス内の順序のみ**を保証する (プロセス間は backend の接続フェンスが担う)。 */
|
|
553
555
|
this._queueGeneration = nextQueueGeneration()
|
|
556
|
+
/** model / permissionMode の「次ターン desired」authority identity。
|
|
557
|
+
* UUID は同一 session object の一致確認だけに使い、大小・生成順は比較しない。 */
|
|
558
|
+
this._optionsInstanceId = randomUUID()
|
|
559
|
+
/** 上記instance内だけで比較するCAS revision。queue seq/generationとは独立。 */
|
|
560
|
+
this._optionsRevision = 0
|
|
554
561
|
/** 配達確認の再表明用 (T06603): この session が受理 (キュー投入 or 直接配送) した
|
|
555
562
|
* input_id の直近リング。`claude.input.ack` は一度きりのユニキャストで、WS 切断が
|
|
556
563
|
* 30s を超えると ws-client の ctrl バッファから破棄され二度と再送されない。その結果
|
|
@@ -988,12 +995,9 @@ export class ClaudeStreamSession {
|
|
|
988
995
|
* browser 接続へライブに流れる (= 通常の生成中表示と同じ)。再アタッチ前の確定分は
|
|
989
996
|
* browser 側の jsonl hydrate (history.request) で復元するため、ここでは replay しない。
|
|
990
997
|
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
* 効くようになる。従来は reattach が stream_id だけ差し替えていたため、起動済み
|
|
995
|
-
* query の model/permission/thinking は初期値のまま変わらなかった (バッジが効かない
|
|
996
|
-
* 不具合の原因)。 */
|
|
998
|
+
* 通常 attach は購読 + current snapshot 取得専用。model / permissionMode は CAS request
|
|
999
|
+
* (`claude.session.options.set`) だけが変更できる。maxThinkingTokens / effort は今回の
|
|
1000
|
+
* authority 対象外なので、既存 attach 反映を維持する。 */
|
|
997
1001
|
reattach(stream_id, opts = undefined) {
|
|
998
1002
|
this.stream_id = stream_id
|
|
999
1003
|
// 多端末共有: この端末を購読集合に追加 (無効時は未使用)。旧端末の stream_id は
|
|
@@ -1005,7 +1009,12 @@ export class ClaudeStreamSession {
|
|
|
1005
1009
|
clearTimeout(this._idleTimer)
|
|
1006
1010
|
this._idleTimer = null
|
|
1007
1011
|
}
|
|
1008
|
-
if (opts)
|
|
1012
|
+
if (opts) {
|
|
1013
|
+
this.applyRuntimeOptions({
|
|
1014
|
+
maxThinkingTokens: opts.maxThinkingTokens,
|
|
1015
|
+
effort: opts.effort,
|
|
1016
|
+
})
|
|
1017
|
+
}
|
|
1009
1018
|
// キュー再表示バグ修正 (0.6.30): 再アタッチした端末は queue_state のライブ配信を
|
|
1010
1019
|
// 取りこぼしている (jsonl hydrate にはキュー状態が含まれない)。現在の pending を
|
|
1011
1020
|
// force で再 emit し、再表示端末・後から接続した端末の送信待ちチップを復元する。
|
|
@@ -1064,11 +1073,13 @@ export class ClaudeStreamSession {
|
|
|
1064
1073
|
* maxThinkingTokens に 0/null が来たら次 spawn 時に budget オフ (thinking 未指定) となる。 */
|
|
1065
1074
|
applyRuntimeOptions({ model, permissionMode, maxThinkingTokens, effort } = {}) {
|
|
1066
1075
|
const q = this._residentQuery
|
|
1076
|
+
let desiredOptionsChanged = false
|
|
1067
1077
|
// モデル
|
|
1068
1078
|
if (model !== undefined) {
|
|
1069
1079
|
const next = model || null
|
|
1070
1080
|
if (next !== this.model) {
|
|
1071
1081
|
this.model = next
|
|
1082
|
+
desiredOptionsChanged = true
|
|
1072
1083
|
if (q && typeof q.setModel === "function") {
|
|
1073
1084
|
q.setModel(next || undefined).catch((err) =>
|
|
1074
1085
|
this.logger?.warn(
|
|
@@ -1084,6 +1095,7 @@ export class ClaudeStreamSession {
|
|
|
1084
1095
|
const next = permissionMode || null
|
|
1085
1096
|
if (next !== this.permissionMode) {
|
|
1086
1097
|
this.permissionMode = next
|
|
1098
|
+
desiredOptionsChanged = true
|
|
1087
1099
|
// setPermissionMode は有効な mode を要求する。null/空 (=未指定へ戻す) は
|
|
1088
1100
|
// SDK 側に「解除」API が無いため、保持値の更新のみ (次回 spawn で既定に従う)。
|
|
1089
1101
|
if (next && q && typeof q.setPermissionMode === "function") {
|
|
@@ -1116,6 +1128,84 @@ export class ClaudeStreamSession {
|
|
|
1116
1128
|
if (effort !== undefined) {
|
|
1117
1129
|
this.effort = effort || null
|
|
1118
1130
|
}
|
|
1131
|
+
if (desiredOptionsChanged) {
|
|
1132
|
+
this._optionsRevision += 1
|
|
1133
|
+
try {
|
|
1134
|
+
this.onOptions?.(this.optionsSnapshot())
|
|
1135
|
+
} catch (err) {
|
|
1136
|
+
this.logger?.warn?.(
|
|
1137
|
+
{ err: err?.message, stream_id: this.stream_id },
|
|
1138
|
+
"onOptions callback threw",
|
|
1139
|
+
)
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/** Exact-base CAS で model / permissionMode の次ターン desired を変更する。 */
|
|
1145
|
+
setSessionOptions({ baseOptionsInstanceId, baseOptionsRevision, patch }) {
|
|
1146
|
+
const current = () => this.optionsSnapshot()
|
|
1147
|
+
const hasModel = patch && Object.hasOwn(patch, "model")
|
|
1148
|
+
const hasPermissionMode =
|
|
1149
|
+
patch && Object.hasOwn(patch, "permission_mode")
|
|
1150
|
+
if (
|
|
1151
|
+
!patch ||
|
|
1152
|
+
typeof patch !== "object" ||
|
|
1153
|
+
Array.isArray(patch) ||
|
|
1154
|
+
Object.keys(patch).length === 0 ||
|
|
1155
|
+
Object.keys(patch).some(
|
|
1156
|
+
(key) => key !== "model" && key !== "permission_mode",
|
|
1157
|
+
) ||
|
|
1158
|
+
!(hasModel || hasPermissionMode) ||
|
|
1159
|
+
(hasModel &&
|
|
1160
|
+
!(patch.model === null || typeof patch.model === "string")) ||
|
|
1161
|
+
(hasPermissionMode &&
|
|
1162
|
+
!(
|
|
1163
|
+
patch.permission_mode === null ||
|
|
1164
|
+
typeof patch.permission_mode === "string"
|
|
1165
|
+
))
|
|
1166
|
+
) {
|
|
1167
|
+
return { outcome: "invalid_patch", current: current() }
|
|
1168
|
+
}
|
|
1169
|
+
if (
|
|
1170
|
+
baseOptionsInstanceId !== this._optionsInstanceId ||
|
|
1171
|
+
!Number.isSafeInteger(baseOptionsRevision) ||
|
|
1172
|
+
baseOptionsRevision !== this._optionsRevision
|
|
1173
|
+
) {
|
|
1174
|
+
return { outcome: "conflict", current: current() }
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// attach/configと同じく空文字はdefault(null)。比較前に正規化してnull状態のnoopを保つ。
|
|
1178
|
+
const normalizedPatch = {
|
|
1179
|
+
...(hasModel ? { model: patch.model || null } : {}),
|
|
1180
|
+
...(hasPermissionMode
|
|
1181
|
+
? { permission_mode: patch.permission_mode || null }
|
|
1182
|
+
: {}),
|
|
1183
|
+
}
|
|
1184
|
+
const nextModel = hasModel ? normalizedPatch.model : this.model
|
|
1185
|
+
const nextPermissionMode =
|
|
1186
|
+
hasPermissionMode
|
|
1187
|
+
? normalizedPatch.permission_mode
|
|
1188
|
+
: this.permissionMode
|
|
1189
|
+
if (nextModel === this.model && nextPermissionMode === this.permissionMode) {
|
|
1190
|
+
return { outcome: "noop", current: current() }
|
|
1191
|
+
}
|
|
1192
|
+
this.applyRuntimeOptions({
|
|
1193
|
+
...(hasModel ? { model: normalizedPatch.model } : {}),
|
|
1194
|
+
...(hasPermissionMode
|
|
1195
|
+
? { permissionMode: normalizedPatch.permission_mode }
|
|
1196
|
+
: {}),
|
|
1197
|
+
})
|
|
1198
|
+
return { outcome: "applied", current: current() }
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/** 次ターンに使う desired options。SDK setter の成功実測値ではない。 */
|
|
1202
|
+
optionsSnapshot() {
|
|
1203
|
+
return {
|
|
1204
|
+
model: this.model,
|
|
1205
|
+
permission_mode: this.permissionMode,
|
|
1206
|
+
options_instance_id: this._optionsInstanceId,
|
|
1207
|
+
options_revision: this._optionsRevision,
|
|
1208
|
+
}
|
|
1119
1209
|
}
|
|
1120
1210
|
|
|
1121
1211
|
/** モデルが effort / adaptive thinking 対応かどうか。
|
|
@@ -2534,12 +2624,10 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2534
2624
|
// 多端末共有 (T06542 で常時): 旧 stream_id を Map から消さない。同一セッションを複数端末が
|
|
2535
2625
|
// 購読する状態を作り、どの端末の input/permission/interrupt も解決できるようにする
|
|
2536
2626
|
// (旧端末は reattach.streamIds に足され、backend が session group へ fan-out する)。
|
|
2537
|
-
//
|
|
2538
|
-
//
|
|
2539
|
-
//
|
|
2627
|
+
// 通常 attach は購読 + current snapshot 取得だけ。旧 frontend は capability flag を
|
|
2628
|
+
// 送らず model/permission を載せるが、新 agent は live session では fail-closed で無視する。
|
|
2629
|
+
// maxThinkingTokens / effort は model/permission authority の対象外なので既存反映を維持。
|
|
2540
2630
|
live.reattach(stream_id, {
|
|
2541
|
-
model,
|
|
2542
|
-
permissionMode,
|
|
2543
2631
|
maxThinkingTokens:
|
|
2544
2632
|
typeof maxThinkingTokens === "number" ? maxThinkingTokens : null,
|
|
2545
2633
|
effort,
|
|
@@ -2562,6 +2650,7 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2562
2650
|
resuming: true,
|
|
2563
2651
|
reattached: true,
|
|
2564
2652
|
turn_active: live._busy === true,
|
|
2653
|
+
...live.optionsSnapshot(),
|
|
2565
2654
|
}
|
|
2566
2655
|
}
|
|
2567
2656
|
}
|
|
@@ -2635,6 +2724,14 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2635
2724
|
error: err?.message || String(err),
|
|
2636
2725
|
})
|
|
2637
2726
|
},
|
|
2727
|
+
onOptions: (snapshot) => {
|
|
2728
|
+
// bridge 内部イベントは wire type を持たない。wire 契約は main builder に閉じる。
|
|
2729
|
+
this.emit("sessionoptions", {
|
|
2730
|
+
stream_id: session.stream_id,
|
|
2731
|
+
session_id: session.sessionId,
|
|
2732
|
+
...snapshot,
|
|
2733
|
+
})
|
|
2734
|
+
},
|
|
2638
2735
|
onReap: (info) => {
|
|
2639
2736
|
// ターン完走後の遅延クローズ、または R1(a) 子プロセス死亡時の即 evict。Map / 索引から
|
|
2640
2737
|
// 撤去し exit を emit する。多端末共有では複数端末キーが Map に載っているのでまとめて外す。
|
|
@@ -2680,9 +2777,39 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
2680
2777
|
stream_id,
|
|
2681
2778
|
resuming: !!resumeSessionId,
|
|
2682
2779
|
turn_active: session._busy === true,
|
|
2780
|
+
...session.optionsSnapshot(),
|
|
2683
2781
|
}
|
|
2684
2782
|
}
|
|
2685
2783
|
|
|
2784
|
+
/** browser の session options CAS request。常に current snapshot 付き outcome を返す。 */
|
|
2785
|
+
setSessionOptions({
|
|
2786
|
+
stream_id,
|
|
2787
|
+
session_id,
|
|
2788
|
+
base_options_instance_id,
|
|
2789
|
+
base_options_revision,
|
|
2790
|
+
patch,
|
|
2791
|
+
}) {
|
|
2792
|
+
const session = this.sessions.get(stream_id)
|
|
2793
|
+
if (!session) {
|
|
2794
|
+
return { outcome: "stream_missing", current: null }
|
|
2795
|
+
}
|
|
2796
|
+
// CAS操作もpermission reply/inputと同じユーザー活性。dead-terminal GCとwarm LRUを更新する。
|
|
2797
|
+
session.touch(stream_id)
|
|
2798
|
+
// fresh は SDK init 前だけ sessionId=null。同じ stream なら CAS 可能だが、session group の
|
|
2799
|
+
// identity がまだ無いため他端末はこの fresh session を購読できない。
|
|
2800
|
+
if (session.sessionId !== session_id) {
|
|
2801
|
+
return {
|
|
2802
|
+
outcome: "session_mismatch",
|
|
2803
|
+
current: session.optionsSnapshot(),
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
return session.setSessionOptions({
|
|
2807
|
+
baseOptionsInstanceId: base_options_instance_id,
|
|
2808
|
+
baseOptionsRevision: base_options_revision,
|
|
2809
|
+
patch,
|
|
2810
|
+
})
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2686
2813
|
/**
|
|
2687
2814
|
* browser → claude の user メッセージ。1 件 = 1 query (resume チェーン)。
|
|
2688
2815
|
*
|
package/src/main.mjs
CHANGED
|
@@ -978,6 +978,9 @@ export async function startDaemon({ version, claudeSdk } = {}) {
|
|
|
978
978
|
claudeBridge.on("exit", ({ stream_id, code, reason, session_id }) => {
|
|
979
979
|
client.send({ type: "claude.exit", stream_id, code, reason, session_id })
|
|
980
980
|
})
|
|
981
|
+
claudeBridge.on("sessionoptions", (message) => {
|
|
982
|
+
client.send(buildClaudeSessionOptionsPayload(message))
|
|
983
|
+
})
|
|
981
984
|
claudeBridge.on("turnsettled", ({ cwd }) => {
|
|
982
985
|
// RC-4: result を届けずにターンが終わった (abort/異常終了)。チャット信号の
|
|
983
986
|
// status=processing を waiting に落として三点リーダー/ステータスドットの固着を防ぐ
|
|
@@ -2052,6 +2055,58 @@ export function buildCodexChatPatch(status, contextPct, threadId, isAuthority) {
|
|
|
2052
2055
|
return patch
|
|
2053
2056
|
}
|
|
2054
2057
|
|
|
2058
|
+
/** 次ターンに使う desired options。現在ターンの setter 成功を観測した値ではない。 */
|
|
2059
|
+
function desiredOptionsWire(snapshot) {
|
|
2060
|
+
// stream_missing はcurrent不明。null(default)と偽装せずoptions fields自体を省く。
|
|
2061
|
+
if (!snapshot) return {}
|
|
2062
|
+
return {
|
|
2063
|
+
model: snapshot?.model ?? null,
|
|
2064
|
+
permission_mode: snapshot?.permission_mode ?? null,
|
|
2065
|
+
options_instance_id: snapshot?.options_instance_id,
|
|
2066
|
+
options_revision: snapshot?.options_revision,
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
/** claude.ready の wire payload。bridge attach info の options snapshot を透過する。 */
|
|
2071
|
+
export function buildClaudeReadyPayload(streamId, info) {
|
|
2072
|
+
return {
|
|
2073
|
+
type: "claude.ready",
|
|
2074
|
+
stream_id: streamId,
|
|
2075
|
+
resuming: info?.resuming === true,
|
|
2076
|
+
...desiredOptionsWire(info),
|
|
2077
|
+
turn_active: info?.turn_active === true,
|
|
2078
|
+
history_pushed: info?.resuming === true,
|
|
2079
|
+
supports_queue_input_id: true,
|
|
2080
|
+
supports_queue_snapshot: true,
|
|
2081
|
+
supports_session_options_cas: true,
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
/** bridge internal options event → session fanout wire。 */
|
|
2086
|
+
export function buildClaudeSessionOptionsPayload(message) {
|
|
2087
|
+
return {
|
|
2088
|
+
type: "claude.session.options",
|
|
2089
|
+
stream_id: message.stream_id,
|
|
2090
|
+
session_id: message.session_id ?? null,
|
|
2091
|
+
...desiredOptionsWire(message),
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
/** options CAS result は要求元 stream だけへ返す。 */
|
|
2096
|
+
export function buildClaudeSessionOptionsResultPayload(
|
|
2097
|
+
streamId,
|
|
2098
|
+
requestId,
|
|
2099
|
+
result,
|
|
2100
|
+
) {
|
|
2101
|
+
return {
|
|
2102
|
+
type: "claude.session.options.result",
|
|
2103
|
+
stream_id: streamId,
|
|
2104
|
+
request_id: requestId,
|
|
2105
|
+
outcome: result.outcome,
|
|
2106
|
+
...desiredOptionsWire(result.current),
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2055
2110
|
/** codex.ready の wire payload を組み立てる。history は同じ配列参照を保持し、
|
|
2056
2111
|
* started_history_index が指す位置を main 側の加工でずらさない。表示を保持する reconnect
|
|
2057
2112
|
* (`info.subagents` 未定義) では subagents キー自体を省略し、空 snapshot で既存表示を消さない。 */
|
|
@@ -3768,27 +3823,7 @@ async function dispatch(msg, ctx) {
|
|
|
3768
3823
|
effort: msg.effort || ctx.config?.claude_effort || null,
|
|
3769
3824
|
resumeSessionId,
|
|
3770
3825
|
})
|
|
3771
|
-
ctx.client.send(
|
|
3772
|
-
type: "claude.ready",
|
|
3773
|
-
stream_id,
|
|
3774
|
-
resuming: info.resuming,
|
|
3775
|
-
// 再接続時の生成状態は resident session を保持する hub-agent が正本。
|
|
3776
|
-
// jsonl 履歴末尾の推測だけでは「実際は busy なのに UI は idle」となり、
|
|
3777
|
-
// 送信ボタンが楽観バブルを作る一方 agent はキューへ積む二重表示が起きる。
|
|
3778
|
-
turn_active: info.turn_active === true,
|
|
3779
|
-
history_pushed: resumeSessionId !== null,
|
|
3780
|
-
// id 空間統一 (2026-07-08): この agent は browser の input_id をキュー id として
|
|
3781
|
-
// 採用し、楽観送信待ちアイテムの確認前キャンセルに対応することを通知する capability。
|
|
3782
|
-
// 旧 agent にはこのフラグが無く、browser は楽観アイテムの×を出さない (ゴースト
|
|
3783
|
-
// 実行防止のフォールバック)。
|
|
3784
|
-
supports_queue_input_id: true,
|
|
3785
|
-
// pull 型再同期 capability (T06603, 2026-07-25): この agent は
|
|
3786
|
-
// claude.queue.request に claude.queue.response で答え、queue_state に
|
|
3787
|
-
// 版番号 (seq) と受理済み input_id (accepted) を載せることを通知する。
|
|
3788
|
-
// 旧 agent はこのフラグが無く、browser は従来の「配信到達に依存する」
|
|
3789
|
-
// 挙動へフォールバックする (混在期間も安全に段階デプロイできる)。
|
|
3790
|
-
supports_queue_snapshot: true,
|
|
3791
|
-
})
|
|
3826
|
+
ctx.client.send(buildClaudeReadyPayload(stream_id, info))
|
|
3792
3827
|
if (resumeSessionId) {
|
|
3793
3828
|
void ctx.siblingInjectBridge
|
|
3794
3829
|
?.replayDelegations(cwdArg || process.env.HOME || process.cwd(), resumeSessionId)
|
|
@@ -3874,6 +3909,23 @@ async function dispatch(msg, ctx) {
|
|
|
3874
3909
|
}
|
|
3875
3910
|
return
|
|
3876
3911
|
}
|
|
3912
|
+
case "claude.session.options.set": {
|
|
3913
|
+
const result = ctx.claudeBridge?.setSessionOptions({
|
|
3914
|
+
stream_id: msg.stream_id,
|
|
3915
|
+
session_id: msg.session_id ?? null,
|
|
3916
|
+
base_options_instance_id: msg.base_options_instance_id,
|
|
3917
|
+
base_options_revision: msg.base_options_revision,
|
|
3918
|
+
patch: msg.patch,
|
|
3919
|
+
}) || { outcome: "stream_missing", current: null }
|
|
3920
|
+
ctx.client.send(
|
|
3921
|
+
buildClaudeSessionOptionsResultPayload(
|
|
3922
|
+
msg.stream_id,
|
|
3923
|
+
msg.request_id,
|
|
3924
|
+
result,
|
|
3925
|
+
),
|
|
3926
|
+
)
|
|
3927
|
+
return
|
|
3928
|
+
}
|
|
3877
3929
|
case "claude.input":
|
|
3878
3930
|
// message: { role: 'user', content: string | Array } を期待
|
|
3879
3931
|
handleClaudeChatInput(msg, ctx)
|
package/src/ws-client.mjs
CHANGED
|
@@ -56,6 +56,9 @@ const MIN_BACKOFF_AFTER_5XX_MS = 5_000
|
|
|
56
56
|
// 「送信待ち(未配信)」が消えない (S1/S3)。再接続 flush で遅れて届けば即解消する。
|
|
57
57
|
// - claude/codex の event / ready / exit / error / permission:
|
|
58
58
|
// 喪失すると新規セッションの楽観バブルが本物へ昇格できず、再 attach も固着する。
|
|
59
|
+
// - claude.session.options.result: options.set は再要求型RPCではなく明示intentの終端。
|
|
60
|
+
// agent→Hub切断中のapplied resultも保持し、request_idで同streamへ戻す。frontend側の
|
|
61
|
+
// stale-stream/request mismatch/10s timeoutが遅延結果を無害化する(LWW圧縮はしない)。
|
|
59
62
|
// - claude.jsonl.event / session.event: TUI と一覧の確定状態を復元する。
|
|
60
63
|
// 高頻度の delta 系はここに積まず (下の isEphemeral*DeltaEvent で除外)、低頻度な
|
|
61
64
|
// ack/event がリング evict されるのを防ぐ (ack/event は 1 件ずつ意味を持つ)。
|
|
@@ -63,6 +66,8 @@ const BUFFERED_CTRL_TYPES = new Set([
|
|
|
63
66
|
"claude.jsonl.event",
|
|
64
67
|
"claude.event",
|
|
65
68
|
"claude.ready",
|
|
69
|
+
"claude.session.options",
|
|
70
|
+
"claude.session.options.result",
|
|
66
71
|
"claude.exit",
|
|
67
72
|
"claude.error",
|
|
68
73
|
"claude.permission.request",
|