@cocorograph/hub-agent 0.6.59 → 0.6.60
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/main.mjs +36 -0
- package/src/tmux.mjs +56 -0
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
removeWorktree as removeWorktreeDir,
|
|
70
70
|
resumeWithMessage,
|
|
71
71
|
setTmuxGlobalEnv,
|
|
72
|
+
setTuiModel,
|
|
72
73
|
} from "./tmux.mjs"
|
|
73
74
|
import { TuiPermissionBridge } from "./tui-permission-bridge.mjs"
|
|
74
75
|
import { TuiViewerRegistry } from "./tui-viewer-registry.mjs"
|
|
@@ -1383,6 +1384,41 @@ async function dispatch(msg, ctx) {
|
|
|
1383
1384
|
})()
|
|
1384
1385
|
return
|
|
1385
1386
|
}
|
|
1387
|
+
case "claude.tui.setModel": {
|
|
1388
|
+
// モデルバッジ選択 → 対話 claude TUI へ `/model <id>` を送ってモデルを切り替える。
|
|
1389
|
+
// 権限循環 (cyclePermission) と同設計: agent が実キーを送出 → 確認プロンプトを確定 →
|
|
1390
|
+
// 全ブラウザへ claude.tui.model を broadcast し、実際に動いているターミナルのモデルを
|
|
1391
|
+
// 正本として全端末に同期する。jsonl の message.model は次の assistant ターンまで更新
|
|
1392
|
+
// されないため、この即時 broadcast で切替直後のバッジズレを解消する。
|
|
1393
|
+
const cwd = typeof msg.cwd === "string" ? msg.cwd : ""
|
|
1394
|
+
const sessionName =
|
|
1395
|
+
typeof msg.session_name === "string" ? msg.session_name : ""
|
|
1396
|
+
if (!sessionName) return
|
|
1397
|
+
// model="" は「デフォルト」= `/model default`。frontend へはそのまま空で返し、
|
|
1398
|
+
// 解決後の実 id は次ターンの jsonl 由来 (message.model) に委ねる。
|
|
1399
|
+
const model = typeof msg.model === "string" ? msg.model : ""
|
|
1400
|
+
;(async () => {
|
|
1401
|
+
try {
|
|
1402
|
+
await setTuiModel(sessionName, model || "default", { logger })
|
|
1403
|
+
ctx.client.send({
|
|
1404
|
+
type: "claude.tui.model",
|
|
1405
|
+
cwd: cwd || undefined,
|
|
1406
|
+
session_name: sessionName,
|
|
1407
|
+
model,
|
|
1408
|
+
})
|
|
1409
|
+
logger.info(
|
|
1410
|
+
{ session: sessionName, cwd, model: model || "(default)" },
|
|
1411
|
+
"tui model switched → notified browser",
|
|
1412
|
+
)
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
logger.warn(
|
|
1415
|
+
{ err: err?.message, session: sessionName },
|
|
1416
|
+
"claude.tui.setModel failed",
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
})()
|
|
1420
|
+
return
|
|
1421
|
+
}
|
|
1386
1422
|
case "claude.tui.probePermission": {
|
|
1387
1423
|
// 読み取り専用の権限モード問い合わせ (cold-load seed)。キーは送らず、ペインを
|
|
1388
1424
|
// 読んで現在の実モードを claude.tui.permission として broadcast するだけ。
|
package/src/tmux.mjs
CHANGED
|
@@ -865,6 +865,62 @@ export async function cyclePermissionMode(name, opts = {}) {
|
|
|
865
865
|
}
|
|
866
866
|
}
|
|
867
867
|
|
|
868
|
+
/**
|
|
869
|
+
* 対話 claude TUI に `/model <id>` を送ってモデルを切り替える。
|
|
870
|
+
*
|
|
871
|
+
* cockpit のモデルバッジ選択 (claude.tui.setModel) の書込側。権限循環 (cyclePermissionMode)
|
|
872
|
+
* と同じく、frontend は raw pty.data ではなく claude.tui.setModel を送り、agent 側で本関数を
|
|
873
|
+
* 実行 → 全ブラウザへ claude.tui.model を broadcast して全端末を実モデルに揃える。
|
|
874
|
+
*
|
|
875
|
+
* `/model` はフルモデルID をそのまま受理する (起動時 `claude --model <id>` と同仕様)。引数
|
|
876
|
+
* `default` は起動時の既定へ戻す。会話に出力済みの履歴があるとモデル切替時に確認プロンプトが
|
|
877
|
+
* 出るため、Enter を送って本文確定 → 少し待って追い Enter で確認を畳む (素の入力欄では空 Enter
|
|
878
|
+
* となり無害)。copy-mode に入っているとキーが奪われるので先に抜ける。ベストエフォート。
|
|
879
|
+
*
|
|
880
|
+
* @param {string} name tmux セッション名
|
|
881
|
+
* @param {string} modelArg `/model` 引数 (フルモデルID または "default")
|
|
882
|
+
* @param {{logger?:object,tmuxBin?:string}} [opts]
|
|
883
|
+
* @returns {Promise<{ok:boolean, error?:string}>}
|
|
884
|
+
*/
|
|
885
|
+
export async function setTuiModel(name, modelArg, opts = {}) {
|
|
886
|
+
const bin = tmuxBin(opts)
|
|
887
|
+
const arg = String(modelArg || "default").replace(/[\r\n]+/g, " ").trim()
|
|
888
|
+
if (!arg) return { ok: false, error: "empty model arg" }
|
|
889
|
+
try {
|
|
890
|
+
// copy-mode 等に入っているとキーが奪われるので、入っている時だけ抜ける。
|
|
891
|
+
try {
|
|
892
|
+
const { stdout } = await execFileP(bin, [
|
|
893
|
+
"display-message",
|
|
894
|
+
"-p",
|
|
895
|
+
"-t",
|
|
896
|
+
`${name}:`,
|
|
897
|
+
"-F",
|
|
898
|
+
"#{pane_in_mode}",
|
|
899
|
+
])
|
|
900
|
+
if (stdout.trim() === "1") {
|
|
901
|
+
await execFileP(bin, ["send-keys", "-t", name, "-X", "cancel"])
|
|
902
|
+
}
|
|
903
|
+
} catch {
|
|
904
|
+
// pane_in_mode 取得失敗はベストエフォートで無視。
|
|
905
|
+
}
|
|
906
|
+
// `/model <id>` をリテラルで送る (-l でキー名解釈・スラッシュ補完の暴発を避ける)。
|
|
907
|
+
await execFileP(bin, ["send-keys", "-t", name, "-l", `/model ${arg}`])
|
|
908
|
+
await _delay(120)
|
|
909
|
+
// Enter で本文確定 (send-keys の離散イベントなので paste 吸収は起きにくい)。
|
|
910
|
+
await execFileP(bin, ["send-keys", "-t", name, "Enter"])
|
|
911
|
+
// prior output ありの場合に出るモデル切替の確認プロンプトを Enter で畳む。
|
|
912
|
+
await _delay(450)
|
|
913
|
+
await execFileP(bin, ["send-keys", "-t", name, "Enter"])
|
|
914
|
+
return { ok: true }
|
|
915
|
+
} catch (err) {
|
|
916
|
+
opts.logger?.warn(
|
|
917
|
+
{ session: name, model: arg, err: err?.message },
|
|
918
|
+
"setTuiModel failed",
|
|
919
|
+
)
|
|
920
|
+
return { ok: false, error: err?.message || String(err) }
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
868
924
|
/**
|
|
869
925
|
* 中断キャンセル後の入力欄復旧 (claude.tui.recoverInput / agent >= 0.6.57)。
|
|
870
926
|
*
|