tailii-host 0.10.1 → 0.11.0
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/dist/approval/hook.d.ts +2 -0
- package/dist/approval/hook.js +38 -3
- package/dist/approval/hook.js.map +1 -1
- package/dist/backend/herdr.d.ts +31 -23
- package/dist/backend/herdr.js +181 -50
- package/dist/backend/herdr.js.map +1 -1
- package/dist/backend/sessionBackend.d.ts +8 -1
- package/dist/backend/sessionBackend.js +3 -0
- package/dist/backend/sessionBackend.js.map +1 -1
- package/dist/backend/tmux.d.ts +387 -2
- package/dist/backend/tmux.js +936 -55
- package/dist/backend/tmux.js.map +1 -1
- package/dist/chat/subagentTranscript.js +11 -3
- package/dist/chat/subagentTranscript.js.map +1 -1
- package/dist/chat/transcriptTailer.d.ts +3 -1
- package/dist/chat/transcriptTailer.js +65 -4
- package/dist/chat/transcriptTailer.js.map +1 -1
- package/dist/commands/launch.js +4 -2
- package/dist/commands/launch.js.map +1 -1
- package/dist/engine/engine.js +1 -0
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/handlers/conversation.js +29 -2
- package/dist/engine/handlers/conversation.js.map +1 -1
- package/dist/hub/hubDaemon.js +18 -5
- package/dist/hub/hubDaemon.js.map +1 -1
- package/dist/hub/hubProtocol.d.ts +16 -0
- package/dist/hub/hubProtocol.js +26 -2
- package/dist/hub/hubProtocol.js.map +1 -1
- package/dist/hub/reaper.d.ts +31 -0
- package/dist/hub/reaper.js +122 -4
- package/dist/hub/reaper.js.map +1 -1
- package/dist/hub/sessionHub.d.ts +76 -0
- package/dist/hub/sessionHub.js +177 -12
- package/dist/hub/sessionHub.js.map +1 -1
- package/dist/protocol/decode.js +11 -0
- package/dist/protocol/decode.js.map +1 -1
- package/dist/protocol/messages.d.ts +30 -0
- package/dist/sessions/claudeLiveStatus.d.ts +62 -0
- package/dist/sessions/claudeLiveStatus.js +202 -0
- package/dist/sessions/claudeLiveStatus.js.map +1 -0
- package/dist/sessions/claudeSessionStore.js +13 -0
- package/dist/sessions/claudeSessionStore.js.map +1 -1
- package/dist/sessions/cronSchedule.d.ts +20 -0
- package/dist/sessions/cronSchedule.js +172 -0
- package/dist/sessions/cronSchedule.js.map +1 -0
- package/dist/sessions/heartbeat.d.ts +44 -3
- package/dist/sessions/heartbeat.js +44 -4
- package/dist/sessions/heartbeat.js.map +1 -1
- package/dist/sessions/sessionSearch.js +9 -0
- package/dist/sessions/sessionSearch.js.map +1 -1
- package/dist/shared/clientBuild.d.ts +1 -1
- package/dist/shared/clientBuild.js +1 -1
- package/dist/shared/crossSession.d.ts +10 -3
- package/dist/shared/crossSession.js +105 -1
- package/dist/shared/crossSession.js.map +1 -1
- package/dist/shared/localCommandModelCopy.d.ts +2 -0
- package/dist/shared/localCommandModelCopy.js +43 -0
- package/dist/shared/localCommandModelCopy.js.map +1 -0
- package/dist/shared/narration.d.ts +7 -0
- package/dist/shared/narration.js +119 -0
- package/dist/shared/narration.js.map +1 -0
- package/dist/shared/pastedContent.d.ts +7 -0
- package/dist/shared/pastedContent.js +113 -0
- package/dist/shared/pastedContent.js.map +1 -0
- package/dist/shared/systemNotice.js +4 -1
- package/dist/shared/systemNotice.js.map +1 -1
- package/package.json +3 -3
package/dist/backend/tmux.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface TmuxCommandResult {
|
|
|
7
7
|
stderr: string;
|
|
8
8
|
}
|
|
9
9
|
/** tmux コマンド実行の注入可能な抽象(テストはモックを注入する)。 */
|
|
10
|
-
export type TmuxCommandRunner = (args: string[]) => Promise<TmuxCommandResult>;
|
|
10
|
+
export type TmuxCommandRunner = (args: string[], input?: string) => Promise<TmuxCommandResult>;
|
|
11
11
|
/** tmux 実行ファイルの既定絶対パス(PATH 外のため絶対指定)。 */
|
|
12
12
|
export declare const DEFAULT_TMUX_PATH = "/opt/homebrew/bin/tmux";
|
|
13
13
|
/** tmux の pane_current_command がエージェント実行中に見えるか。空文字は判定不能なので安全側。 */
|
|
@@ -30,7 +30,7 @@ export declare function inputBoxIsShellMode(box: ClaudeInputBox | null): boolean
|
|
|
30
30
|
/**
|
|
31
31
|
* 画面から claude TUI の入力欄を取り出す(TESTABLE)。
|
|
32
32
|
*
|
|
33
|
-
*
|
|
33
|
+
* 入力欄は上下 2 本の水平罫線に挟まれた領域(枠の探し方は `findInputBoxFrame`)。`❯` 行だけを探す旧実装は
|
|
34
34
|
* シェルモード(プロンプトが `!` になる)で入力欄を見失い、注入検証が必ず失敗
|
|
35
35
|
* → 本文を 3 回重ね打ちして入力欄を壊し、送信失敗として throw していた
|
|
36
36
|
* (実障害 2026-08-03: `!` 始まりの送信が HerdrFailedError)。
|
|
@@ -98,7 +98,215 @@ export interface SendTextSubmitOptions {
|
|
|
98
98
|
* 本文を明示再送へ倒すと二重送信になり得るため(「配送済み扱い + 可観測化」を選ぶ)。
|
|
99
99
|
*/
|
|
100
100
|
onUnconfirmedSubmit?: () => void;
|
|
101
|
+
/**
|
|
102
|
+
* 選択ダイアログ(AskUserQuestion の設問 / ❯ メニュー)が開いていたら、Esc で閉じてから本文を送る
|
|
103
|
+
* (chat-cancel-choice)。利用者が「選択肢に答えずにメッセージを送る」と決めた送信にだけ付く。
|
|
104
|
+
* 付いていない送信は従来どおり 1 キーも打たずに拒否する(tmux)。
|
|
105
|
+
* 承認ダイアログと `/login` フローは対象外(どちらも専用の操作経路があり、Esc の意味が重い)。
|
|
106
|
+
*/
|
|
107
|
+
cancelChoiceDialog?: boolean;
|
|
108
|
+
/** 選択ダイアログを実際に Esc で閉じたときに 1 回だけ呼ばれる(hub が監査ログへ残す)。 */
|
|
109
|
+
onChoiceDialogCancelled?: () => void;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* CLI が 1 回に読み取る入力の上限(バイト)。macOS の pty は 1022(実測 2.1.284: tmux / herdr とも)。
|
|
113
|
+
* Linux の pty は 1 回で約 4095 バイトまで返しうる(未実測。広い側に倒す)。
|
|
114
|
+
*/
|
|
115
|
+
export declare const PASTE_READ_BYTES: number;
|
|
116
|
+
export declare function typedTextWouldBreak(text: string, readBytes?: number): boolean;
|
|
117
|
+
/** tmux で 1 回で打つと壊れる本文か(TESTABLE)。引数の上限を超える本文も貼り付けで渡す。 */
|
|
118
|
+
export declare function tmuxTypedTextWouldBreak(text: string): boolean;
|
|
119
|
+
/** 括弧付き貼り付けの開始・終了(端末の bracketed paste)。 */
|
|
120
|
+
export declare const BRACKETED_PASTE_START = "\u001B[200~";
|
|
121
|
+
export declare const BRACKETED_PASTE_END = "\u001B[201~";
|
|
122
|
+
/**
|
|
123
|
+
* 貼り付けとして渡せる本文か。ESC を含む本文は、途中の `ESC[201~` で貼り付けが終わり、残りが
|
|
124
|
+
* 打鍵として解釈されうるので渡さない(従来どおり打つ)。
|
|
125
|
+
*/
|
|
126
|
+
export declare function textIsPasteSafe(text: string): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* 入力反映検証に使う probe(本文の**末尾側**)。検証不能な本文(空など)は null。
|
|
129
|
+
*
|
|
130
|
+
* 末尾側なのは composer の表示特性のため: 入力が表示高を超えると composer は下へ
|
|
131
|
+
* スクロールし**先頭行が窓外へ消える**(実測 2.1.220: 10行ペーストで先頭4行が
|
|
132
|
+
* capture から消失)。カーソルは常に末尾にあるので、末尾側の probe だけが
|
|
133
|
+
* 「見えている範囲」との照合を保証できる(先頭24字の旧 probe は多行/長文で
|
|
134
|
+
* 構造的に偽陰性 → 再投入 → 本文二重化の温床だった)。
|
|
135
|
+
*
|
|
136
|
+
* 先頭 `!`(シェルモード)は claude TUI がモード記号として吸い上げ、入力欄本文には
|
|
137
|
+
* 残らない(`!ls -la` は `! ls -la` と描画される)。単一行本文では照合キーからも
|
|
138
|
+
* 落とさないと末尾24字に `!` が含まれるとき反映検証が失敗する。
|
|
139
|
+
*/
|
|
140
|
+
export declare function typedTextProbe(text: string): string | null;
|
|
141
|
+
/**
|
|
142
|
+
* 入力欄テキストに probe が反映されているか(折り返し非依存)。
|
|
143
|
+
* extractClaudeInputBox は表示行を trim + "\n" 連結で返すため、probe が入力欄の
|
|
144
|
+
* 行折り返しをまたぐと生の includes は絶対に一致しない(全角24字=48桁 > 内幅で必発)。
|
|
145
|
+
* この偽陰性が「反映済み本文の再投入 = 初回送信の本文二重化」の根因だった(実機5件)。
|
|
146
|
+
* 空白類(折り返しの改行・trim 痕・全角空白含む)と不可視文字を両辺から除去して照合する。
|
|
147
|
+
*
|
|
148
|
+
* 結合文字は両辺を NFC にそろえる。端末には合成済みの形で映るので(`e` + U+0301 → `é`、
|
|
149
|
+
* `か` + U+3099 → `が`)、分解形(macOS 由来の文字列に多い)のままの probe は一致しない
|
|
150
|
+
* (独立検証が実測: 結合文字を含む本文の反映検証が必ず失敗した)。
|
|
151
|
+
*/
|
|
152
|
+
export declare function inputBoxTextIncludesProbe(boxText: string, probe: string): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* 1 つの貼り付けに入れる本文の上限(TESTABLE)。CLI(実測 2.1.284)は、1 つの貼り付けが
|
|
155
|
+
* **800 字以下で改行 2 個以下**なら、畳まずに本文のまま入力欄へ入れる(801 字・改行 3 個から
|
|
156
|
+
* `[Pasted text #N +M lines]` に畳む。字数は UTF-16 の単位で数える)。境界より手前に取る。
|
|
157
|
+
*/
|
|
158
|
+
export declare const INLINE_PASTE_MAX_CHARS = 500;
|
|
159
|
+
export declare const INLINE_PASTE_MAX_NEWLINES = 1;
|
|
160
|
+
/**
|
|
161
|
+
* 1 つの貼り付けのバイト数の上限。括弧(12 バイト)を足しても、CLI の 1 回の読み取り(1022 バイト)と
|
|
162
|
+
* pty の入力の待ち行列に収まる大きさにする。まとめて書き込むと、CLI が読み進めるのを待たされる
|
|
163
|
+
* (実測 herdr: 8KB を 1 回で書くと、入り切るまで 4〜7 秒。1 つずつなら 0.3 秒)。
|
|
164
|
+
*/
|
|
165
|
+
export declare const INLINE_PASTE_MAX_BYTES = 900;
|
|
166
|
+
/**
|
|
167
|
+
* 貼り付ける前に、CLI が記録するときの形へそろえる(TESTABLE)。CLI(実測 2.1.284)は貼り付けの
|
|
168
|
+
* CR / CRLF / U+2028 を LF にし、制御文字(DEL・U+009B など)を落とす。先にそろえておくと、
|
|
169
|
+
* 入力欄に映る本文と照合できる(そろえないと、これらを含む本文は毎回「入らなかった」と判定される)。
|
|
170
|
+
* タブと LF は残す。ESC を含む本文は貼り付けない(`textIsPasteSafe`)。
|
|
171
|
+
*/
|
|
172
|
+
export declare function normalizeTextForPaste(text: string): string;
|
|
173
|
+
/**
|
|
174
|
+
* 本文を、畳まれずに入力欄へ入る大きさの貼り付けに分ける(long-text-paste, TESTABLE)。
|
|
175
|
+
* つなげると元の本文に戻る。
|
|
176
|
+
*
|
|
177
|
+
* - 1 つは `INLINE_PASTE_MAX_CHARS` 字・`INLINE_PASTE_MAX_BYTES` バイト・改行
|
|
178
|
+
* `INLINE_PASTE_MAX_NEWLINES` 個まで
|
|
179
|
+
* - 書記素の途中では分けない(結合文字・ZWJ でつないだ絵文字・サロゲートの対)
|
|
180
|
+
* - **拡張子の付いた語を、1 つの貼り付けの中に丸ごと入れない**。CLI は、貼り付けの中の実在する
|
|
181
|
+
* 画像のパスを `[Image #N]` の添付に置き換える(実測 2.1.284: 貼り付けの全体がパスのとき。
|
|
182
|
+
* パスが 2 つ以上並ぶときは、後ろに本文が続いていても置き換わり、語の順序まで変わる)。
|
|
183
|
+
* アプリは添付のパスを本文の前に空白区切りで並べる(`p1 p2 本文`)。拡張子の「.」の直後で
|
|
184
|
+
* 分けると、前半は拡張子が無く、後半はパスの形をしていない
|
|
185
|
+
*/
|
|
186
|
+
export declare function splitForInlinePaste(text: string): string[];
|
|
187
|
+
/** ボトムバーの位置に `paste again to expand` が出ているか(常駐 TUI 行は読み飛ばす, TESTABLE)。 */
|
|
188
|
+
export declare function screenShowsPasteHint(screen: string): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* 入力欄に映っている本文が、`text` の一部として順に現れるか(TESTABLE)。
|
|
191
|
+
* 置き換えの表示(`TRUNCATED_DISPLAY`)で区切られた各部分が、`text` の中にこの順で現れることを
|
|
192
|
+
* 確かめる。`anchoredAtEnd` なら、最後の部分は `text` の末尾と同文でなければならない。
|
|
193
|
+
* 一致した字数を返す(一致しなければ -1)。照合は空白類・不可視文字を落とした形で行う。
|
|
194
|
+
*/
|
|
195
|
+
export declare function matchInputBoxAgainstText(boxText: string, text: string, anchoredAtEnd: boolean): number;
|
|
196
|
+
/**
|
|
197
|
+
* 貼り付けた本文が入力欄に入ったか(TESTABLE)。**入力欄に映っている全体が、本文の末尾と同文**
|
|
198
|
+
* であることで確かめる(入力欄は長い本文の末尾側だけを映す)。pty は先入れ先出しなので、最後の
|
|
199
|
+
* 貼り付けが入っていれば、その前の貼り付けも CLI が読み終えている。
|
|
200
|
+
* 約 10000 字を超える本文は、中ほどが置き換えの表示になる(`matchInputBoxAgainstText`)。
|
|
201
|
+
*
|
|
202
|
+
* 末尾の数十字だけを探す照合にはしない。各行の終わりが同じ文言の本文(表・ログ)だと、途中まで
|
|
203
|
+
* 入った時点で一致してしまう。CLI の処理が止まっている最中にそうなると、続けて打つ Enter が
|
|
204
|
+
* 貼り付けの残りと一緒に読まれて改行として本文に入り、送信されないまま入力欄に残る(実測
|
|
205
|
+
* 2.1.284: 30 行の本文を 11 行まで入った時点で止めた)。
|
|
206
|
+
*/
|
|
207
|
+
export declare function pastedTextArrived(boxText: string, text: string): boolean;
|
|
208
|
+
/**
|
|
209
|
+
* 入力欄に映っているのが、貼り付けた本文の一部か(TESTABLE)。貼り付けが途中まで入った形
|
|
210
|
+
* (CLI がまだ読んでいる / 処理が止まっている)を、別の中身と見分ける。
|
|
211
|
+
*/
|
|
212
|
+
export declare function pastedTextPartlyArrived(boxText: string, text: string): boolean;
|
|
213
|
+
/** 本文を小分けの貼り付けで渡すための入出力(tmux / herdr が与える)。 */
|
|
214
|
+
export interface PastedTextIo {
|
|
215
|
+
/** 小分けにした本文を、1 つずつ括弧付き貼り付けとして、順に渡す。 */
|
|
216
|
+
paste: (pieces: string[]) => Promise<void>;
|
|
217
|
+
/** viewport を ANSI 付きで撮る(撮れなければ null)。 */
|
|
218
|
+
capture: () => Promise<string | null>;
|
|
219
|
+
/** C-u(kill-line)を `count` 回、続けて打つ。 */
|
|
220
|
+
sendKills: (count: number) => Promise<void>;
|
|
221
|
+
/** 入力欄に入ったのを確かめる間隔と上限(ms)。 */
|
|
222
|
+
pollMs: number;
|
|
223
|
+
timeoutMs: number;
|
|
224
|
+
/** C-u をまとめて打った後、画面を撮り直すまでの間隔(ms)。 */
|
|
225
|
+
clearDelayMs: number;
|
|
226
|
+
/** C-u をまとめて打った後、入力欄が変わるのを待つ上限(ms。既定 `TYPED_TEXT_CLEAR_SETTLE_MS`)。 */
|
|
227
|
+
clearSettleMs?: number;
|
|
228
|
+
/** 貼り付けが入るのを待つのを、遅くともこの時刻(epoch ms)で打ち切る。 */
|
|
229
|
+
verifyDeadlineMs?: number;
|
|
101
230
|
}
|
|
231
|
+
/** 貼り付けが入力欄に入るのを待つ上限と、確かめる間隔の既定(ms)。実測は 0.3 秒以内に入る。 */
|
|
232
|
+
export declare const PASTED_TEXT_TIMEOUT_MS = 3000;
|
|
233
|
+
/**
|
|
234
|
+
* tmux で待つ上限の既定(ms)。tmux は確かめられなくても送信確定へ進むので、長く待つ意味が薄い
|
|
235
|
+
* (画面が崩れて読めない間は、貼り付けで渡す送信のたびにこの時間がかかる)。入っている途中なら延ばす。
|
|
236
|
+
*/
|
|
237
|
+
export declare const PASTED_TEXT_TIMEOUT_TMUX_MS = 1500;
|
|
238
|
+
export declare const PASTED_TEXT_POLL_MS = 100;
|
|
239
|
+
/**
|
|
240
|
+
* C-u をまとめて打った後、入力欄が変わるのを待つ上限(ms)。長い本文は、1 回消すたびに入力欄の
|
|
241
|
+
* 描き直しがかかり、40 回ぶんの反映に 1 秒以上かかる(実測 herdr: 10000 字の本文。150ms おきに
|
|
242
|
+
* 3 回見て変わらなければ諦める作りでは、消している途中で諦め、先頭の 468 字が残った)。
|
|
243
|
+
*/
|
|
244
|
+
export declare const TYPED_TEXT_CLEAR_SETTLE_MS = 1500;
|
|
245
|
+
/**
|
|
246
|
+
* 入れかけの長い本文を入力欄から消す(TESTABLE)。空にできたら true。
|
|
247
|
+
*
|
|
248
|
+
* C-u(kill-line)は 1 回で 1 行ぶんしか消えない: 多行の本文は末尾行 → その改行の順に消え、
|
|
249
|
+
* N 行なら 2N-1 回かかる(実測 2.1.220)。回数を固定(`clearInputBox` は 15 回)にすると
|
|
250
|
+
* 9 行以上の本文は途中までしか消えない。40 回ずつまとめて打ち、**入力欄が空になるまで、
|
|
251
|
+
* 進んでいる限り続ける**(上限は本文の大きさから決める)。まとめて打った後は、入力欄が変わるのを
|
|
252
|
+
* `clearSettleMs` まで待つ。2 回続けて変わらなければ諦める(同じ行が並ぶ本文は、消えていても
|
|
253
|
+
* 映っている範囲が同じに見えるので、1 回では諦めない)。空の入力欄への C-u は何もしない。
|
|
254
|
+
* 入力欄のフレームと確かめられないとき(ダイアログ・判別不能)は 1 キーも打たない。
|
|
255
|
+
*/
|
|
256
|
+
export declare function clearTypedInput(text: string, io: Pick<PastedTextIo, "capture" | "sendKills" | "clearDelayMs" | "clearSettleMs">): Promise<boolean>;
|
|
257
|
+
/**
|
|
258
|
+
* 行数の分からない残存を消すときの上限(`clearTypedInput` の本文の代わりに渡す)。
|
|
259
|
+
* 400 行ぶん(C-u 約 800 回)まで。
|
|
260
|
+
*/
|
|
261
|
+
export declare const CLEAR_UNKNOWN_INPUT_BOUND: string;
|
|
262
|
+
/** 貼り付けの送出が途中で失敗し、入った分を入力欄から消した(= 非配達が確定)ときの拒否文言。 */
|
|
263
|
+
export declare const CHAT_BLOCKED_BY_UNTYPED_TEXT = "\u9577\u3044\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u5165\u529B\u6B04\u3078\u6B63\u3057\u304F\u5165\u529B\u3067\u304D\u306A\u304B\u3063\u305F\u305F\u3081\u3001\u9001\u4FE1\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u3082\u3046\u4E00\u5EA6\u9001\u3063\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
264
|
+
/**
|
|
265
|
+
* 注入を始めてから、貼り付けが入ったのを確かめ終えるまでに使ってよい時間(ms)。アプリは応答を
|
|
266
|
+
* 18 秒待つ。この後の送信確定(最大で tmux 3.4 秒 / herdr 5.2 秒)を引いた残りに収める。
|
|
267
|
+
*/
|
|
268
|
+
export declare const INJECT_VERIFY_BUDGET_MS = 12000;
|
|
269
|
+
/** 貼り付けた本文が入力欄に入ったのを、確かめられたか。 */
|
|
270
|
+
export type PasteOutcome = "arrived" | "unverified";
|
|
271
|
+
/**
|
|
272
|
+
* 本文を、小分けの括弧付き貼り付けで入力欄へ入れる(tmux / herdr 共通, long-text-paste)。
|
|
273
|
+
*
|
|
274
|
+
* ```
|
|
275
|
+
* 小分けにして順に貼り付ける(splitForInlinePaste)
|
|
276
|
+
* 100ms ごとに最大 3 秒、入力欄に入ったのを確かめる(pastedTextArrived)→ `arrived`
|
|
277
|
+
* (入っている途中で、進んでいる間は延ばす。最大 9 秒。注入を始めてから 12 秒まで)
|
|
278
|
+
* 確かめられなかった → `unverified`(1 キーも打たない。どう扱うかは backend が決める)
|
|
279
|
+
* 貼り付けの送出が途中で失敗した:
|
|
280
|
+
* 入った分が入力欄に映っている → 消す(clearTypedInput)。空にできたら
|
|
281
|
+
* ChatInjectionRejectedError(非配達が確定)
|
|
282
|
+
* それ以外 → 送出のエラーをそのまま返す
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* 貼り付けは打鍵と違い、読み取りの境界・タブ・結合文字で壊れない。1 つが 800 字以下・改行 2 個
|
|
286
|
+
* 以下なら CLI は畳まずに本文のまま入力欄へ入れるので、**打った場合と同じ本文が記録される**
|
|
287
|
+
* (`<pasted_content>` の包みが付かない)。
|
|
288
|
+
*
|
|
289
|
+
* **確かめられなかったときの扱いは、本文を打つ場合(変更前からの経路)と同じにする**:
|
|
290
|
+
* - tmux: そのまま送信確定へ進む(Enter を押し、成立を確認できなければ `onUnconfirmedSubmit`)。
|
|
291
|
+
* 画面を読めないだけのことがある(実測: 肌色付きの ✌🏽 などを含む発話の後、tmux の画面が
|
|
292
|
+
* 崩れて入力欄が 1 行ずれる)。Enter は貼り付けの後ろに並ぶので、途中までの本文が送信される
|
|
293
|
+
* ことは無い
|
|
294
|
+
* - herdr: 失敗として返す(打つ場合の反映検証と同じ。Remote Control 切断直後は入力が捨てられ、
|
|
295
|
+
* 確かめずに進むと「送信成立」と誤判定する)
|
|
296
|
+
*
|
|
297
|
+
* 採らなかった方式:
|
|
298
|
+
* - **小分けにして打つ**: タブのカーソルずれ・処理の停止・結合文字で、順序の崩れた本文や先頭が
|
|
299
|
+
* 重複した本文が黙って届いた(独立検証が実 claude で再現)。
|
|
300
|
+
* - **全文を 1 つの貼り付けで渡す**: 入力欄は `[Pasted text #N +M lines]` になり、本文は包まれて
|
|
301
|
+
* 記録される。確かめる根拠が改行の数しか無く、同じ行数の別の貼り付け(Mac 側の下書き)と
|
|
302
|
+
* 見分けられない。包みは Claude に「利用者が書いたとは限らない内容」と伝えるためのもので、
|
|
303
|
+
* 利用者の指示の全体をその扱いにしてしまう。会話の一覧・タイトル・検索にも包みが出る。
|
|
304
|
+
* - **確かめられなかった本文を控えて、後から入った分を消す**(控え・確認用の印): CLI が止まった
|
|
305
|
+
* ときの「ちょうど 1 通」は守れるが、画面の読み取りに頼る判定が増え、それ自体が誤配送と
|
|
306
|
+
* 送信不能の源になった(独立検証 5 周: 印が発話として届く・下書きを削る・画面が崩れると
|
|
307
|
+
* 以後の送信がすべて拒否される)。
|
|
308
|
+
*/
|
|
309
|
+
export declare function pasteTextInline(text: string, io: PastedTextIo): Promise<PasteOutcome>;
|
|
102
310
|
/**
|
|
103
311
|
* 入力欄の残存テキストが、transcript に記録済みの発話本文と同文か(restored-prompt-discard)。
|
|
104
312
|
*
|
|
@@ -247,6 +455,153 @@ export declare class TmuxFailedError extends Error {
|
|
|
247
455
|
* (`screenHasSelectionFooter` と同じ硬化)。
|
|
248
456
|
*/
|
|
249
457
|
export declare function screenShowsDialogFooter(screen: string): boolean;
|
|
458
|
+
/**
|
|
459
|
+
* **Esc で閉じてよい**選択ダイアログ(AskUserQuestion の設問 / ❯ メニュー)が画面下部に出ているか
|
|
460
|
+
* (chat-cancel-choice, TESTABLE)。実測 2.1.283 のフッター:
|
|
461
|
+
* - 設問(1 問): `Enter to select · ↑/↓ to navigate · Esc to cancel`
|
|
462
|
+
* - 設問(複数問): `Enter to select · Tab/Arrow keys to navigate · Esc to cancel`
|
|
463
|
+
* - 自由記述欄にフォーカス中: 上に `· ctrl+g to edit in VS Code` が挟まる(80 桁では折り返す)
|
|
464
|
+
*
|
|
465
|
+
* `screenShowsDialogFooter` より狭い: ツール承認(`Esc to cancel · Tab to amend`)と `/login` の
|
|
466
|
+
* 継続待ちは含めない。承認の Esc は「ツールを拒否してターンを止める」で、選択肢を閉じるのとは
|
|
467
|
+
* 重さが違う(承認はアプリの承認モーダルが正規の経路)。
|
|
468
|
+
* 折り返しで `Esc to cancel` が次の行へ落ちるので、同じ行に両方あることは要求しない。
|
|
469
|
+
* Esc は処理中の Claude を中断するキーでもあるため、**ボトムバーが見えているフレームでは
|
|
470
|
+
* 決して true にしない**(本文の引用を末尾窓で拾っても、composer を見ているなら撃たない)。
|
|
471
|
+
*/
|
|
472
|
+
export declare function screenShowsCancellableChoice(screen: string): boolean;
|
|
473
|
+
/**
|
|
474
|
+
* ツール承認ダイアログ(フッター `Esc to cancel · Tab to amend`)が画面下部に出ているか(TESTABLE)。
|
|
475
|
+
* 通常の承認は PreToolUse hook がアプリの承認モーダルで捌き TUI には出ないが、hook の allow を
|
|
476
|
+
* 上書きする Claude Code 自身の安全確認(作業フォルダ外への Artifact 保存・`.claude/` への書き込み等)
|
|
477
|
+
* は TUI に残り、利用者が答えるまで会話が止まる。pane_preview の「静止した入力待ち」として初回
|
|
478
|
+
* フレームから送り、iPhone の転写カードで答えられるようにする(開き直しでもカードが出る)。
|
|
479
|
+
* フッターはカーソルが 1 番目のとき `Esc to cancel · Tab to amend`、2 番目以降へ動かすと
|
|
480
|
+
* `Esc to cancel` 単独になる(実測 2.1.283)ので、行頭の `Esc to cancel` で拾う(`/login` の各画面も
|
|
481
|
+
* 同じフッターだが、そちらも静止した入力待ちとして初回から送る対象なので区別しない)。
|
|
482
|
+
* 判定は `screenShowsCancellableChoice` と同じ硬化(末尾数行・行頭一致・ボトムバーが見えたら偽)。
|
|
483
|
+
*/
|
|
484
|
+
export declare function screenShowsApprovalDialog(screen: string): boolean;
|
|
485
|
+
/**
|
|
486
|
+
* 選択ダイアログを Esc で閉じる試行の結果。
|
|
487
|
+
* - `absent`: 閉じるべき選択ダイアログが見えない(1 キーも打っていない)。
|
|
488
|
+
* - `cancelled`: Esc を 1 回打ち、ダイアログが消えたことを確認した。
|
|
489
|
+
* - `stuck`: Esc を打ったが、待ってもダイアログが消えない(または画面を読めない)。
|
|
490
|
+
*/
|
|
491
|
+
export type ChoiceDialogCancelOutcome = "absent" | "cancelled" | "stuck";
|
|
492
|
+
/**
|
|
493
|
+
* 選択ダイアログを Esc で閉じ、消えたことを確認する(tmux / herdr 共通, chat-cancel-choice)。
|
|
494
|
+
*
|
|
495
|
+
* **Esc は 1 回しか打たない**。実測 2.1.283 では設問が複数でも、自由記述欄にフォーカスが
|
|
496
|
+
* あっても 1 回で閉じて 0.3 秒以内に空の入力欄へ戻る。閉じた後の入力欄へもう 1 回届くと
|
|
497
|
+
* 「Esc 2 回」= 巻き戻し(rewind)の画面が開き、続けて打つ本文がそこへ落ちる。
|
|
498
|
+
* 消えたかどうかは読めたフレームだけで判断する(capture 不能を「閉じた」と見なさない)。
|
|
499
|
+
*/
|
|
500
|
+
export declare function cancelChoiceDialog(io: {
|
|
501
|
+
/** 呼び出し側が直前に撮ったフレーム(事前確認の capture 回数を増やさない)。null = 撮れなかった。 */
|
|
502
|
+
screen: string | null;
|
|
503
|
+
capture: () => Promise<string | null>;
|
|
504
|
+
sendEscape: () => Promise<void>;
|
|
505
|
+
/** 既定は `screenShowsCancellableChoice`。herdr は従来の判定窓を渡す。 */
|
|
506
|
+
detect?: (screen: string) => boolean;
|
|
507
|
+
pollMs: number;
|
|
508
|
+
maxPolls: number;
|
|
509
|
+
}): Promise<ChoiceDialogCancelOutcome>;
|
|
510
|
+
/** Esc 後にダイアログの消滅を確かめる間隔と回数の既定(合計 約 2.4s)。 */
|
|
511
|
+
export declare const CHOICE_CANCEL_POLL_MS = 300;
|
|
512
|
+
export declare const CHOICE_CANCEL_MAX_POLLS = 8;
|
|
513
|
+
/**
|
|
514
|
+
* 設問を閉じて送る指定なのに、入力欄へ戻ったことを画面で確かめられなかったときの拒否文言。
|
|
515
|
+
* hub は「未回答の設問がある」と知っている。判別できない画面へ本文を打つと選択肢へ落ちる。
|
|
516
|
+
*/
|
|
517
|
+
export declare const CHAT_BLOCKED_BY_UNREADABLE_CHOICE = "\u8A2D\u554F\u306E\u753B\u9762\u3092\u78BA\u8A8D\u3067\u304D\u306A\u304B\u3063\u305F\u305F\u3081\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u5C11\u3057\u5F85\u3063\u3066\u9001\u308A\u76F4\u3059\u304B\u3001\u30A2\u30D7\u30EA\u3067\u8A2D\u554F\u306B\u7B54\u3048\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
518
|
+
/** 設問は閉じたが、その後の入力欄を画面で確かめられなかったときの拒否文言。 */
|
|
519
|
+
export declare const CHAT_BLOCKED_AFTER_CHOICE_CANCELLED = "\u8A2D\u554F\u306F\u9589\u3058\u307E\u3057\u305F\u304C\u3001\u5165\u529B\u6B04\u3092\u78BA\u8A8D\u3067\u304D\u306A\u304B\u3063\u305F\u305F\u3081\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u9001\u308A\u76F4\u3057\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
520
|
+
/**
|
|
521
|
+
* 選択ダイアログを閉じた直後のフレームを、入力欄が描画されるまで撮り直す(tmux / herdr 共通)。
|
|
522
|
+
* 閉じた直後は描画途中でボトムバーが欠け、`unknown` に見えることがある。ここで諦めると
|
|
523
|
+
* 「設問は閉じたのに本文は未送信」になるので、`unknown` の間だけ短く待つ。
|
|
524
|
+
*/
|
|
525
|
+
export declare function settleFrameAfterChoiceCancel(io: {
|
|
526
|
+
capture: () => Promise<string | null>;
|
|
527
|
+
pollMs: number;
|
|
528
|
+
maxPolls: number;
|
|
529
|
+
}): Promise<{
|
|
530
|
+
screen: string | null;
|
|
531
|
+
frame: SubmitFrameVerdict;
|
|
532
|
+
}>;
|
|
533
|
+
/** 選択ダイアログを閉じられなかったときの拒否文言(hub が会話本文へそのまま出す)。 */
|
|
534
|
+
export declare const CHAT_BLOCKED_BY_STUCK_CHOICE = "\u9078\u629E\u80A2\u3092\u9589\u3058\u3089\u308C\u306A\u304B\u3063\u305F\u305F\u3081\u3001\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u30A2\u30D7\u30EA\u3067\u9078\u629E\u80A2\u306B\u7B54\u3048\u308B\u304B\u3001Mac \u5074\u306E\u753B\u9762\u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
535
|
+
/**
|
|
536
|
+
* キュー済みの発話の直下に出る「今すぐ送信」ヒント行の状態(chat-send-now, TESTABLE)。
|
|
537
|
+
* - `absent`: ヒント行が無い = CLI のキューに利用者の発話は無い。
|
|
538
|
+
* - `ready`: 既定のキー割り当てのヒントが出ている。
|
|
539
|
+
* - `rebound`: ヒントは出ているが、キー割り当てが既定と違う(利用者が keybindings を変えた)。
|
|
540
|
+
*/
|
|
541
|
+
export type SendNowHintState = "absent" | "ready" | "rebound";
|
|
542
|
+
/**
|
|
543
|
+
* 画面に「今すぐ送信」のヒントが出ているか。実測 2.1.283 の形:
|
|
544
|
+
*
|
|
545
|
+
* ```
|
|
546
|
+
* ❯ キュー済みの発話(長ければ 2 桁字下げで折り返す)
|
|
547
|
+
* ctrl+x ctrl+s to send now
|
|
548
|
+
* ✽ Proofing… (9s · ↓ 183 tokens)
|
|
549
|
+
* ────────
|
|
550
|
+
* ❯ Press up to edit queued messages
|
|
551
|
+
* ```
|
|
552
|
+
*
|
|
553
|
+
* 会話本文がこの文言を引用しただけで拾わないよう、**ヒント行のすぐ上がキュー済みの発話**
|
|
554
|
+
* (`❯` で始まる行と、その折り返し)であることを要求する。応答本文は `⏺` で始まるので、
|
|
555
|
+
* 本文中の引用は上へ辿ると先に `⏺` に当たって外れる。
|
|
556
|
+
* 誤検出しても実害は無い(キューが空のときの chord は何もしない。実測)。
|
|
557
|
+
*/
|
|
558
|
+
export declare function screenSendNowHint(screen: string): SendNowHintState;
|
|
559
|
+
/** `sendQueuedNow` の結果(ワイヤーの `chat_send_now_result.status` と同じ 4 値)。 */
|
|
560
|
+
export type SendQueuedNowOutcome = {
|
|
561
|
+
status: "sent";
|
|
562
|
+
} | {
|
|
563
|
+
status: "nothing_queued";
|
|
564
|
+
} | {
|
|
565
|
+
status: "blocked";
|
|
566
|
+
reason: string;
|
|
567
|
+
} | {
|
|
568
|
+
status: "failed";
|
|
569
|
+
reason: string;
|
|
570
|
+
};
|
|
571
|
+
export declare const SEND_NOW_BLOCKED_BY_DIALOG = "\u30C0\u30A4\u30A2\u30ED\u30B0\u306E\u8868\u793A\u4E2D\u306F\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3067\u304D\u307E\u305B\u3093\uFF08\u30A2\u30D7\u30EA\u3067\u9078\u629E\u80A2\u306B\u7B54\u3048\u3066\u304B\u3089\u3082\u3046\u4E00\u5EA6\u8A66\u3057\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
572
|
+
export declare const SEND_NOW_BLOCKED_BY_DRAFT = "Mac \u5074\u306E\u5165\u529B\u6B04\u306B\u5165\u529B\u4E2D\u306E\u6587\u5B57\u304C\u3042\u308B\u305F\u3081\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u305D\u306E\u6587\u5B57\u3054\u3068\u9001\u4FE1\u3055\u308C\u308B\u306E\u3092\u9632\u3050\u305F\u3081\uFF09";
|
|
573
|
+
export declare const SEND_NOW_BLOCKED_BY_UNREADABLE = "\u753B\u9762\u3092\u78BA\u8A8D\u3067\u304D\u306A\u304B\u3063\u305F\u305F\u3081\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u5C11\u3057\u5F85\u3063\u3066\u3082\u3046\u4E00\u5EA6\u8A66\u3057\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
574
|
+
export declare const SEND_NOW_BLOCKED_BY_REBOUND = "Claude Code \u306E\u30AD\u30FC\u5272\u308A\u5F53\u3066\u304C\u5909\u66F4\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3067\u304D\u307E\u305B\u3093\uFF08chat:sendNow \u3092\u65E2\u5B9A\u306B\u623B\u3057\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
575
|
+
export declare const SEND_NOW_UNCONFIRMED = "\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3092\u9001\u308A\u307E\u3057\u305F\u304C\u3001\u5C4A\u3044\u305F\u3053\u3068\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F";
|
|
576
|
+
export declare const SEND_NOW_EXPIRED_IN_QUEUE = "\u5148\u306B\u9001\u3063\u305F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u51E6\u7406\u306B\u6642\u9593\u304C\u304B\u304B\u3063\u305F\u305F\u3081\u300C\u4ECA\u3059\u3050\u9001\u4FE1\u300D\u3057\u307E\u305B\u3093\u3067\u3057\u305F\uFF08\u3082\u3046\u4E00\u5EA6\u8A66\u3057\u3066\u304F\u3060\u3055\u3044\uFF09";
|
|
577
|
+
/**
|
|
578
|
+
* chord 後にヒント行の消滅を確かめる間隔と回数の既定(合計 約 6s)。短い発話は 0.7s 以内に消えるが、
|
|
579
|
+
* 貼り付け扱いの長文(約 800 字超)は 4.5s かかった(実測 2.1.283)。届いているのに `failed` を
|
|
580
|
+
* 返すと、利用者は届いた発話をもう一度流そうとする。
|
|
581
|
+
*/
|
|
582
|
+
export declare const SEND_NOW_POLL_MS = 300;
|
|
583
|
+
export declare const SEND_NOW_MAX_POLLS = 20;
|
|
584
|
+
/** 判別できないフレーム(描画の途中)を撮り直す回数。chord を送る前だけ(合計 約 1.5s)。 */
|
|
585
|
+
export declare const SEND_NOW_SETTLE_POLLS = 5;
|
|
586
|
+
/**
|
|
587
|
+
* CLI のキューに溜まっている発話を今すぐ届ける(tmux / herdr 共通, chat-send-now)。
|
|
588
|
+
* CLI の `chat:sendNow` を 1 回送り、ヒント行が消えたことを確かめる。
|
|
589
|
+
*
|
|
590
|
+
* 実測 2.1.283:
|
|
591
|
+
* - ツールの実行中: ツールが背景へ回り、発話は同じターンへ届く(`queued_command`)。
|
|
592
|
+
* - 応答の生成中 / `/` で始まる発話(添付のパス): そのターンを打ち切り、発話は新しいターンで届く。
|
|
593
|
+
* - キューが空 / アイドル: 何も起きない。
|
|
594
|
+
* - **入力欄に文字がある: その文字ごと送信される**。Mac 側の下書きを勝手に送らないよう、
|
|
595
|
+
* 入力欄が空と確かめられたフレームでだけ送る(fail-closed)。
|
|
596
|
+
*/
|
|
597
|
+
export declare function sendQueuedNow(io: {
|
|
598
|
+
capture: () => Promise<string | null>;
|
|
599
|
+
sendChord: () => Promise<void>;
|
|
600
|
+
pollMs: number;
|
|
601
|
+
maxPolls: number;
|
|
602
|
+
/** 判別できないフレームを撮り直す回数(既定 `SEND_NOW_SETTLE_POLLS`)。 */
|
|
603
|
+
settlePolls?: number;
|
|
604
|
+
}): Promise<SendQueuedNowOutcome>;
|
|
250
605
|
/**
|
|
251
606
|
* 画面最下部に composer のボトムバーが出ているか=「いま見ているのは入力欄であって
|
|
252
607
|
* ダイアログではない」か(TESTABLE。text / ANSI どちらのキャプチャでも使える)。
|
|
@@ -303,6 +658,16 @@ export declare class TmuxSessionManager {
|
|
|
303
658
|
private readonly submitDelayMs;
|
|
304
659
|
/** Enter → 送信成立確認 の間隔 ms(テスト注入用)。 */
|
|
305
660
|
private readonly submitVerifyDelayMs;
|
|
661
|
+
/** 選択ダイアログを Esc で閉じた後の消滅確認の間隔 ms(テスト注入用)。 */
|
|
662
|
+
private readonly choiceCancelPollMs;
|
|
663
|
+
/** 「今すぐ送信」の chord 後、ヒント行の消滅確認の間隔 ms(テスト注入用)。 */
|
|
664
|
+
private readonly sendNowPollMs;
|
|
665
|
+
/** 貼り付けた本文が入力欄に入るのを確かめる間隔と上限 ms(テスト注入用)。 */
|
|
666
|
+
private readonly pastedTextPollMs;
|
|
667
|
+
private readonly pastedTextTimeoutMs;
|
|
668
|
+
private readonly clearSettleMs;
|
|
669
|
+
/** 貼り付けバッファ名の通番。 */
|
|
670
|
+
private pasteSequence;
|
|
306
671
|
constructor(options?: {
|
|
307
672
|
runner?: TmuxCommandRunner;
|
|
308
673
|
store?: SessionMetadataStore;
|
|
@@ -316,6 +681,12 @@ export declare class TmuxSessionManager {
|
|
|
316
681
|
clearKeyDelayMs?: number;
|
|
317
682
|
submitDelayMs?: number;
|
|
318
683
|
submitVerifyDelayMs?: number;
|
|
684
|
+
choiceCancelPollMs?: number;
|
|
685
|
+
sendNowPollMs?: number;
|
|
686
|
+
pastedTextPollMs?: number;
|
|
687
|
+
pastedTextTimeoutMs?: number;
|
|
688
|
+
/** C-u をまとめて打った後、入力欄が変わるのを待つ上限(テスト用に短くできる)。 */
|
|
689
|
+
clearSettleMs?: number;
|
|
319
690
|
});
|
|
320
691
|
/**
|
|
321
692
|
* 現存する各セッションを name/cwd/alive で列挙する(name 昇順、メタのみは alive:false)。
|
|
@@ -340,6 +711,20 @@ export declare class TmuxSessionManager {
|
|
|
340
711
|
* literal 送出 → 150ms(Ink 再描画待ち)→ Enter。
|
|
341
712
|
*/
|
|
342
713
|
sendTextSubmit(name: string, text: string, options?: SendTextSubmitOptions): Promise<void>;
|
|
714
|
+
/**
|
|
715
|
+
* 小分けにした本文を、1 つずつ括弧付き貼り付けとして pane へ渡す(long-text-paste)。
|
|
716
|
+
* 本文は標準入力から tmux の貼り付けバッファへ載せる(`load-buffer -`)。引数で渡すと、tmux は
|
|
717
|
+
* 末尾の `;` をコマンドの区切りとして落とし、約 16KB を超えると `command too long` で失敗する
|
|
718
|
+
* (実測 tmux 3.7)。`paste-buffer -p` で括弧を付け、`-r` で改行を LF のまま渡す(`-r` が無いと
|
|
719
|
+
* LF を CR に置き換える)。`-d` で貼った後にバッファを消す。バッファ名は呼び出しごとに変える
|
|
720
|
+
* (並行する注入と取り違えない)。
|
|
721
|
+
*/
|
|
722
|
+
private pasteBracketed;
|
|
723
|
+
/**
|
|
724
|
+
* CLI のキューに溜まっている発話を今すぐ届ける(chat-send-now, SessionBackend 共通面)。
|
|
725
|
+
* `C-x C-s` は 1 回の send-keys で続けて送る(chord を分断しない)。
|
|
726
|
+
*/
|
|
727
|
+
sendQueuedNow(name: string): Promise<SendQueuedNowOutcome>;
|
|
343
728
|
/**
|
|
344
729
|
* 入力済みの本文を Enter で送信し、成立を確認するまで上限つきで Enter を撃ち直す
|
|
345
730
|
* (herdr 側 `sendTextSubmit` の送信確定ループと同型)。
|