@cocorograph/hub-agent 0.6.62 → 0.6.63

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.6.62",
3
+ "version": "0.6.63",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/src/main.mjs CHANGED
@@ -69,6 +69,7 @@ import {
69
69
  removeWorktree as removeWorktreeDir,
70
70
  resumeWithMessage,
71
71
  setTmuxGlobalEnv,
72
+ setTuiEffort,
72
73
  setTuiModel,
73
74
  } from "./tmux.mjs"
74
75
  import { TuiPermissionBridge } from "./tui-permission-bridge.mjs"
@@ -1419,6 +1420,39 @@ async function dispatch(msg, ctx) {
1419
1420
  })()
1420
1421
  return
1421
1422
  }
1423
+ case "claude.tui.setEffort": {
1424
+ // effort バッジ選択 → 対話 claude TUI へ `/effort <level>` を送って reasoning effort を
1425
+ // 切り替える。setModel と同設計: agent が実キーを送出 → 全ブラウザへ claude.tui.effort を
1426
+ // broadcast し、実際に動いているターミナルの effort を正本として全端末に同期する。
1427
+ // effort は jsonl に記録されないため、この即時 broadcast が唯一の表示根拠になる。
1428
+ const cwd = typeof msg.cwd === "string" ? msg.cwd : ""
1429
+ const sessionName =
1430
+ typeof msg.session_name === "string" ? msg.session_name : ""
1431
+ if (!sessionName) return
1432
+ // effort="" は「auto」= `/effort auto`。frontend へはそのまま空で返し、バッジは auto 表示。
1433
+ const effort = typeof msg.effort === "string" ? msg.effort : ""
1434
+ ;(async () => {
1435
+ try {
1436
+ await setTuiEffort(sessionName, effort || "auto", { logger })
1437
+ ctx.client.send({
1438
+ type: "claude.tui.effort",
1439
+ cwd: cwd || undefined,
1440
+ session_name: sessionName,
1441
+ effort,
1442
+ })
1443
+ logger.info(
1444
+ { session: sessionName, cwd, effort: effort || "(auto)" },
1445
+ "tui effort switched → notified browser",
1446
+ )
1447
+ } catch (err) {
1448
+ logger.warn(
1449
+ { err: err?.message, session: sessionName },
1450
+ "claude.tui.setEffort failed",
1451
+ )
1452
+ }
1453
+ })()
1454
+ return
1455
+ }
1422
1456
  case "claude.tui.probePermission": {
1423
1457
  // 読み取り専用の権限モード問い合わせ (cold-load seed)。キーは送らず、ペインを
1424
1458
  // 読んで現在の実モードを claude.tui.permission として broadcast するだけ。
package/src/tmux.mjs CHANGED
@@ -934,6 +934,59 @@ export async function setTuiModel(name, modelArg, opts = {}) {
934
934
  }
935
935
  }
936
936
 
937
+ /**
938
+ * 対話 claude TUI に `/effort <level>` を送って reasoning effort を切り替える。
939
+ *
940
+ * cockpit の effort バッジ選択 (claude.tui.setEffort) の書込側。モデル切替 (setTuiModel) と
941
+ * 同設計で、frontend は raw pty.data ではなく claude.tui.setEffort を送り、agent 側で本関数を
942
+ * 実行 → 全ブラウザへ claude.tui.effort を broadcast して全端末を実 effort に揃える。
943
+ *
944
+ * `/effort` は引数 (low/medium/high/xhigh/max/auto) を受理し即時反映する (確認プロンプトを
945
+ * 挟まない)。effort 非対応レベルを与えても CLI がモデルの対応上限へ自動フォールバックする。
946
+ * `auto` はモデル既定へリセット。copy-mode に入っているとキーが奪われるので先に抜ける。
947
+ * ベストエフォート。
948
+ *
949
+ * @param {string} name tmux セッション名
950
+ * @param {string} effortArg `/effort` 引数 (low/medium/high/xhigh/max または "auto")
951
+ * @param {{logger?:object,tmuxBin?:string}} [opts]
952
+ * @returns {Promise<{ok:boolean, error?:string}>}
953
+ */
954
+ export async function setTuiEffort(name, effortArg, opts = {}) {
955
+ const bin = tmuxBin(opts)
956
+ const arg = String(effortArg || "auto").replace(/[\r\n]+/g, " ").trim()
957
+ if (!arg) return { ok: false, error: "empty effort arg" }
958
+ try {
959
+ // copy-mode 等に入っているとキーが奪われるので、入っている時だけ抜ける。
960
+ try {
961
+ const { stdout } = await execFileP(bin, [
962
+ "display-message",
963
+ "-p",
964
+ "-t",
965
+ `${name}:`,
966
+ "-F",
967
+ "#{pane_in_mode}",
968
+ ])
969
+ if (stdout.trim() === "1") {
970
+ await execFileP(bin, ["send-keys", "-t", name, "-X", "cancel"])
971
+ }
972
+ } catch {
973
+ // pane_in_mode 取得失敗はベストエフォートで無視。
974
+ }
975
+ // `/effort <level>` をリテラルで送る (-l でキー名解釈・スラッシュ補完の暴発を避ける)。
976
+ await execFileP(bin, ["send-keys", "-t", name, "-l", `/effort ${arg}`])
977
+ await _delay(120)
978
+ // Enter で本文確定 (send-keys の離散イベントなので paste 吸収は起きにくい)。
979
+ await execFileP(bin, ["send-keys", "-t", name, "Enter"])
980
+ return { ok: true }
981
+ } catch (err) {
982
+ opts.logger?.warn(
983
+ { session: name, effort: arg, err: err?.message },
984
+ "setTuiEffort failed",
985
+ )
986
+ return { ok: false, error: err?.message || String(err) }
987
+ }
988
+ }
989
+
937
990
  /**
938
991
  * 中断キャンセル後の入力欄復旧 (claude.tui.recoverInput / agent >= 0.6.57)。
939
992
  *