@cocorograph/hub-agent 0.6.27 → 0.6.28
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 +91 -20
- package/src/main.mjs +3 -0
package/package.json
CHANGED
|
@@ -209,6 +209,11 @@ class ClaudeStreamSession {
|
|
|
209
209
|
/** 改修3: 直近 browser へ通知した queue 署名 (件数 + id 列)。変化時のみ emit する。
|
|
210
210
|
* 空キューの署名 ("0:") で初期化し、空→空の冗長 emit を抑止する。 */
|
|
211
211
|
this._lastEmittedQueueSig = "0:"
|
|
212
|
+
/** ultracode (0.6.28): 常駐 query へ現在適用済みの ultracode 状態。ターン単位の
|
|
213
|
+
* ワンショット適用を applyFlagSettings で reconcile する際の差分判定に使う。
|
|
214
|
+
* query を (再)起動すると flag settings は既定に戻るため、_runResidentQuery 冒頭で
|
|
215
|
+
* false に戻す。 */
|
|
216
|
+
this._ultracodeCurrent = false
|
|
212
217
|
|
|
213
218
|
/** @type {Map<string, {resolve: (decision: object) => void}>} permission 応答待ち */
|
|
214
219
|
this._permissionResolvers = new Map()
|
|
@@ -445,11 +450,18 @@ class ClaudeStreamSession {
|
|
|
445
450
|
* 既存ターン実行中 (busy) は破棄せず pending キューへ退避し、現ターン完了時に drain する
|
|
446
451
|
* (改修3)。常駐 query 対象 (新規セッション) は InputQueue へ積む (改修2)。
|
|
447
452
|
*/
|
|
448
|
-
async sendMessage(message) {
|
|
453
|
+
async sendMessage(message, opts = {}) {
|
|
449
454
|
if (this._closed) return
|
|
450
455
|
const prompt = extractPromptText(message)
|
|
451
456
|
if (!prompt) return
|
|
452
457
|
|
|
458
|
+
// ultracode ワンショット (0.6.28): このターンのみ xhigh effort + 常時 dynamic-workflow
|
|
459
|
+
// オーケストレーションを有効化する。セッション既定としては持たず (トークン消費が
|
|
460
|
+
// 桁違いになるため)、ターン単位でトグルし、完了後は通常状態へ戻す。
|
|
461
|
+
// per-message では options.settings に乗せ、resident では applyFlagSettings で
|
|
462
|
+
// ターン前に ON / 次ターン前に OFF へ reconcile する (詳細は _reconcileResidentUltracode)。
|
|
463
|
+
const ultracode = opts.ultracode === true
|
|
464
|
+
|
|
453
465
|
// 改修2+4: 常駐query対象セッション。
|
|
454
466
|
if (this._residentEligible) {
|
|
455
467
|
if (!this._inputQueue) this._inputQueue = new InputQueue()
|
|
@@ -458,7 +470,7 @@ class ClaudeStreamSession {
|
|
|
458
470
|
// 次を push すると SDK streaming-input で割り込み扱いになり得るため (公式 interrupt 警告)。
|
|
459
471
|
// pending は queue_state で UI (送信待ちチップ) に出す (per-message と同じ体験)。
|
|
460
472
|
if (this._busy) {
|
|
461
|
-
this._enqueuePending(prompt)
|
|
473
|
+
this._enqueuePending(prompt, ultracode)
|
|
462
474
|
this.logger?.info(
|
|
463
475
|
{ stream_id: this.stream_id, queued: this._pendingMessages.length },
|
|
464
476
|
"resident busy, message queued",
|
|
@@ -467,14 +479,18 @@ class ClaudeStreamSession {
|
|
|
467
479
|
return
|
|
468
480
|
}
|
|
469
481
|
this._busy = true
|
|
470
|
-
this._inputQueue.push(toSDKUserMessage(prompt))
|
|
471
482
|
// 改修4 (A): 死亡ガード。常駐 query が未起動 or (エラー等で) 終了済み (_residentQuery=null)
|
|
472
483
|
// なら (再)起動する。_runResidentQuery は起動時 options.resume=this.sessionId で文脈を
|
|
473
484
|
// 復元するため、途中死からの復活でも過去コンテキストは失われない。
|
|
485
|
+
// ultracode のとき: query を先に起動 (空 InputQueue なので入力待ちでブロック) し、
|
|
486
|
+
// applyFlagSettings を await してから push することで、設定適用前にターンが
|
|
487
|
+
// 消費されるレースを防ぐ。
|
|
474
488
|
if (!this._residentQuery) {
|
|
475
489
|
this._residentStarted = true
|
|
476
490
|
this._startResidentQuery()
|
|
477
491
|
}
|
|
492
|
+
await this._reconcileResidentUltracode(ultracode)
|
|
493
|
+
this._inputQueue.push(toSDKUserMessage(prompt))
|
|
478
494
|
return
|
|
479
495
|
}
|
|
480
496
|
|
|
@@ -482,7 +498,7 @@ class ClaudeStreamSession {
|
|
|
482
498
|
// 改修3: busy 中の送信は破棄せず pending キューへ退避し、現ターン完了時に drain する
|
|
483
499
|
// (ターミナル流の「積む→待機→順次実行」)。常駐 query 化はしないので暴走リスクは増えない。
|
|
484
500
|
if (this._busy) {
|
|
485
|
-
this._enqueuePending(prompt)
|
|
501
|
+
this._enqueuePending(prompt, ultracode)
|
|
486
502
|
this.logger?.info(
|
|
487
503
|
{ stream_id: this.stream_id, queued: this._pendingMessages.length },
|
|
488
504
|
"claude busy, message queued",
|
|
@@ -490,12 +506,12 @@ class ClaudeStreamSession {
|
|
|
490
506
|
this._emitQueueState()
|
|
491
507
|
return
|
|
492
508
|
}
|
|
493
|
-
return this._runPerMessage(prompt)
|
|
509
|
+
return this._runPerMessage(prompt, { ultracode })
|
|
494
510
|
}
|
|
495
511
|
|
|
496
512
|
/** per-message 1 ターンを実行する (resume チェーン)。busy 中に届いた送信は sendMessage
|
|
497
513
|
* が pending キューへ退避し、本メソッドの finally で drain する。 */
|
|
498
|
-
async _runPerMessage(prompt) {
|
|
514
|
+
async _runPerMessage(prompt, opts = {}) {
|
|
499
515
|
this._busy = true
|
|
500
516
|
this._abortController = new AbortController()
|
|
501
517
|
let aborted = false
|
|
@@ -512,6 +528,16 @@ class ClaudeStreamSession {
|
|
|
512
528
|
if (this.maxTurns != null) options.maxTurns = this.maxTurns
|
|
513
529
|
// 思考オプション (effort / adaptive thinking / 旧 budget) はモデルに応じて切替。
|
|
514
530
|
this._applyThinkingOptions(options)
|
|
531
|
+
// ultracode ワンショット (0.6.28): per-message は 1 query = 1 ターンなので、
|
|
532
|
+
// このターンの options.settings (= --settings 相当 / flag settings 層) に乗せるだけで
|
|
533
|
+
// 自然に 1 ターン限定になる。次ターンは options を作り直すため通常状態に戻る。
|
|
534
|
+
if (opts.ultracode === true) {
|
|
535
|
+
options.settings = {
|
|
536
|
+
...(options.settings || {}),
|
|
537
|
+
ultracode: true,
|
|
538
|
+
enableWorkflows: true,
|
|
539
|
+
}
|
|
540
|
+
}
|
|
515
541
|
// 直前ターンまでの session_id があれば resume チェーン
|
|
516
542
|
if (this.sessionId) options.resume = this.sessionId
|
|
517
543
|
|
|
@@ -613,18 +639,21 @@ class ClaudeStreamSession {
|
|
|
613
639
|
}
|
|
614
640
|
const next = this._pendingMessages.shift()
|
|
615
641
|
this._emitQueueState([next.text])
|
|
616
|
-
this._runPerMessage(next.text).catch(
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
642
|
+
this._runPerMessage(next.text, { ultracode: next.ultracode === true }).catch(
|
|
643
|
+
(err) => {
|
|
644
|
+
this.logger?.error(
|
|
645
|
+
{ stream_id: this.stream_id, err: err?.message },
|
|
646
|
+
"drain runPerMessage threw",
|
|
647
|
+
)
|
|
648
|
+
},
|
|
649
|
+
)
|
|
622
650
|
}
|
|
623
651
|
|
|
624
|
-
/** キャンセル機能 (0.6.26): pending キューへ安定 id 付きで 1 件積む。
|
|
625
|
-
|
|
652
|
+
/** キャンセル機能 (0.6.26): pending キューへ安定 id 付きで 1 件積む。
|
|
653
|
+
* ultracode (0.6.28): ワンショット ultracode フラグもエントリに保持し、drain 時に伝播する。 */
|
|
654
|
+
_enqueuePending(prompt, ultracode = false) {
|
|
626
655
|
const id = `q${++this._queueSeq}`
|
|
627
|
-
this._pendingMessages.push({ id, text: prompt })
|
|
656
|
+
this._pendingMessages.push({ id, text: prompt, ultracode: ultracode === true })
|
|
628
657
|
return id
|
|
629
658
|
}
|
|
630
659
|
|
|
@@ -685,9 +714,38 @@ class ClaudeStreamSession {
|
|
|
685
714
|
})
|
|
686
715
|
}
|
|
687
716
|
|
|
717
|
+
/** ultracode (0.6.28): 常駐 query の ultracode 状態を目標値へ寄せる (差分時のみ
|
|
718
|
+
* applyFlagSettings を発行)。push の前に await して、設定適用前にターンが消費される
|
|
719
|
+
* レースを防ぐ。query 未起動 / applyFlagSettings 非対応 SDK では no-op。 */
|
|
720
|
+
async _reconcileResidentUltracode(desired) {
|
|
721
|
+
const want = desired === true
|
|
722
|
+
if (want === this._ultracodeCurrent) return
|
|
723
|
+
const q = this._residentQuery
|
|
724
|
+
if (!q || typeof q.applyFlagSettings !== "function") return
|
|
725
|
+
try {
|
|
726
|
+
await q.applyFlagSettings(
|
|
727
|
+
want
|
|
728
|
+
? { ultracode: true, enableWorkflows: true }
|
|
729
|
+
: { ultracode: false },
|
|
730
|
+
)
|
|
731
|
+
this._ultracodeCurrent = want
|
|
732
|
+
this.logger?.info(
|
|
733
|
+
{ stream_id: this.stream_id, ultracode: want },
|
|
734
|
+
"resident ultracode reconciled",
|
|
735
|
+
)
|
|
736
|
+
} catch (err) {
|
|
737
|
+
this.logger?.warn(
|
|
738
|
+
{ stream_id: this.stream_id, err: err?.message },
|
|
739
|
+
"applyFlagSettings ultracode failed",
|
|
740
|
+
)
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
688
744
|
/** 改修4 (A): ターン完了時に pending の先頭 1 件を InputQueue へ流す (ターンのシリアライズ)。
|
|
689
|
-
* queue_state を更新して送信待ちチップを drain させる (frontend がバブル昇格する)。
|
|
690
|
-
|
|
745
|
+
* queue_state を更新して送信待ちチップを drain させる (frontend がバブル昇格する)。
|
|
746
|
+
* ultracode (0.6.28): 次ターンの目標 ultracode 状態へ reconcile してから push する
|
|
747
|
+
* (await するため async 化。result ハンドラからは fire-and-forget で呼ばれる)。 */
|
|
748
|
+
async _drainResidentPending() {
|
|
691
749
|
if (this._closed) return
|
|
692
750
|
if (this._pendingMessages.length === 0) {
|
|
693
751
|
this._emitQueueState()
|
|
@@ -695,6 +753,7 @@ class ClaudeStreamSession {
|
|
|
695
753
|
}
|
|
696
754
|
const next = this._pendingMessages.shift()
|
|
697
755
|
this._busy = true
|
|
756
|
+
await this._reconcileResidentUltracode(next.ultracode === true)
|
|
698
757
|
this._inputQueue.push(toSDKUserMessage(next.text))
|
|
699
758
|
this._emitQueueState([next.text])
|
|
700
759
|
}
|
|
@@ -718,6 +777,10 @@ class ClaudeStreamSession {
|
|
|
718
777
|
if (this.permissionMode) options.permissionMode = this.permissionMode
|
|
719
778
|
if (this.maxTurns != null) options.maxTurns = this.maxTurns
|
|
720
779
|
this._applyThinkingOptions(options)
|
|
780
|
+
// ultracode (0.6.28): 新規 query は flag settings 既定 (ultracode=off) で始まる。
|
|
781
|
+
// 適用済み状態の追跡を false にリセットし、次ターンの reconcile が正しく差分判定できる
|
|
782
|
+
// ようにする (異常終了→resume 再起動時にも確実にリセット)。
|
|
783
|
+
this._ultracodeCurrent = false
|
|
721
784
|
// 改修4: 起動時に sessionId (= resumeSessionId) があれば resume チェーンで文脈を引き継ぐ。
|
|
722
785
|
// query 起動時点の値のみ有効 (起動後に確定/変化する session_id は同一 query 内で継続される)。
|
|
723
786
|
if (this.sessionId) options.resume = this.sessionId
|
|
@@ -748,7 +811,14 @@ class ClaudeStreamSession {
|
|
|
748
811
|
denyPending("turn ended")
|
|
749
812
|
this._busy = false
|
|
750
813
|
// 改修4 (A): シリアライズした pending があれば次の 1 件を InputQueue へ流す。
|
|
751
|
-
|
|
814
|
+
// ultracode (0.6.28): _drainResidentPending は applyFlagSettings を await するため
|
|
815
|
+
// async。result ハンドラ (for await ループ内) からは fire-and-forget で呼ぶ。
|
|
816
|
+
this._drainResidentPending().catch((err) =>
|
|
817
|
+
this.logger?.warn(
|
|
818
|
+
{ stream_id: this.stream_id, err: err?.message },
|
|
819
|
+
"drainResidentPending threw",
|
|
820
|
+
),
|
|
821
|
+
)
|
|
752
822
|
}
|
|
753
823
|
try {
|
|
754
824
|
this.onEvent?.(msg)
|
|
@@ -1030,14 +1100,15 @@ export class ClaudeStreamBridge extends EventEmitter {
|
|
|
1030
1100
|
}
|
|
1031
1101
|
|
|
1032
1102
|
/** browser → claude の user メッセージ。1 件 = 1 query (resume チェーン)。 */
|
|
1033
|
-
input({ stream_id, message }) {
|
|
1103
|
+
input({ stream_id, message, ultracode }) {
|
|
1034
1104
|
const s = this.sessions.get(stream_id)
|
|
1035
1105
|
if (!s) {
|
|
1036
1106
|
this.logger?.warn({ stream_id }, "claude.input but stream missing")
|
|
1037
1107
|
return false
|
|
1038
1108
|
}
|
|
1039
1109
|
// 非同期でターン実行 (完了は result イベント + onEvent 経由で browser に届く)
|
|
1040
|
-
|
|
1110
|
+
// ultracode (0.6.28): このメッセージのみ ultracode ワンショットを適用するフラグ。
|
|
1111
|
+
s.sendMessage(message, { ultracode: ultracode === true }).catch((err) => {
|
|
1041
1112
|
this.logger?.error(
|
|
1042
1113
|
{ stream_id, err: err?.message },
|
|
1043
1114
|
"claude sendMessage threw unexpectedly",
|
package/src/main.mjs
CHANGED
|
@@ -781,6 +781,9 @@ async function dispatch(msg, ctx) {
|
|
|
781
781
|
ctx.claudeBridge.input({
|
|
782
782
|
stream_id: msg.stream_id,
|
|
783
783
|
message: msg.message,
|
|
784
|
+
// ultracode (0.6.28): browser がこのメッセージ単位で送るワンショット指定。
|
|
785
|
+
// true のときだけそのターンを xhigh effort + dynamic-workflow で実行する。
|
|
786
|
+
ultracode: msg.ultracode === true,
|
|
784
787
|
})
|
|
785
788
|
return
|
|
786
789
|
case "claude.upload": {
|