mytty-ctl リファレンス
mytty-ctl は、コーディングエージェント(Claude Code、Codex、Cursor など)が Mytty 自身を操作するためのローカル CLI です。agent サブコマンドは新しいペインに worker provider を起動し、タスクを1回のシェル入力として渡して、その起動を1つの job として追跡します。orchestrator はその job を待ち、結果を読み、追加指示を送れます。従来のペイン単位のコマンド(split、send、wait、read など)もそのまま使えるので、ペインを手動で操作したいときはこちらを使ってください。どちらを使うべきかは orchestration の how-toを参照してください。通信には MyTTYApp の ControlServer を使い、同一ユーザーに制限された Unix ドメインソケット経由で、1接続につき1つの JSON リクエストをやりとりします。iOS リモート(RemoteAccessServer、TCP + ペアリング + 暗号化)とは別系統のトランスポートです。出典は Sources/MyTTYCore/ControlProtocol.swift、Sources/MyTTYCore/ControlCommandLineParser.swift、Sources/MyTTYCore/AgentJob.swift、Sources/MyTTYCore/AgentLaunchPlan.swift、Sources/MyTTYApp/ControlServer.swift、Sources/MyTTYApp/ControlCoordinator.swift、Sources/MyTTYApp/AgentJobCoordinator.swift。
環境変数
Mytty が開くすべてのペインには、次の3変数が自動で設定されます(AgentEventServer.environment(for:))。同じ箇所で mytty-ctl の置き場所を PATH にも足しているので、ペイン内では mytty-ctl と名前で呼ぶだけで使え、事前設定は不要です。
| 変数 | 意味 |
|---|---|
MYTTY_CONTROL_SOCKET |
mytty-ctl が接続する Unix ソケットの絶対パス |
MYTTY_CTL_BIN |
mytty-ctl バイナリの絶対パス(名前で呼べるか怪しい場面向け) |
MYTTY_SURFACE_ID |
このペイン自身の pane ID。コマンド中で "自分自身" として使える |
mytty-ctl split "$MYTTY_SURFACE_ID" right --cwd /path/to/worktree
dev ビルド(Mytty Dev)と release ビルドは、それぞれ別の ~/.config/mytty(-dev) 配下にソケットを持ちます。mytty-ctl 自身はどちらと通信しているかを意識しません。どちらと通信するかは、どのペインの環境変数を継承したかで決まります。
Mytty の外から使う
上の PATH 追加は、あくまで Mytty が開いたペインの中だけの話です。Mytty の外向けの CLI インストールは Mytty 自身では面倒を見ませんが、自分で用意することはできます。Mytty のペイン内で echo $MYTTY_CTL_BIN を実行すると同梱バイナリの場所がわかるので、それを自分の PATH 上にシンボリックリンクしてください。例:
ln -s "$MYTTY_CTL_BIN" ~/.local/bin/mytty-ctl
ただしこれだけでは、普通のターミナルタブやスクリプトから mytty-ctl を呼べるようにはなりません。Mytty のペインの外には MYTTY_CONTROL_SOCKET が環境変数として存在しないため、mytty-ctl は接続先を持たないからです。こちらもペイン内で値を確認し(echo $MYTTY_CONTROL_SOCKET)、mytty-ctl を呼びたいシェルで export してください。
Codex のサンドボックス内から呼んだ場合
Codex が実行するシェルコマンドは macOS の Seatbelt サンドボックス(review でも workspace-write でも)の中で動くため、そこから呼んだ mytty-ctl は Unix ドメインソケットへの connect(2)自体をオペレーティングシステムに拒否され、socketOperation(1)(EPERM)であらゆるコマンド(list や agent spawn を含む)が失敗します。ソケットファイル・権限・MYTTY_CONTROL_SOCKET はどれも正常で、サンドボックスの外(他のペインや通常のシェル)からは同じソケットに問題なく繋がります。Codex を司令塔にして他のペインを操作させたい場合は、mytty-ctl コマンドをサンドボックス外で実行するよう承認を求めてください。agent spawn で別ペインに起動した worker 側はこの制約を受けません(worker 自身のシェルで動くため)。
終了ステータスと出力
すべてのコマンドは、成功時に JSON を1行だけ標準出力に書き出し、終了コード 0 で終わります。失敗時は標準エラーにメッセージを出し、終了コード 1 で終わります。
mytty-ctl list | jq .
コマンド一覧
「worker を1つ以上動かして結果を集める」形の作業には agent コマンドを使ってください。worker の起動、その起動が実際に始めた実行の待機、結果の読み取り、追加指示の送信をひとまとめに扱えます。ペイン単位のコマンドを組み合わせたときに呼び出し側が自分で避けなければならない競合も、心配する必要がありません(詳細は下の「job のバインディング」を参照)。ペインを手動で操作する用途には、引き続きペイン単位のコマンドを使ってください。
| コマンド | 引数 | 成功時のレスポンス |
|---|---|---|
agent spawn |
--provider <codex|claude|cursor> (--task <text>|--task-file <path>) [--anchor <pane-id>] [--direction <left|right|up|down>] [--cwd <path>] [--access <review|workspace-write|inherit>] [--model <text>] [--label <text>] [--worktree <branch>] |
{"type":"agentJob","job":{...}} |
agent wait |
<job-id> --until <running|attention|completed> [--timeout-seconds <n>] |
{"type":"agentWaitResult","job":{...},"timedOut":false} |
agent result |
<job-id> |
{"type":"agentResult","job":{...},"content":{...}} |
agent send |
<job-id> <text> [--enter] |
{"type":"ok"} |
agent focus |
<job-id> |
{"type":"ok"} |
agent close |
<job-id> |
{"type":"ok"} |
integration list |
なし | {"type":"integrationStatuses","integrations":[...]} |
integration enable |
<codex|claude-code|opencode|antigravity|cursor> |
{"type":"integrationStatuses","integrations":[...]} |
integration repair |
[<provider>] |
{"type":"integrationStatuses","integrations":[...]} |
guide |
なし | ペインチームの手順書をプレーンテキストで標準出力、ソケット不要 |
list |
なし | {"type":"list","panes":[...]} |
new-tab |
[--cwd <path>] [--command <text>] |
{"type":"pane","paneID":"..."} |
split |
<pane-id> <left|right|up|down> [--cwd <path>] [--command <text>] |
{"type":"pane","paneID":"..."} |
send |
<pane-id> <text> [--enter] |
{"type":"ok"} |
send-key |
<pane-id> <key> [--modifiers <mod,mod,...>] |
{"type":"ok"} |
read |
<pane-id> |
{"type":"content","content":{...}} |
status |
(<text> | --clear) [--pane <pane-id>] |
{"type":"ok"} |
wait |
<pane-id> --until <idle|attention> [--timeout-seconds <n>] |
{"type":"waitResult","state":"...","timedOut":false} |
events |
[--after <seq>] [--timeout <seconds>] |
{"type":"events","records":[...],"latestSequence":7,"timedOut":false} |
close-pane |
<pane-id> |
{"type":"ok"} |
focus |
<pane-id> |
{"type":"ok"} |
pane ID は TerminalSurfaceID の UUID 文字列です。list のレスポンス、pane レスポンス、$MYTTY_SURFACE_ID のいずれかから取得できます。job ID は AgentJobID の {"rawValue":"..."} 形式の UUID で、agentJob/agentWaitResult/agentResult レスポンスの job.jobID.rawValue から取得します。
agent spawn
--anchor(デフォルトは $MYTTY_SURFACE_ID)を分割して新しいペインを作り、指定した provider を起動します。--task(または --task-file の内容。ファイルはリクエストを送る前に mytty-ctl 自身が読みます)は、起動コマンドと合わせて1回のシェル入力として渡すので、worker の TUI 起動と競合しうる別の send は発生しません。
推奨: 自分と同じ provider の worker を起動するときは --access を省略してください。 省略した場合、--provider が anchor ペインの現在の前面エージェントと一致していれば inherit として、一致していなければ workspace-write として解決されます。つまり自分と同じ provider の worker を --access なしで起動すると、固定のフラグ集合に絞られるのではなく、自分の現在の permission/sandbox モードを自動的に引き継ぎます。inherit は、anchor ペインで動いているエージェント(つまり呼び出し元自身のプロセス)のモードを worker の起動コマンドに引き継ぎます(例: --permission-mode acceptEdits や --dangerously-skip-permissions で動いている claude の lead は、同じフラグの claude worker を起動します)。inherit はまず起動時のフラグを見ますが、Claude Code または Cursor の lead に限っては、フラグにモード関連の指定がなければ自分自身のローカルデータ(Claude Code は transcript、Cursor は store.db)から読み取った 現在の モードを使います。そのため起動後に対話的に切り替えたモード(Claude Code の shift+tab によるモード切り替えなど)も引き継がれます(起動時のフラグだけしか見ないわけではありません)。Codex の lead は今のところ起動時のフラグからしか引き継ぎません。どちらの手段でも何も得られなかった場合(lead がデフォルトモードで動いていて transcript にもモードの記録がない場合)、inherit は workspace-write と同じフラグにフォールバックします。明示的に --access inherit を指定した場合も同じ引き継ぎをしますが、anchor ペインの前面プロセスが読み取れない、または --provider と別の provider だった場合は workspace-write へ黙ってフォールバックせず inherit-unavailable で失敗します。provider をまたいでフラグを引き継ぐことに意味はないためです。
review を指定すると provider を read-only/plan モードで起動します。workspace-write は worker がファイルを編集できるモードですが、claude の worker では lead 自身のモードに関わらず常に --permission-mode acceptEdits で起動します。lead がより広いモード(auto、--dangerously-skip-permissions など)で動いているときに --access workspace-write を指定すると、lead に合わせるのではなく、承認プロンプトのたびに止まって待つ worker になってしまいます。acceptEdits でも Bash コマンドの承認は別に必要だからです。worker が自分と同じ provider のときは、--access を省略する(または明示的に --access inherit を指定する)ほうを --access workspace-write より優先してください。workspace-write が向いているのは provider が異なる worker を起動するとき、あるいは acceptEdits 相当の固定ポリシーが本当に欲しいときです。
--model は省略可能で、provider が使うモデルを指定します。値は provider CLI 自身のモデルフラグ(codex は -m <model>、claude と cursor は --model <model>)にそのまま渡されます。例: claude の worker に --model sonnet。省略すると provider のデフォルトモデルになります。--cwd を省略した場合のデフォルトは、anchor ペインのシェルが報告した作業ディレクトリ(シェル統合が最後に知らせたディレクトリ)であって、呼び出し元プロセス自身の cwd ではありません。ペインのシェルと違うディレクトリで動いている場合 -- たとえば claude --worktree で起動すると claude は git worktree に移動しますが、ペインのシェルは元の場所に残ります -- は --cwd "$PWD" を明示しないと、worker が worktree ではなく元のチェックアウトで動き出します。すべてのタスクには worker contract(作業ディレクトリの外に出ない、詰まったら止めずに続ける、最後に簡潔な要約で締める、という指示)が自動で追記されます。また、打鍵される起動行の先頭には HISTFILE を unset するガード(hist_ignore_space 構成向けの行頭スペース付き)が付くので、タスク全文を含む起動コマンドも、その後そのペインのシェルに手で入力したコマンドも ~/.zsh_history には残りません。
--worktree <branch> を指定すると、単に解決済みの --cwd/anchor ペインのディレクトリではなく、<branch> 用の git worktree の中で worker を起動します。ベースとなるリポジトリは同じディレクトリから解決します(git -C <dir> rev-parse --show-toplevel)。worktree の置き場所は <repo の親ディレクトリ>/<repo 名>-worktrees/<branch> です(branch 中の / はディレクトリ名の部分だけ - に置き換わります。例: branch feat/x なら mytty-worktrees/feat-x)。リポジトリ本体の兄弟ディレクトリになるので、メインのチェックアウト側の git status には一切現れません。そのパスが既にそのリポジトリの登録済み worktree であれば、現在どの branch をチェックアウトしているかにかかわらずそのまま再利用します(再 spawn した場合に冪等になります)。そうでない場合、<branch> が既存の local branch ならそれをチェックアウトして worktree を作り、存在しなければ現在の HEAD から新規に作成します。Mytty が worktree を自動で削除することはありません -- agent close でもペインを閉じるだけです -- 未コミットの作業が残っている可能性があるためです。worker の branch をマージまたは破棄したら、ベースリポジトリ側で git worktree remove <path> を自分で実行して片付けてください。not-a-git-repository、worktree-create-failed、invalid-worktree-branch については下のエラーコード表を参照してください。
job=$(mytty-ctl agent spawn \
--provider codex --access review \
--task "ログイン処理が高負荷時にタイムアウトする原因を調査して。" \
--label investigate-a | jq -r '.job.jobID.rawValue')
{
"type": "agentJob",
"job": {
"jobID": { "rawValue": "..." },
"paneID": { "rawValue": "..." },
"provider": "codex",
"label": "investigate-a",
"state": "launching",
"runID": null,
"sessionID": null,
"message": null
}
}
state は最初 launching で、worker 自身の hook イベント、または hook 連携が未インストールの場合は Mytty のネイティブな実行推定(ネイティブ推定による run 検出参照)のいずれかが実行の開始を確認した時点で running に変わります。--task/--task-file はどちらか一方が必須です。エンコード後のリクエストが 64 KiB のソケットエンベロープを超えるタスクは、接続を開く前に CLI 側で拒否されます。jobID が実際に何を指しているかは、下の「job のバインディング」を参照してください。
agent wait
その job がバインドした実行(job より前から存在していた実行ではありません)が指定条件に達するか、タイムアウトするまでブロックします。
mytty-ctl agent wait "$job" --until completed
{
"type": "agentWaitResult",
"job": { "...": "agent spawn の job と同じ形" },
"timedOut": false
}
runningは、job が実行にバインドし、その実行が running かそれより先に進んだ時点で解決します。attentionはwaiting-input/waiting-approvalのときだけ解決します。completedはsucceeded、failed、disconnected、launch-failed、lostのいずれかで解決します。
--timeout-seconds のデフォルトは 120 です。provider が一度も起動しない場合(実行ファイルが無い、hook 連携が壊れているなど)は、spawn から30秒以内に launch-failed になります。agent wait はこのケースで、フルタイムアウトまで待ちません。running と completed は、その job の provider に hook 連携が導入されていなくても解決します — Mytty がその実行のライフサイクルをネイティブに推定するためです。attention は依然として provider の hook が必要で、ブロックする代わりに即座に失敗します — 詳細は下記「wait の挙動」を参照してください。
worker が承認プロンプトで止まった場合の対処: 経過時間で見当をつけるのではなく --until attention(その provider の hook 連携が必要です)で捕捉します。agent result "$job" を読んで、ペインの画面に出ているプロンプトを確認し、同じレスポンスからペイン ID(.job.paneID.rawValue)を取り出して答えます。agent send でテキストを送っても claude の承認ダイアログは反応しないので、代わりに send-key で実際のキー入力(表示されている選択肢に合わせた "1" や "2" など)を送り、その後もう一度 agent wait --until completed してください。
agent result
job の最新状態と、そのペインの現在の画面内容を返します。agent wait --until completed の後にこれを呼び、worker の成果を回収します。
mytty-ctl agent result "$job"
{
"type": "agentResult",
"job": { "...": "agent spawn の job と同じ形" },
"content": {
"paneID": "...",
"text": "...",
"cursorRow": 10,
"cursorColumn": 2
}
}
これは provider のトランスクリプトではなく、ペインの現在の画面を読みます。そのため起動時のプロンプトには、最終的な要約を画面上で読める程度に簡潔にまとめるよう指示しています。
agent send / agent focus / agent close
job ID をそのペインに解決し、ペイン単位の send/focus/ close-pane の挙動をそのまま再利用します。途中で他のペインが開いたり閉じたりしていても、追加指示は常に意図した worker に届きます。
mytty-ctl agent send "$job" "回帰テストも追加して。" --enter
mytty-ctl agent focus "$job"
mytty-ctl agent close "$job"
{ "type": "ok" }
agent close は job のペインを閉じ、まだ完了していない job を lost に遷移させます。ユーザーがペインを閉じた、シェルが終了したなど、自然にペインが消えた場合も同じ扱いです。どの agent コマンドでも、pane-not-found ではなく lost として報告されます。
guide
ペインチームの手順書(環境変数、split/send/wait/read の流れ、provider ごとの起動コマンド)をプレーンテキストで標準出力に出し、終了コード 0 で終わります。MYTTY_CONTROL_SOCKET も起動中の Mytty も不要です。このファイルはコマンド引数と JSON の形を説明する場所なので、手順の一次情報は mytty-ctl guide の出力そのものを見てください。mytty-ctl --help(-h や引数なしでも同じ)を実行すると、代わりに上のコマンド一覧が出ます。Mytty 本体はこれと同じ内容を起動のたびに ~/Library/Application Support/mytty/mytty-ctl.md へ書き出しており、Claude Code / Codex 向けの「Agent に Mytty オーケストレーションの使い方を教える」設定はこのファイルへの参照を書き込みます(agent-providers リファレンス 参照)。
mytty-ctl guide
integration list / integration enable / integration repair
mytty-ctl integration list
mytty-ctl integration enable claude-code
mytty-ctl integration repair
{ "type": "integrationStatuses", "integrations": [
{ "provider": "claude-code", "status": "installed" },
{ "provider": "codex", "status": "not-installed" }
] }
integration list は全 provider の hook 連携の状態(installed、not-installed、needs-repair)を返します。設定 > Agents が表示するのと同じ状態を、呼び出しのたびに各 provider の設定ファイルから読み直したものです。直前のインストール試行が失敗している場合は error 文字列も入ります。
integration enable <provider> はその provider の hook をインストールし、integration repair は needs-repair の連携(provider を指定した場合はその 1 つ。未インストールのものを指定すれば単なるインストールになります)を再インストールします。どちらも AgentProvider の 5 つの識別子(codex、claude-code、opencode、antigravity、cursor)を取ります。agent spawn --provider の 3 値の語彙とは別物です — 連携は Mytty が追跡できる全 provider に存在し、agent spawn が起動できるものに限らないためです。
hook の有効化は、その provider のセッション内で Mytty 提供のコードが実行されるようになることを意味します。そのため変更を伴う 2 コマンドは、Mytty アプリが Mac の前にいる人間に確認ダイアログを提示して回答があるまでブロックします。これをスキップするフラグは意図的に存在しません — AI オーケストレーターはインストールを「依頼」できても「承認」は絶対にできない、という構造を保つためです。CLI は回答を最大 180 秒待ちます。拒否されると integration-declined で失敗し、承認されると設定画面のトグルと同じインストール(pane-team pointer の処理込み)を行った上で最新の状態一覧を返します。すでにインストール済みの provider の enable や、修復対象が無いときの repair は、ダイアログを出さずに現在の状態を返します。
list
開いている全ウィンドウの全ペインを一覧します。
mytty-ctl list

{
"type": "list",
"panes": [
{
"paneID": "B9AA8B83-B42D-4C11-B838-36B84C73032A",
"windowID": "...",
"tabID": "...",
"title": "zsh",
"command": "zsh",
"workingDirectory": "/Users/me/project",
"isActive": true,
"provider": "codex",
"agentState": "running"
}
]
}
provider は AgentProvider.rawValue( エージェントイベントプロトコル参照)、agentState は AgentRunState.rawValue です。そのペインにまだ1件もエージェントイベントが記録されていなければ、null にはならず、両方のキー自体が JSON から消えます(エージェント履歴の無いペインで実際に mytty-ctl list | jq . を実行して確認済み。provider も agentState も出力に現れません)。
new-tab
アクティブウィンドウ(なければ最初に見つかったウィンドウ)に新規タブを作成します。特定のウィンドウを狙って指定する方法はありません。特定のウィンドウに作りたい場合は、そのウィンドウの既存ペインを split してください。
mytty-ctl new-tab --cwd /path/to/project
{ "type": "pane", "paneID": "..." }
--cwd を省略すると、アクティブウィンドウの現在の作業ディレクトリを継承します。
--command <text> を指定すると、ペイン作成と同時に、その1回限りの入力としてテキストが新しいペインに渡されます。agent spawn が起動コマンドとタスクを渡すのと同じ仕組み(AgentLaunchPlan.initialInput(command:))で、履歴抑制ガードが先頭に付き、末尾に改行が1つだけ付きます。worker をタスク込みで一気に起動するにはこちらが推奨で、起動後に別途 send する必要がないため、worker の TUI がまだ起動中で入力を取りこぼす競合が起きません。agent spawn の起動行と同様、ペインのシェル履歴には残らず、セッション状態にも保存されません -- 新しいペインへの打鍵として存在するだけです。明示的に空の --command "" を渡すと、CLI が接続を開く前に拒否します。
mytty-ctl new-tab --cwd /path/to/project \
--command 'claude --permission-mode acceptEdits -- "$(cat /tmp/task.md)"'
split
指定ペインを対象方向に分割します。分割対象のペインは、自動でフォーカスしてから分割します。
mytty-ctl split "$MYTTY_SURFACE_ID" right --cwd /tmp
{ "type": "pane", "paneID": "..." }
--command <text> の挙動は new-tab と同じです -- 分割と同時に1回限りの初期入力として渡され、worker の TUI 起動と競合しうる別の send を避けられます。タスクをファイルに書いてから $(cat <task-file>) で渡せば、複数行のタスクも1つの引数のままです。--command のテキスト中の改行は(send と同様)ペインに届いた時点で Enter キー入力になるので注意してください。
echo "ログイン処理が高負荷時にタイムアウトする原因を調査して。" > /tmp/task-a.md
mytty-ctl split "$MYTTY_SURFACE_ID" right --cwd /path/to/worktree \
--command 'codex -s workspace-write -a never -- "$(cat /tmp/task-a.md)"'
すでに動いている worker への追加指示には、引き続き send を使ってください。
ペイン内で TUI アプリをテストする: エージェントは自分のツール越しに、パイプで繋がれた標準入出力でシェルコマンドを実行しており、自分のペインの pty は自分の TUI がふさいでいます。そのため開発中の TUI アプリ(raw mode で端末を扱うもの)を同じセッションの中から直接動かしたり操作したりすることはできません。代わりに別のペインを開いてください -- split "$MYTTY_SURFACE_ID" right --command '<テスト対象のアプリ>' -- そのうえで send/send-key で操作し、read で結果を確認し、終わったら close-pane で閉じます。こうすればそのアプリに本物の pty を渡せます。script -q /dev/null <アプリ> でも疑似端末を割り当てられるので、ペインを使うまでもない手早い動作確認に使えます。これはテスト対象のプログラムを実行しているだけでサブエージェントを作っているわけではないので、agent spawn の worker contract が禁止している hidden/native なサブエージェントの作成には当たりません。
send
ペインにテキストを送信します。--enter を付けると、続けて Enter も送ります。
mytty-ctl send "$paneA" "claude" --enter
mytty-ctl send "$paneA" "issue #42 を調査して" --enter
{ "type": "ok" }
send-key
単発のキーイベントを送ります。プレーンテキストでは反応しない対話的なプロンプト(矢印キーで選ぶメニュー、キャンセル用の Esc など)に使います。
mytty-ctl send-key "$paneA" escape
mytty-ctl send-key "$paneA" up
mytty-ctl send-key "$paneA" c --modifiers control
<key> に指定できる値(RemoteKeyMapping.swift):
| 分類 | 値 |
|---|---|
| 名前付きキー | escape、tab、return、delete、space、up、down、left、right、f1-f12 |
| 単一文字 | a-z、0-9、`、-、=、[、]、\、;、'、,、.、/ のいずれか1文字 |
--modifiers は shift、control、option、command からカンマ区切りで指定します(例: --modifiers shift,command)。未対応の <key> を渡すと、ペインが実在していても pane-not-found エラーになります。ControlCoordinator は「キーマッピングが無い」場合と「そのペインが無い」場合を区別せず同じ扱いにしているため、このケースに限りエラーコードの名前と実態が食い違います。この場合、キーイベントは送信されません。
{ "type": "ok" }
read
ペインの現在の画面テキストとカーソル位置を取得します。
mytty-ctl read "$paneA"
{
"type": "content",
"content": {
"paneID": "...",
"text": "$ ls\nREADME.md\n...",
"cursorRow": 3,
"cursorColumn": 0
}
}
カーソル位置を報告できない場合、cursorRow と cursorColumn は null になります。
status
ペインのエージェント自身による進捗の自己申告です。1 行・最大 100 Unicode スカラー・制御文字不可。
mytty-ctl status "running tests" # $MYTTY_SURFACE_ID に対して申告
mytty-ctl status "reviewing" --pane "$pane"
mytty-ctl status --clear
{ "type": "ok" }
ペインは呼び出し元自身の $MYTTY_SURFACE_ID がデフォルトです(agent spawn --anchor と同じフォールバック)。worker は自分の pane ID を知らなくても自分のペインに申告できます。申告はステータスバーのエージェント名・モデル名の隣に表示され、list のペイン項目と各 agent レスポンスの job スナップショットに statusNote として入ります — オーケストレーターは画面を読まなくても worker の作業フェーズを追えます。agent spawn の worker contract は、フェーズごとにこの申告を行うよう worker に指示しています。
申告は設計上 ephemeral です: メモリのみに保持(アプリ再起動で消える)、ペインの run の開始・終了(started、idle、succeeded、failed、disconnected イベント)で自動クリア、run 途中の遷移(running、承認・入力要求)では保持。--clear で明示的に消せます。空文字列は有効な申告ではありません — クリアは独立した操作です。自己申告は意図的にエージェントイベントのパイプラインに載せていません: run の状態を変えず、永続化してはならないものなので、その脇に置いています(mytty-ctl アーキテクチャ参照)。
wait
対象ペインの直近のエージェント実行が条件を満たすか、タイムアウトするまでブロックします。
mytty-ctl wait "$paneA" --until idle
mytty-ctl wait "$paneA" --until attention --timeout-seconds 600
{ "type": "waitResult", "state": "idle", "timedOut": false }
--timeout-seconds のデフォルトは 120 です。state は wait が解決した時点で観測された AgentRunState(そのペインに一度もイベントが来ていなければ null)です。timedOut は、タイムアウトまでに条件を満たせなかった場合に true になります。--until idle は、ペインの provider に hook 連携が導入されていなくても機能します — Mytty が実行のライフサイクルをネイティブに推定するためです。hook 連携が未インストールのエージェントが既に動いているペインに対して --until attention を実行すると、ブロックせず即座に provider-integration-not-installed で失敗します。詳細は下記「wait の挙動」を参照してください。
events
全ペイン分のエージェントイベントをまとめて long-poll で取得します。複数の worker を並行して見張るオーケストレーター向けの機能で、ペインごとに wait/agent wait をバックグラウンドシェルで並列に走らせる代わりに、ループの中で events を1つ呼べば済みます。
mytty-ctl events # カーソルだけ確立、履歴は返さない
mytty-ctl events --after 7 --timeout 60 # 次のイベントまでブロック
{
"type": "events",
"records": [
{
"sequence": 8,
"paneID": "...",
"provider": "codex",
"runID": "...",
"kind": "approval-requested",
"occurredAt": "2026-01-01T00:00:00Z",
"toolName": "shell",
"synthesized": false
}
],
"latestSequence": 8,
"timedOut": false
}
--after を省略するとカーソルを確立します。この場合は意図的に records を返しません -- 現在の latestSequence を返すだけで、呼び出し元が「今から」の監視を始められるようにするためです(それより前に起きたことを再生することはありません)。その値(または以降のレスポンスの latestSequence)を次回呼び出しの --after として渡してください。そのカーソルより新しいイベントが既に保持されていれば即座に返り、なければ、どこかのペインで新しいイベントが1つでも発生するか --timeout が経過するまでブロックします。明示的な --after 0 はカーソル確立とは別物で、通常の取得カーソルとして扱われます -- 保持されている履歴を先頭から全部返す(なければ最初の1件が届くまでブロックする)ので、ledger が空の状態から追いつきたいときはこちらを使ってください。--timeout のデフォルトは 30 秒で、600 に丸められます。タイムアウトしたレスポンスは records が空、timedOut: true、latestSequence は変わりません -- タイムアウトはエラーではなく、そのカーソルのままポーリングを続けてください。records[].paneID/runID は list/agent レスポンスと同じ UUID 文字列形式です。kind は AgentEventKind の raw value、synthesized は mytty 自身が生成したイベント(ネイティブな実行推定、Cursor の承認待ち推定など)である場合に true になります -- provider 本来の hook から来たイベントではありません。呼び出し元のフィードに現れうるそうした記録の一つが起動時スイープです。起動時に Mytty は前回の app インスタンスが非終端のまま残した run を掃除し、その run に対して合成した disconnected event(hookName: "mytty.startupSweep")を記録します。裏側の ledger はアプリ全体で最大 1000 件しか保持しないため、カーソルが十分古くなっていると、はみ出した分は黙ってスキップされます -- events は保持されている履歴を高々1回配信する仕組みであって、再生を保証するログではありません。
cursor=$(mytty-ctl events | jq -r '.latestSequence')
while true; do
response=$(mytty-ctl events --after "$cursor" --timeout 60)
cursor=$(echo "$response" | jq -r '.latestSequence')
echo "$response" | jq -c '.records[]' # 新しいイベントごとに処理
done
close-pane
確認ダイアログを出さずに、即座にペインを閉じます。呼び出し元は人間が Close をクリックしたのではなく、自動化されたエージェントである、という前提に立っています。
mytty-ctl close-pane "$paneA"
{ "type": "ok" }
ウィンドウ内で最後のタブの最後のペインを閉じる場合、そのウィンドウ自体のクローズ確認ダイアログ(ユーザーが設定していれば)は依然として出ます。サブエージェント用に作ったペインでは、通常この状況は起きません。
focus
指定ペインを前面にフォーカスします。制御をユーザーに戻したいときに使います。
mytty-ctl focus "$paneA"
{ "type": "ok" }
失敗時のレスポンス
失敗したリクエストには、サーバーから {"type":"failure","code":"..."} が返ります。CLI 側はこれを JSON として出力せず、非ゼロの終了コードと標準エラーへのメッセージとして表現します。
| コード | 意味 |
|---|---|
invalid-request |
JSON としてデコードできなかった、または CLI 側の引数構文が不正 |
not-ready |
control server にまだ delegate が設定されていない(アプリ起動中) |
new-tab-failed |
new-tab がタブを作成できなかった |
split-failed |
split が指定ペインを分割できなかった |
pane-not-found |
指定した pane-id が生存中のペインに解決できない(send、send-key、read、wait、close-pane、focus でも返り得る)。agent spawn も --anchor が生存中のペインに解決できない場合にこれを返す |
provider-integration-not-installed |
wait --until attention / agent wait --until attention: ペイン(または job)のフォアグラウンドプロセスが既知のエージェントで、その連携が有効化されていない(下記「wait の挙動」参照)。agent spawn はこのコードをもう返しません — job は hook が報告する実行ではなく、ネイティブに推定された実行にバインドされるためです |
provider-integration-needs-repair |
agent spawn: provider の hook 連携は導入済みだが古くなっている、または壊れている — ネイティブな実行推定は needs-repair に対してはあえて関与しないため、修復だけが解決策です |
invalid-cwd |
agent spawn: --cwd が実在するディレクトリを指していない |
invalid-label |
agent spawn: --label に制御文字が含まれる、または 100 Unicode スカラー値を超えている |
invalid-model |
agent spawn: --model が空、制御文字か空白を含む、または 100 Unicode スカラー値を超えている |
inherit-unavailable |
agent spawn: --access inherit を指定したが、anchor ペインの前面プロセスを読み取れないか、--provider と同じ provider ではない |
invalid-worktree-branch |
agent spawn --worktree: branch が空、100 Unicode スカラー値を超えている、または git の branch 名として不正 |
not-a-git-repository |
agent spawn --worktree: 解決済みの --cwd/anchor ペインのディレクトリが git リポジトリの中にない |
worktree-create-failed |
agent spawn --worktree: git が worktree を作成できなかった -- たとえば対象パスがディスク上に存在するが、そのリポジトリに登録済みの worktree ではない場合 |
invalid-status |
status: テキストが空、制御文字を含む、または 100 Unicode スカラーを超えている |
invalid-task |
agent spawn: 解決後のタスクテキストが空 |
spawn-failed |
agent spawn: ペインを作成できなかった |
job-not-found |
agent wait/agent result/agent send/agent focus/agent close: 指定した job ID が未知(そもそも存在しない、または直前の Mytty 再起動より前に発行されたもの。job ID は永続化されない) |
integration-declined |
integration enable/integration repair: Mac の前にいる人間が確認ダイアログを拒否した(または誰も回答しなかった) |
job-lost |
agent send/agent focus: job のペインが消えている(下記 lost を参照)。agent result と agent close はこのコードを使わない -- job の lost 状態と空の結果を返し、すでに消えているペインを閉じる操作も終了コード 0 になる |
wait の挙動
wait は、条件を満たすかタイムアウトするまで、一定間隔(約300 ミリ秒)でポーリングします。
--until idleは、対象ペインの直近のエージェント実行がidle/succeeded/failed/disconnectedのいずれかになった時点で解決します。一度もエージェントイベントを受け取っていないペインは、タイムアウトするまでブロックし続けます。「まだ何も来ていない=idle 扱い」というデフォルトはありません。--until attentionは、実行状態がwaiting-inputまたはwaiting-approvalになった時点で解決します。Antigravity の導入済み hook は承認・入力待ちイベントを一切出さないため(Agent providers 参照)、この provider が動くペインではwait --until attentionは常にタイムアウトします。Antigravity には--until idleを使ってください。Cursor も入力待ちイベントは出しませんが、承認待ちには到達しえます。mytty は Cursor のpreToolUsehook と、対応するpostToolUse/postToolUseFailureとの間隔から、詰まった tool call を推定します。そのため、その推定が発火した時点(何も解決しなければ tool call 開始からおおよそ10秒後)で、Cursor ペインのwait --until attentionも解決します。- 対象 provider の hook 連携が設定でまだ有効化されていない場合、Mytty は代わりに実行のライフサイクルをネイティブに推定します — ペインのポーリング済みフォアグラウンドプロセスから、Claude Code/Codex/Cursor についてはそれぞれの transcript や
store.db(Cursor)からも(Agent event protocol 参照)。これで--until idleは完全にカバーされますが、ネイティブ推定はwaiting-input/waiting-approvalをあえて一切報告しません(誤検知が Attention Inbox に偽の要対応項目を出しかねないため)。そのため--until attentionには引き続き provider 本来の hook が必要です。waitはポーリングループに入る前に一度だけこれを検査します: 条件がattentionで、ペインのフォアグラウンドプロセスが既に既知のエージェント(0.5 秒周期のプロセスポーリングが見ているもの。MYTTY_AGENTヒント込み)、かつその連携がnot-installedなら、タイムアウトまでブロックする代わりに即座にprovider-integration-not-installedで失敗します。mytty-ctl integration enable <provider>か 設定 > Agents で修復してください。--until idleはこの事前検査で失敗することはありません。検査が毎ポーリングでなく事前の 1 回だけなのは意図的です(wait の途中でエージェントが終了してシェルに戻っても、正当な wait を失敗させないため)。needs-repairの連携をどちらの条件でも素通しするのも意図的で、進行中のセッションからイベントが届き続けている可能性があるためです —needs-repairにはネイティブ推定も関与しません。wait 開始時点でエージェントがまだ起動していない場合(split --commandの直後にwaitする通常の流れ)、事前検査にはシェルしか見えないため attention wait は従来どおりタイムアウトまでブロックします。provider を起動した直後の attention wait のタイムアウトは、同じ連携未導入のシグナルとして扱ってください。
agent wait も同じようにポーリングしますが、対象はペインではなく agent spawn が作った job です。条件も wait(idle/attention)とは異なる3種類(running/attention/completed)になります。詳細は上の agent wait を参照してください。同じ attention 限定の事前検査が適用され、not-installed な provider に対して spawn した job でも agent wait --until running/completed は(どちらもネイティブ推定から)解決できますが、--until attention は即座に provider-integration-not-installed で失敗します。
job のバインディング
agent spawn が作る job は、「そのペインが今やっていること」全般ではなく、特定の1つの worker 実行を指します。内部では AgentJobTracker が、新しいペインが作られた瞬間に既に存在する実行 ID の集合(新規ペインなので通常は空)を記録しておきます。その後、そのペイン/provider について観測した実行のうち、ID がその集合に含まれていない最初の実行に job をバインドします。一度バインドすると、job が自然に別の実行へ切り替わることはありません。これにより、連続して spawn した2つの job が互いの実行を誤って観測することはなく、agent wait --until completed が job より前から存在していた実行で解決してしまうこともありません。
唯一の例外は意図的なものです。agent send が既に完了している job に追加指示を送ると、その job を再アーム(AgentJobTracker.prepareForFollowUp)します。これにより、以後の agent wait --until completed は、既に終端に達した古い実行ではなく、その追加指示が新たに始める実行を追跡します。まだ実行中の job に追加指示を送っても、この効果はありません -- 進行中の実行にバインドされたままです。
job の状態は、バインドした実行の AgentRunState を直接マッピングしたものです。ステータスバーが使う AttentionCenter の「このペインで最も関連度の高い実行」というロジックは経由しません。両者は別の問いに答えています。30秒以内にどの実行もバインドできなければ、job は launch-failed になります(実行ファイルが無い、TUI が一度も起動しなかった、hook が一度も発火しなかった、のいずれもこれでカバーされます)。job のペインが消えたら、まだ完了していない job は lost になります。job がバインドしている実行が起動時スイープで掃除された場合、その job はほかの run と同様にその run の disconnected 状態を報告します。launch-failed と lost のどちらの遷移も後戻りしません。
job のレジストリは実行中のアプリのメモリ上にしかなく、永続化されません。Mytty を再起動すると、それより前に発行された job ID は job-not-found になります。job が指していたペインやプロセス自体は影響を受けず、その job ID からはもう辿れなくなるだけです。
制約・注意点
new-tabはどのウィンドウに作るかを明示的に指定できず、アクティブウィンドウ(なければ最初に見つかったウィンドウ)に作られます。特定のウィンドウを狙いたい場合は、そのウィンドウの既存ペインをsplitしてください。close-paneは確認ダイアログを出さずに即座に閉じます。唯一の例外(ウィンドウ内最後のタブの最後のペインを閉じる場合にウィンドウ自体の確認ダイアログが出るケース)は、サブエージェント用ペインの通常運用ではまず発生しません。- pane ID は
TerminalSurfaceIDの UUID 文字列です。listのレスポンス、paneレスポンス、$MYTTY_SURFACE_IDのいずれかから取得します。 - control ソケットが受け付けるリクエストの最大サイズは、エージェントイベントソケットと同じ 64 KiB のエンベロープ上限に合わせてあります。非常に大きな
sendの引数は、1つの巨大なリテラルとして渡すのではなく、分割するか shell 経由でパイプしてください。agent spawnも、エンコード後のリクエスト(タスク本文 + 自動追記される worker contract)に対して同じ上限を接続前にチェックします。そのため、大きすぎるタスクはソケット書き込みが黙って失敗するのではなく、その場で CLI のエラーになります。 agent spawnは既存ペインに worker を起動することは一切なく、必ず新しいペインを作ります。これは job のバインディングを正しく保つための前提(上の「job のバインディング」を参照)です。少数のペインを手動で管理する場合よりも、不要になった job をagent closeで閉じることの重要性が増すということでもあります。- job ID は
AgentJobIDの{"rawValue":"..."}形式の UUID で、どのagentレスポンスでもjob.jobID.rawValueから取得します。pane ID とは互換性がありません。
参考
- mytty-ctl でエージェントのチームを動かす:
agentコマンドを使った複数worker の段階的な例を扱っています。 mytty-ctl guideの "WATCHING SEVERAL WORKERS AT ONCE" セクションにも、上記と同じeventsカーソルループが載っています。CLI 自身が組み込みで持っているマニュアルとして、エージェントが読む前提で書かれています。- mytty-ctl アーキテクチャ: control ソケットが事前設定不要で動く理由と、
agent waitを支える job/実行のバインディングの仕組みを説明しています。 - Agent providers: どの provider が承認・入力イベントを出すかをまとめています。
wait --until attentionに関係します。 - エージェントイベントプロトコル:
listとwaitに出てくるAgentProviderとAgentRunStateの値を定義しています。 .claude/skills/mytty-panes/SKILL.md: これらのコマンドを使ったレシピを読むためにmytty-ctl guideを実行するよう指示する短いポインタです。