Aiterm Mcp について
Aiterm は、AI エージェントに永続的な対話型セッションを提供する MCP ターミナルサーバーです。Claude Code、Codex CLI、Grok CLI、Cursor Agent CLI を起動でき、tmux または psmux のセッションはサーバーやクライアントの再起動後も維持され、人間も共有ターミナルから操作できます。
主な機能
- 01永続的なターミナルセッション
- 024 種類のコーディングエージェントを起動
- 03SSH、Docker、REPL をセッション内で実行
- 04トークン消費を抑えて出力を取得
- 05実行中エージェントのモデルと推論強度を変更
活用シーン
- 01あるコーディングエージェントから別のエージェントを実行
- 02クライアント再起動後にターミナル作業を継続
- 03CI や定期ジョブでエージェントを実行
- 04共有ターミナルからエージェントと共同操作
MCP 対応
ターミナルセッション、エージェント起動、設定、承認、診断のツールを MCP クライアントに提供します。POSIX では tmux、ネイティブ Windows では psmux を使い、MCP サーバーやクライアントの再起動後もセッションを維持します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
14掲載時のツール一覧 · 3 週間前に更新
このツールにパラメーターはありません。
詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
From any MCP client, launch Claude Code, Codex CLI, Grok CLI, or Cursor Agent CLI through one harness API inside a persistent interactive TUI.
This image represents different intelligences sharing one persistent workspace and advancing the same work from their own perspectives.
Aiterm
(日本語: README.ja.md)
Let your AI orchestrate other AIs. One
agent_launchcall selects the execution harness separately from its model and hands you a persistent session to drive. Cursor can run GPT, Claude, or Grok while Cursor still owns the session, hooks, and transcript.What it is: one persistent MCP terminal your AI drives — and can launch other coding agents into.
ssh,docker exec, a REPL, or another agent's TUI all nest inside that one terminal as just text you send in. The mechanism is deliberately plain — your MCP client drives the other agent's terminal turn by turn: no hidden protocol, no separate aiterm-owned shared-memory layer, no autonomous negotiation. Launched agents still read the normal project and harness memory/configuration that a direct CLI launch would use.No human at a terminal required. aiterm is driven programmatically over MCP, so an AI can launch and drive another agent with no one sitting in the terminal — from an orchestration loop, a CI step, or a cron job.
MCP = Model Context Protocol — the open standard that lets tools like Claude Code plug capabilities into an AI.
Built and maintained by Quo at kitepon.dev.
Install in your MCP client
検出したClaude Code・Codex・Grok・Cursorのユーザー設定へ登録する標準入口:
npm install -g aiterm-mcp@latest
aiterm-setup --json
aiterm-setupは端末の依存準備、MCP経由の端末実行、登録と読戻しまでを一回で行う。
WindowsはwingetでPowerShell 7・Git for Windows・psmux、macOSはHomebrewでtmux、
Ubuntu/Debianはsudoとaptでtmuxを準備する。必要な公式package managerと実行権限は事前に必要。
他のLinuxでも既存tmuxを利用できるが、自動導入はunsupportedで停止する。
既存設定の他サーバーを保持し、JSON設定は変更前の.aiterm-backupを残す。
結果のstatusはready/unsupported/failed/restart_required。未検出のAIはnot_detectedとし、全AI未検出は成功にしない。
登録先はglobal packageのNodeとMCP入口の絶対パスで、npm一時cacheやsource checkoutは登録しない。
global packageは、npmの現在のglobal rootか、実行中のNodeの既定のglobal rootにあるものを指す。npmのprefixを利用者ごとの場所へ向けた環境でも、共通の場所へ導入したAitermを登録できる。
CodexとGrokは、登録が同じなら公式CLIで作り直さず、利用者が足した項目(待ち時間など)を保つ。
HomebrewのNodeは更新後も有効なoptのパスをMCP登録とCodexのhookに使う。旧版の登録でNode更新後に起動できなくなった場合も、更新後のaiterm-setup --jsonで修復できる。
更新後も同じ入口を実行し、MCP clientを再起動する。npm install自体はユーザー設定を変更しない。
公開JSONはschema: "aiterm.setup-result.v1"、全体のstatus、端末のbackend、
AI別のintegrationsと選択機能のcodex_steerを持つ。失敗時はreason_codeを付け、終了コードはreadyなら0、再起動待ちは3、それ以外は2となる。
MCP登録を利用者や他の製品が管理する環境では、aiterm-setup --hooks-onlyでClaude Code・Cursorの親配送hookだけを登録できる。
依存準備、端末の実動作確認、MCP登録、Codex Steerには触れない。登録済みなら設定を書き換えない。
結果はschema: "aiterm.parent-hooks-result.v1"、全体のstatus(ready/unsupported/failed)、AI別のhooks(configured/unchanged/not_detected/failed)を持つ。終了コードはreadyなら0、それ以外は2となる。
CodexへSteerを有効にする(macOS・Windows・Linux)
対話実行のaiterm-setupで「Aiterm単品」と「Steer付き」を選べます。無人導入では明示します。
aiterm-setup --json --codex-steer enable
公式キューと公式hookを使い、実行中の親には同じターンの次の推論へ回答を渡し、終了後は同じ会話を自動再開します。
Codexの起動プログラムと通常のstdio通信は変更しません。hookの実行ファイルが失われてもCodexの起動・応答は継続します。
終了後の再開は公式キューの監視周期に従い、約10秒かかる場合があります。
Codexのアプリは、見ていない会話を60秒で寝かせます。寝ている会話のキューは、その会話が開かれるまで動きません。
Aitermは、入れた約15秒後にまだ文が残っていて、番が普通に終わって寝ている会話なら、アプリにその会話を開かせて起こします(codex://threads/<会話>)。
その時、アプリの画面はその会話へ切り替わります。番の途中の会話、途中で止められた会話、CLIの席は起こしません。止める時は環境変数AITERM_STEER_CODEX_WAKE=0を立てます。
起こすのはmacOSとWindowsのアプリです。Linuxのアプリが寝かせた会話は、まだ起こしません(実物で確かめられていません。会話が開かれた時に届きます)。
aiterm-setupはCODEX_HOME/hooks.jsonへ専用のPostToolUseとStopを追加し、公式APIでその2件だけを承認・読戻しします。
他のhookや承認は保持します。選択と配送の所有記録は~/.config/aiterm-mcp/codex-parent-hooks/へ保存します。
同じ設定で再実行しても既存hookの順序を変えず、新たな再起動要求を発生させません。
WindowsのhookはPowerShell 7で実行します。更新後のsetupで、既存のAiterm hookコマンドも更新します。
既存の中継は新しいhookの確認後に解除し、保存していたCODEX_CLI_PATHを復元します。macOSの専用LaunchAgentも解除します。
移行前から動いているCodexがあればrestart_required(終了コード3)を返します。完全終了・再起動後に
aiterm-setup --codex-steer statusでreadyを確認してください。旧設定は移行を実行するまで維持します。
hookはAiterm自身の配送記録と本文が一致する回答だけを取り出し、利用者がキューに入れた入力は保持します。
取り出し中断や出力失敗はparent_deliveriesにunknownとCODEX_HOOK_DELIVERY_UNCONFIRMEDで現れ、本文を保存します。
自動再送はしません。長い回答はCodexの公式hook処理で抜粋と全文ファイルへの参照になる場合があります。
解除・hook未対応の旧版への巻き戻し前はaiterm-setup --codex-steer disableを実行してCodexを再起動してください。
公式Codex Desktop(macOS・Windows・Linux)の同梱CLIを先に使い、Desktopが無い端末では通常のCodex CLI(公式キュー・hookに対応する0.154以上)を使います。
When a Desktop update moves the bundled Codex CLI, Aiterm finds it again at use time and updates its configuration. If it cannot, it returns CODEX_DESKTOP_BINARY_MOVED; start Desktop and rerun setup.
Aiterm単品の公式キュー配送は従来どおり利用できます。
No clone or build is required. Each client launches the published package with:
npx -y aiterm-mcp
Requires Node.js ≥ 18 and a supported multiplexer backend: tmux on POSIX or psmux 3.3.8+ on native Windows. Driving Codex also requires the Codex CLI to be installed and authenticated.
Claude Code
Add it for your user account:
claude mcp add --scope user --transport stdio aiterm -- npx -y aiterm-mcp
Or commit this as a project-scoped .mcp.json:
{
"mcpServers": {
"aiterm": {
"command": "npx",
"args": ["-y", "aiterm-mcp"]
}
}
}
Claude Desktop
Add this server to claude_desktop_config.json:
{
"mcpServers": {
"aiterm": {
"command": "npx",
"args": ["-y", "aiterm-mcp"]
}
}
}
Cursor
Save this as .cursor/mcp.json for the project, or ~/.cursor/mcp.json globally:
{
"mcpServers": {
"aiterm": {
"command": "npx",
"args": ["-y", "aiterm-mcp"]
}
}
}
Ownership boundary: this repository owns installation, configuration, persistent PTYs, agent sessions, state/schema/migrations, diagnostics, recovery, updates, and releases. It can be cloned and operated on its own using this README and the product docs. dotagents optionally integrates Aiterm into the wider factory—host wiring, cross-product compatibility, and aggregate acceptance—but does not control Aiterm and is not a runtime dependency.
Measured, not claimed: in the recorded 203-test benchmark, a pty_read puts ~7.1× fewer tokens in your context than the raw log — and the pass/fail verdict survives the fold. → When to reach for it vs. the built-in shell
18 tools: seven PTY tools — pty_open / pty_send / pty_read / pty_key / pty_close / pty_list / pty_observe — to open, drive, read, and observe one persistent terminal; one canonical agent launcher, agent_launch, which selects claude-code, codex-cli, grok-cli, or cursor-cli as the execution harness; three deprecated launcher aliases kept for migration; agent_models; agent_configure; agent_auth; agent_approval; claude_turn; claude_approval; and diagnostics. The backend is tmux on POSIX and psmux on native Windows, so sessions survive even if the MCP server or the AI client restarts.
v0.28.0 では、実行ハーネスとモデルを分離しました。 ハーネスはエージェントのループ、認証、フック、セッション、トランスクリプトを管理し、model はそのハーネスで実行するものを指定します。そのため、Cursor Agent CLI では、補完の契約を Cursor のフックから別のハーネスのものに変えることなく、GPT、Claude、Grok を選択できます。Composer は Cursor のモデルの一つであり、ハーネスでも Grok のモデルでもありません。harness: "cursor-cli", model: "composer-2.5-fast"(または composer-2.5)を使用してください。以前のランチャーツールは、同じ実装を呼び出す薄い互換性エイリアスです。
v0.25.2 では、Grok 4.6 を含む、設定を繰り返しインプレースで変更する動作が安定しました。 Grok Build
1.0.3 で /model の成功通知を確認する前に画面が再描画される場合、aiterm はその状態がコマンド実行前には存在しなかったとき、永続フッターから要求されたモデルと推論の強さを確認します。呼び出し側で再試行や再起動を行ったり、失敗を成功として扱ったりする必要はありません。grok-4.6 の明示的な起動と設定では、引き続きライブカタログのチェックが行われます。
v0.25.0 では、Grok と Composer で共通のランチャー制御を利用できるようになりました。 各ランチャーは reasoning_effort を渡し、--sandbox read-only で write_scope: "read-only" を適用し、agent_configure によるモデルと推論の強さのインプレース変更をサポートします。PTY を作成する前に、aiterm は明示的に指定された Grok/Composer のモデルと、Composer のデフォルトモデルを、ライブの grok models カタログと照合します。利用できないモデルは明示的に失敗し、ハーネスの CLI が別のモデルにフォールバックすることはありません。その後 Composer は Grok CLI を離れ、Cursor のモデルの一つになりました。
v0.24.3 では、現在の MCP プロセスから明示的に選択されたランチャー環境変数を転送します。 env_vars に変数名を渡すと、aiterm は起動時に現在の値を読み取り、値が存在するものだけをそのエージェントに設定します。永続マルチプレクサーサーバーが MCP プロセスより前に起動していても機能するため、古いバックエンドサーバーの環境によって、シートごとの ID やワークフロー変数が消えることはありません。また、Codex v0.147 の長時間実行中のモデル/推論の強さのフッターにある任意の fast トークンも認識するため、再描画、再試行、再起動を行わずに、アイドル状態の medium fast · セッションで agent_configure を利用できます。
v0.24.2 では、長時間実行中の Codex セッションでもインプレース設定が機能し続けます。 起動ヘッダーがキャプチャされたペインの表示範囲からスクロールして消えた後も、aiterm は永続的なモデル/推論の強さのフッターと入力プロンプトを組み合わせて Codex を認識します。そのため、アイドル状態のセッションを直接設定でき、呼び出し側で TUI の再描画、再試行、エージェントの再起動を行う必要はありません。
v0.24.0 では、エージェントのインプレース設定が追加されました。 agent_configure は各ハーネス固有の制御を使用して、実行中の Codex または Claude セッションのモデルや推論の強さを変更しながら、その PTY、ハーネスセッション、会話コンテキストを保持します。
v0.23.0 では、ローカルで動作する、ハーネスをまたいで利用可能なフォーク機能が追加されました。 任意のランチャーに throughline_source_session を指定し、prompt にミッションを渡すと、aiterm は PTY の作成前に、ローカルにインストールされた Throughline にそのセッションの読み取り専用ハンドオフコンテキストを要求します。返されたメモリを変更せずにミッションの先頭に追加し、ソースセッションのデータベースの所有権を移動したりコピーしたりすることはありません。Throughline が見つからない場合や、無効または空の結果を返した場合、起動は明示的に失敗し、クリーンなフォールバックは行われません。このフィールドを省略した場合は、通常どおりクリーンな状態で起動します。
v0.22.0 makes launched agents full project collaborators. All four launchers now use the
same normal HOME, working tree, harness home, project/user/local configuration, MCP servers,
plugins, skills, permissions, trust, memory, and session history as a direct CLI launch. Aiterm
isolates only its own per-launch completion correlation state. Every child is told that it is a
sub-agent and receives its parent session, delegation depth, lineage, and
delegation_allowed=true; a child may delegate further, while the lineage makes reflexive
self-copy loops visible and avoidable. The historical managed_completion receipt field remains
for API compatibility and means “completion correlation enabled,” not environment isolation.
v0.21.3 removes Codex Stop hooks from the completion path. Codex completion and
final-message attribution now come from the root rollout transcript's durable
task_complete.turn_id, observed after the dispatch byte boundary. A broken or stale
hook executable can no longer strand aiterm-wait. v0.21.0 added explicit
write_scope declarations for external-agent launchers; v0.21.3 also fixes their
structured launch receipts so a supplied scope and its enforcement status are retained.
v0.20.3 prevents concurrent
correlated Claude/Fable sessions from turning one broken login into many competing login
flows. Every new Claude launch verifies the
harness-owned shared credential store before creating a PTY, while healthy credentials
remain reusable across concurrent and repeated sessions. The v0.20 line also distinguishes
a non-blocking aiterm-wait --timeout 0 observation (running, exit 5) from a real timed-out
wait. The v0.19 line added the correlated Claude approval relay,
preserved multiline shell delivery, and extended factory diagnostics on native
Windows. As of v0.16/0.17 a parent agent never blocks on aiterm:
agent sessionへの送信は非ブロックdispatchであり、Codex/Claude Code親には回答本文を自動配送する。
それ以外の親はreceiptのprocess起動情報でaiterm-waitを実行する。
終了コードは0=done、3=timeout、4=closed、待機しない照会の5=runningを表す。
Factory diagnostics and the local runtime-error store collect only when
canonical dotagents config explicitly sets collection.enabled: true;
collection is off by default and performs no network I/O. It ships via
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
Release re-registers the Official MCP Registry entry.
Status: actively maintained · current public release v0.60.1 · runs on Linux · WSL2 · macOS · native Windows (tmux on POSIX, the tmux-CLI-compatible psmux on native Windows — no WSL required) · MIT · see the CHANGELOG.
Update and rollback
The npm package is the standalone distribution; dotagents is not involved. For a global install,
update with aiterm-update. It reinstalls the requested version into the same npm prefix, then reruns the new version's
aiterm-setup --json to re-register and re-verify.
aiterm-update # this machine to latest
aiterm-update --host rabbit --host win-test # this machine and SSH hosts to the same version
aiterm-update --version 0.39.0 --check # report current and target versions without changing anything
--host takes an ~/.ssh/config alias or host name; hosts are not stored. The version is resolved once on the calling
machine so every host lands on the same version. Hosts older than aiterm-update get it through npm first. When the
npm prefix is not writable, the result is permission_required with the command to run as an administrator. aiterm-mcp
servers that were already running keep the old code until their MCP client restarts (running_servers); tmux/psmux
sessions survive. Versions without aiterm-update use npm install -g aiterm-mcp@latest and aiterm-setup --json. To roll back, install a known-good immutable version,
for example npm install -g "aiterm-mcp@<known-good-version>", then restart the MCP client. setupを持つ版では再起動前にaiterm-setup --jsonを再実行する。For an npx configuration,
use aiterm-mcp@latest to update or replace it with aiterm-mcp@<version> to pin or roll back.
Check the CHANGELOG for state/schema compatibility before downgrading. Maintainer
release and artifact rollback are specified in the product-owned release procedure.
Why now
A lot of 2026's agent tooling is converging on orchestration: a lead model delegating a mechanical refactor to Codex, running Composer on a bulk edit while it reviews the diff, fanning one task across several agents to spare its own context window. All of those agents already live in a terminal. aiterm makes that terminal a first-class, MCP-native tool — so the model doing the orchestrating can spawn and steer the others without a human wiring up panes.
Built with Codex and GPT-5.6 for OpenAI Build Week 2026
aiterm predates Build Week, so the event work is kept visible in dated commits. During the submission window (July 14–16, 2026), I extended it with safe serialized delivery for long PTY input, correlated operation IDs and bounded result recovery, machine-readable launch and idempotent close receipts, and a hardened readiness gate that prevents prompts from disappearing during TUI startup redraws. The public comparison from the pre-event release is v0.12.2...main.
I used Codex with GPT-5.6 as an engineering collaborator: it inspected the implementation, challenged the API and recovery contracts, generated focused regression cases, and helped verify race, security, timeout, and malformed-event paths. I reviewed the diffs and test evidence and retained the final product and architecture decisions. At that Build Week checkpoint, the regression suite contained 262 tests covering normal operation as well as failure and recovery behavior; current release receipts live in the CHANGELOG and release ADRs.
ClaudeがAPIエラーや安全判定の拒否で終了した時は、Stop hookが発火しなくても次のpty_sendを新しいturnとして扱う。現在のturn開始後のエラー記録だけを確認し、過去のエラーで実行中のturnを解除しない。上流の拒否はエラーのまま返す。
Codexがサービスの誤りや通信の失敗でturnを打ち切った時は、完了待ちがdoneではなくerrorを返す(aiterm-waitはexit 7、親への配送はoutcome=error)。errorは応答の本文とURLを落とした1行の文で、Codexが記録した種類(internal_server_error・http_connection_failedなど)はerror_kindに載る。利用上限は今までどおりrate_limited。誤りで終わった後の席は、次のpty_sendを新しいturnとして受ける。
Cursorが誤りでturnを終えた時(記録のturn_endedがstatus: "error")も同じで、完了待ちはerrorを返し、文の頭の角括弧の名前(unavailableなど)をerror_kindに載せる。利用上限の文はrate_limited、席で止めたturn(User aborted request)は今までどおりdone。
Two ways to use it
1. Drive SSH, containers, and REPLs in one persistent terminal — the primitive
This is the base, and it works with just the platform backend — tmux on POSIX or psmux on native Windows. pty_open grabs one local terminal; ssh host, docker exec -it x bash, or a REPL are just text you pty_send into it — once. Every command after that rides the same already-authenticated session. Session kind is never a tool-level distinction.
pty_open() → grab one local terminal
pty_send(id, "ssh 192.168.1.2") → authenticate once, inside that terminal
pty_send(id, "uname -a") → every later command rides the SAME session
pty_read(id, { wait: true }) → read the token-reduced output, completion detected
Origin. I built aiterm for exactly this. Driving my homelab from Claude Code one command at a time meant every SSH command became its own connect → authenticate → disconnect: re-typing the passphrase and one-time code each time, short-lived sessions piling up, and eventually my own defenses (fail2ban, MaxStartups/MaxSessions, account lockout) locking me out — the security meant to stop attackers ended up stopping me. Holding one authenticated session fixes all three at once. That pain is why the persistent terminal exists; launching whole other agents inside it is what it grew into.
2. Launch other coding agents into that terminal — the orchestration flagship
The same primitive hosts another agent's TUI. agent_launch starts a selected execution harness inside a fresh persistent terminal and returns a session_id. harness names the component that owns the agent loop, authentication, hooks, session, and transcript; model remains an independent choice. The launched process sees the same project and user environment as a direct CLI invocation: normal configuration, MCPs, plugins, skills, permissions, trust decisions, memory, and history are not copied, filtered, or replaced. Aiterm adds only completion correlation and a non-user sub-agent context containing role=subagent, the parent session, delegation depth, lineage, and delegation_allowed=true.
起動結果には正規harnessを含むaiterm.agent-launch-result.v1が付き、旧providerは互換fieldとして残る。同じharnessはagent dispatch、aiterm-wait、agent_configure、pty_listにも載る。Codexは通常rollout、Grokは通常session event、Claudeはlaunch固有Stop hook、Cursorは通常agent transcriptのturn_endedを完了正本に使う。agentへの送信はpty_sendだけで行い、Aitermが送る時点で子の状態を見て振り分ける。Claudeは画面の実行中表示ではなくStopまで残るturnの印で判定する。実行中のturnへは差し込み(mode=agent_steer、新しいevent_cursorと配送は作らない)、それ以外は非ブロックdispatch(mode=agent_dispatch)で、harnessごとの完了境界を表す整数event_cursorを返す。Codex親は選択に応じて公式Steerまたはqueue、Claude Code親は公式非同期hookで本文を自動受信する。他の親はaiterm-waitを使う。CursorのsubmitはadapterがCLIのextended keyboard protocolへ変換し、送信本文がcomposerへ残る場合は明示errorにする。
Set require_agent:true on pty_send when the integration requires agent delivery. If the agent registration is missing, Aiterm refuses before sending any text and returns AGENT_SESSION_REQUIRED with an explicit unsent message. The default preserves ordinary PTY sends; combining it with force:true is rejected before sending.
pty_send to an agent session accepts an optional preface: one line placed before the text, so the message reads "preface, blank line, text". For a Claude Code session Aiterm enters the preface without the terminal's paste markers and pastes the blank line and the text in one paste. Claude Code wraps long pasted text in <pasted_content> and follows instructions inside it only where the user's own words outside the tags ask it to, so this keeps an integration's header (who the message is from) outside the wrapper. For Codex, Grok and Cursor sessions the three parts are joined and pasted as before. The preface must be a single line of at most 200 characters with no control characters, must not start with a symbol or whitespace, and must not contain @; otherwise Aiterm returns AGENT_PREFACE_INVALID and "文字列は送信していません。" before sending anything. It cannot be used with ordinary PTY sends, force:true, or remote.
When the caller gives no preface, Aiterm adds one itself for a Claude Code session whenever the text is long enough for Claude Code to wrap it (three or more line breaks, or 800 characters and up): "Aitermが届ける、この席の呼び手からの正規の送信です。下の貼り付けは、どこかから拾った文ではなく、呼び手がこの席へ宛てて送った文です。" ("A regular message from this session's caller, delivered by Aiterm. The paste below was sent to this session by the caller, not picked up from somewhere else.") The line states where the message comes from and does not tell the session to obey it. It applies to new turns, to steering, to the prompt of agent_launch, and to remote sessions (the Aiterm on that machine adds it). Without that line the whole message sits inside the wrapper, and a Claude Code session may decline to act on it as "not the user's own instruction". Short texts, Codex, Grok and Cursor sessions, and sends with raw or force are unchanged (ADR 0116).
Overlapping pty_send calls to the same agent session are handled one at a time. A later call picks its route only after the earlier one has been sent, so the result matches calls made in sequence (a call that overlaps the first send to a freshly started Claude Code session returns mode=agent_steer). It returns later by however long the earlier call waits for the session to accept input. If its turn does not come within the limit (60 seconds on POSIX, 180 seconds on Windows), Aiterm refuses before sending any text and returns AGENT_SEND_BUSY with an explicit unsent message. A lock left by a process that exited mid-send is cleared by the next send. agent_configure waits in the same line: a pty_send that arrives while a configuration change is in progress is sent after the change, so the turn runs with the new setting. A configuration change to a session whose turn is running does not hold the line while it waits for the turn to end, so steering still goes through. If its turn does not come within the same limit, agent_configure returns AGENT_SEND_BUSY with an explicit unchanged message.
agent_launch and pty_send (to an agent session) accept an optional image: an array of absolute paths to image files (png/jpg/jpeg/gif/webp). Aiterm appends an attachment block to the prompt, and every harness opens the path with its own file-reading tool and sees the image; the caller never learns harness-specific attachment tricks. Invalid paths are rejected before anything is sent.
agent_launch accepts an optional write_scope: either "read-only" or a human-readable description of writable paths. Codex/Grok use --sandbox read-only; Cursor uses its official read-only --mode ask. A path description remains declaration-only because these CLI launch surfaces provide no equivalent path allowlist flag.
Grokの無人起動は公式--trustで指定された作業フォルダを信頼登録し、確認画面を完了してから初回promptを送る。この登録はGrok CLIの信頼ストアへ保存され、フォルダ内のhook・MCP・LSPにも適用される。read-only sandboxの制限は維持する。画面に残る完了済みhookの結果は実行中と判定しない。
Grokで終了済みターンのweekly-limitパネルが残っている場合、次の通常pty_sendがShift+Xで一度閉じ、入力受付を確認して今回の本文を送る。同じsessionと会話を保ち、receiptのpane_input_recoveryにgrok_rate_limit_dialog_dismissedを記録する。ターン未終了・harness不在はGROK_RATE_LIMIT_RECOVERY_BLOCKED、解除後の入力受付失敗はGROK_RATE_LIMIT_RECOVERY_FAILEDとなり、本文は未送信。上限の継続はrate_limitedとして返し、過去promptは再送しない。Grokの上限観測には現在の画面だけを使う。Claude Codeの上限は現在の画面の入力欄の下に出る知らせで、Codexの上限はturnを終えた記録(task_completeのcodex_error_info: "usage_limit_exceeded")で見分ける。pane logや道具の出力に残る上限の文字では判定しない。
When a Cursor pre-submit hook (beforeSubmitPrompt, or a Claude Code UserPromptSubmit hook that Cursor loads for compatibility) rejects the prompt, Cursor drops it and no turn or completion follows. Aiterm recognizes the rejection: an initial prompt returns initial_prompt=failed, and pty_send returns an error instead of a success receipt, both with USER_HOOK_BLOCKED and the hook's output. A rejection that comes after the 3-second start check is reported by the completion wait as outcome=error (aiterm-wait exit 7).
Grokがread-only sandboxの適用を拒否した場合、prompt送信時にGROK_SANDBOX_STARTUP_FAILEDとCLIの原因を返す。hookパスのシンボリックリンクなど、CLIが示した原因を設定の管理元で修正し、対象sessionをpty_closeして起動し直す。Aitermはsandboxを解除したりhookをコピーしたりしない。
この判定はGrok専用アダプターが所有する。初回prompt付きのagent_launchと通常のpty_sendで、入力受付待ち中に拒否を検出すると未送信のエラーを返す。promptなし・trust_project指定なしの起動応答は入力受付を保証しない。trust_project:trueでは入力受付まで確認し、startup.statusを返す。Grokのprivacy notice起動設定も同アダプターが所有する。実装の責務分担はDESIGNを参照。
Codex 0.155.1の「Approaching rate limits」model切替dialogは、通常のpty_sendとagent_configureで同じsessionのまま一時的な2. Keep current modelだけを選ぶ。入力受付を再確認してから本文または設定変更を進め、dispatch receiptのpane_input_recoveryにはcodex_rate_limit_model_switch_kept_currentを記録する。model切替と今後の表示抑止は選ばない。入力受付へ戻らなければCODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILEDとなり、本文・設定変更は未送信。このdialogはagent_approvalの対象ではなく、inspectはreason="rate_limit_model_switch"だけを返し、prompt digestとchoicesを出さない。
For a correlated Claude turn stopped at Do you want to proceed?, use claude_approval(action: "inspect", ...) to capture the active operation and SHA-256 screen digest, review the displayed command, then call respond with that exact digest and either approve_once or deny. The relay rechecks the operation and screen under the send lock, never exposes arbitrary input or permanent approval, keeps the active marker intact, and records a prompt-free owner-only receipt. pty_send(force: true) does not bypass this boundary.
agent_launch({ harness: "codex-cli", session_name: "codex1", cwd: "/repo",
prompt: "port test/legacy.py to vitest",
model: "gpt-5.6-sol", reasoning_effort: "high",
write_scope: "test/ only; no commit" })
→ { session_id: "codex1", … } # Codex now live in a persistent terminal
pty_read("codex1", { screen: true }) → read what it's doing (token-reduced)
pty_send("codex1", "also fix the imports it broke")
→ non-blocking dispatch; receipt carries event_cursor
# Codex/Claude Code親には回答が自動で届く。それ以外の親:
$ aiterm-wait --session codex1 --cursor <event_cursor> # never in the parent's foreground; exit 0=done, 3=timeout (not done), 4=closed, 7=error (turn aborted by an API error)
pty_read("codex1", { agent_transcript: true }) → collect the full answer
The canonical harness choices are:
harness | Launches | Notes |
|---|---|---|
claude-code | Claude Code CLI | Claude model and effort controls; correlated Stop hook |
codex-cli | Codex CLI | OpenAI model and effort controls; durable rollout completion |
grok-cli | Grok Build CLI | Grok model selected with model; live catalog check |
cursor-cli | Cursor Agent CLI | GPT, Claude, Grok, or another Cursor catalog model; normal transcript completion |
A new terminal gets the environment of the MCP process that opened it (since 0.50.0, ADR 0081).
Values from another caller that happened to start the tmux server first no longer appear in it.
AITERM_SESSION_ID and AITERM_AGENT_* are not inherited; aiterm sets them per terminal. If a harness
passes only a few variables to its MCP process, the terminal has only those (Codex passes HOME, PATH,
SHELL, and TERM by default; forward more with env_vars under [mcp_servers.aiterm]).
env_vars is an allowlist of environment-variable names, not a name/value map. At launch,
aiterm reads each valid name from its current MCP process, shell-quotes present values, and places
them on that one harness launch command. Missing names are omitted; invalid shell variable names
fail before session creation. Only the named values can be read back with env_keys in pty_list.
Values do not enter the MCP tool arguments, but they are delivered through the
PTY launch command and retained in aiterm's per-session .lastcmd; the launched harness and other
processes with access to the same OS user may read them. Use this for non-secret seat identity and
workflow variables, not as a secret transport.
The selected harness CLI must be installed and authenticated. Aiterm resolves CLAUDE_BIN / CODEX_BIN / GROK_BIN / CURSOR_AGENT_BIN, then the documented default binary, then PATH. Cursor resolution deliberately uses cursor-agent, never the ambiguous agent name. Claude and Cursor authentication are checked before a PTY exists, so a failed preflight leaves no session. All harnesses use their normal harness-owned credential and configuration stores in place.
For Grok, Aiterm does not lock, inspect, or modify the credential. A non-empty inherited
GROK_AUTH_PATH must be absolute and exist; Aiterm passes it unchanged to Grok. Grok owns its
contents, permissions, and link handling. Absence of the default auth file is accepted only when
XAI_API_KEY is set.
ポータブルフォークは任意です。throughline_source_session が指定されている場合、prompt は必須の新しいミッションとなり、launch_operation_id と併用することはできません。aiterm は THROUGHLINE_BIN、次に PATH を使って Throughline を解決し、throughline handoff-context --session <id> --json を実行して、返されたコンテキストを固定セパレーターとミッションの前に配置します。この方法には throughline >= 0.9.0 が必要です。throughline_supplement_file には Throughline 0.10.8 以降が必要です。Aiterm はファイルを読み取ったり解釈したりせず、--supplement-file <path> を追加します。プロジェクトへのバインド、検証、共有コンテキストの予算は Throughline が管理します。この方法では、データベースのセッション所有権を変更せずにソースメモリーを読み取ります。このフィールドを省略した場合、Throughline への依存は不要です。
ハーネスアダプターは model と reasoning_effort を各 CLI の公開設定に変換します。明示的に指定された Grok モデルは grok models に対して検証されます。Cursor では、gpt-5.6-luna などのベースモデルと high などの別個の推論レベルを組み合わせ、生成された現在のカタログ ID を検証し、セッション内の変更には Cursor 標準のモデルピッカーを使用します。モデルが見つからない場合はエラーとなり、キャッシュ、再試行、フォールバックはありません。Claude は起動時に限った Stop フック設定のみを追加し、Codex は通常のロールアウトストアを読み取り、Grok は通常のセッションイベント/履歴を読み取り、Cursor は起動 ID を使って通常のエージェントトランスクリプトにバインドします。絶対パスの cwd を渡してください。~ は展開されません。
エージェント間に隠れたプロトコルはありません。起動した各ハーネスは、ユーザーから見える永続的なターミナルセッションです。MCP クライアントは通常の PTY 操作でこの TUI を操作し、人間は接続して監視したり、操作を引き継いだりできます。
デモ
実際にキャプチャした出力です。以下の各ブロックは、このリポジトリで aiterm を使って実行したものです。数値、省略マーカー、すべての is_complete 判定はツール自体の出力であり、模擬したものではありません。角括弧で囲まれたメタ行は pty_read が追加するものです。実際の出力ではラベルは日本語ですが、ここでは読みやすさのために翻訳しています(日本語版 README には実際の表記がそのまま掲載されています)。
長い出力を先頭と末尾に折りたたんで表示しています。中間部分を省略したのは私ではなくリデューサーです(166 → 56 トークン)。
→ pty_send("demo", "seq 1 150")
→ pty_read("demo", { wait: true })
← 1
2
3
⋮ (head runs to line 29 — abbreviated in this README)
… ⟨102 lines elided · full=true, or line_range="A:B"⟩ … ← the tool's own marker
⋮ (tail resumes at line 132 — abbreviated in this README)
149
150
[aiterm demo: 51 lines / ~56 tok (raw 152 lines / ~166 tok); 102 lines hidden] [is_complete=True via quiescent]
grep の出力は、rtk の上限(合計200行、ファイルごとに25行)以内であれば、grep が出力した内容そのまま返されます。上限を超えると、コマンドごとのリデューサーがヒットをファイル別にまとめ、省略した内容を示します。
→ pty_send("demo", "grep -rn session src/")
→ pty_read("demo", { wait: true, rtk: true })
← 550 matches in 23 files:
src/agent-resolver.ts:211:// …(long lines keep ~80 chars around the pattern)
…
src/remote.ts:268:session_id: session, agent_transcript: true, raw: true,
+4 more in src/remote.ts
+7 more files
[aiterm demo: rtk:grep applied / ~4286 tok (raw ~12546 tok)] [is_complete=True via quiescent]
ネストは、入力として送るテキストだけで実現できます。ここでは同じ PTY 内で Python REPL を実行しています(ssh host、docker exec -it … bash、起動したコーディングエージェントの TUI もまったく同じようにネストされます)。
→ pty_send("demo", "python3")
→ pty_read("demo", { until: ">>>" }) # nested prompt = "the inner shell is ready"
→ pty_send("demo", "print(sum(range(1_000_000)))")
→ pty_read("demo", { wait: true, until: ">>>" })
← 499999500000 [is_complete=True via until]
上のキャプチャに加えた編集は、2行の ⋮(README 用に長い先頭/末尾の実行出力を短縮)と、表示幅に合わせて切り詰めた長すぎる grep の1行だけです。⟨…⟩ マーカー、トークン数、すべての is_complete 判定は、ツールが出力した内容そのままです。(末尾にスペースを付けず until: ">>>" を使ってください。キャプチャしたプロンプトはトリミングされるため、">>> " は一致せず、timeout に進みます。)ネスト中は、クワイエスンスが設計上発生しないため、until(内側のプロンプト)または mark: true を渡してください。完了検出と既知の制約(バグではなく設計上のもの)を参照してください。人間は同じマルチプレクサーバックエンドに attach して、これらの様子をリアルタイムで監視できます(人間が監視できますを参照)。
First run (≈60 seconds)
aiterm-setup --jsonがreadyになったら、利用するMCP clientを再起動して接続を確認する。Claude Codeの場合:
/mcp # aiterm should show as connected, exposing 18 tools
Your first session — four calls, one persistent terminal:
pty_open() → { session_id: "t1", attach: "<platform attach command>" }
pty_send("t1", "echo hello") → command sent into the PTY
pty_read("t1", { wait: true }) → "hello" (token-reduced, completion detected)
pty_close("t1") → terminal released
pty_close is idempotent and returns a structured closed / already_closed
receipt, so durable callers can retry the same session_id after losing the MCP response.
On Windows, closing a Claude Code session first asks Claude Code to exit on its own (Ctrl-C twice, then a wait of up to 4 seconds)
so that its SessionEnd hooks run; psmux stops a session without the hangup signal that tmux sends on POSIX. The session is stopped
afterwards either way.
That's it. The terminal in t1 is real and persistent — ssh, docker exec, a REPL, or a launched agent's TUI are just things that live inside it. To launch a worker agent instead, one call does it: agent_launch({ harness: "codex-cli" }) returns a session_id you drive with the same pty_read / pty_send.
Prefer a global install, or a different client?
# install globally, then register the command name
npm i -g aiterm-mcp
claude mcp add --scope user --transport stdio aiterm -- aiterm-mcp
This registers it in ~/.claude.json; you'll get an approval prompt the first time. For client-specific JSON, see Install in your MCP client.
Headless: no human at the terminal
Because an MCP client drives aiterm programmatically over stdio, everything above can run with nobody sitting at the terminal. Any MCP-capable orchestrator can call agent_launch — including a harness matching itself — then pty_read the result and act on it unattended. That makes aiterm a fit for exactly the places a human-driven terminal isn't:
- Multi-agent orchestration — an orchestrator hands sub-tasks to Claude Code / Codex / Grok / Cursor harnesses, each in its own persistent session, and reads them all back. Composer is one of the models Cursor selects (
composer-2.5-fast). - CI — a job step can spin up an agent, drive it, and tear it down.
- cron — a scheduled run can launch an agent and collect its output.
The terminal is real and shared, so a human can jump in (A human can watch) — but nothing requires one to.
How it works
flowchart LR
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_models · agent_configure · agent_auth · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 18 tools"]
S -->|"pty_read<br/>token-reduced"| AI
S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>survive restarts"]
P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
P -->|"launches a fresh PTY per agent"| A["another coding-agent harness<br/>Claude Code · Codex CLI · Grok CLI · Cursor CLI"]
One PTY is the only primitive. Everything else — SSH, containers, REPLs, and the launched agent TUIs — is just something interactive running inside a persistent terminal, driven with the same pty_send / pty_read. Each launcher opens its own fresh PTY. Because the PTYs live in tmux on POSIX or psmux on native Windows, sessions outlive the MCP server and the AI client.
組み込みシェルではなく、aiterm を使う場面
MCP クライアントにはすでにシェルツールがあり、得意な作業もあります。一方、aiterm が得意な作業もあります。このリポジトリ内の同じコマンドを両方で実行し、どちらも同じ方法(文字数 ÷ 4、aiterm 独自の推定方式)でトークン数を数えて測定したため、公平に比較できます。
軽い単発の作業では、まず組み込みツールを使いましょう。git log --oneline -5 は往復が 1 回ですが、aiterm では pty_send、続いて pty_read の 2 回となり、2 回目の往復にかかる時間は、軽いコマンドで節約できる時間を上回ります(約 7 秒に対し約 13 秒)。
出力が長くなる場合や、呼び出し後も状態を維持する必要がある場合は、2 回目の往復にかかるコストを回収できます。
| コマンド | 組み込みシェル | aiterm | 判定 |
|---|---|---|---|
git log --oneline -5 | 1 回の呼び出し、約 7 秒 | 2 回の呼び出し、約 13 秒 | シェル(往復が少ない) |
npm test(203 件のテスト) | 約 4,292 トークン | 約 607 トークン | aiterm(約 7.1 倍少ない。判定は維持) |
find node_modules -type f | 約 500 トークン¹ | 約 456 トークン | トークン数は同程度。aiterm は先頭と末尾を保持し、line_range も使えます |
grep -rn "session" src/ | 約 2,989 トークン | 約 1,096 トークン | aiterm(約 2.7 倍。長い行は切り詰められます²) |
記録した 203 件のテストのベンチマークでは、削減は実際に確認でき、安全です。組み込みツールでは、223 行のログ全体がコンテキストに入り、約 4,292 トークンになります。aiterm では、実行結果を独自に取り込んだ内容を約 607 トークンにまとめます。
[aiterm demo: 51 行 / ~607 tok (raw 223 行 / ~4292 tok); 172 行 hidden] [is_complete=True via mark]
行 = 行数。メタ行は aiterm の実際の出力から一字一句引用しています。
モデルに届くトークン数は約 7.1 倍少なく、まとめた後も判定に必要な情報は残ります。末尾には、ℹ tests 203 / ℹ pass 203 / ℹ fail 0 がそのまま含まれています。ログを開いた目的の行は残しつつ、余分な情報を省けます。実時間はほぼ同じなので、これほど長い実行では、追加の往復にかかる時間は全体のごく一部です。
aiterm は呼び出しをまたいで状態も保持します。組み込みツールでは呼び出しごとに新しいシェルが起動するため、呼び出しの間で cwd がリセットされ、環境も引き継がれません。cd /tmp && export BENCH_VAR=hello123 を送信してから、2 回目の別の呼び出しで値を読み取ります。
built-in shell → var= # empty; env dropped, cwd back at project root
aiterm → cwd=/tmp var=hello123 # one persistent PTY holds both
cd してから環境変数を設定してビルドする場合、ssh を一度実行して認証済みセッションで 10 個のコマンドを実行する場合、ライブ REPL や起動したエージェントの TUI を順番に操作する場合など、永続的な PTY があればすべてを保持できます。ターミナルに状態を覚えておいてほしいときは、aiterm を使いましょう。
¹ 現在のハーネスは約 192 KB のダンプをファイルに自動で退避し、約 2 KB の先頭部分だけをプレビューするため、トークン数はほぼ同じです。aiterm は正確な行数を報告し、後から line_range="A:B" で任意の範囲(先頭でも末尾でも)を取得できます。² rtk の grep リデューサーは、rtk の上限(200 行、ファイルあたり 25 行)に収まる間は grep の出力をそのまま返します。上限を超えるとファイルごとにまとめ、長い行はパターンの前後約 80 文字を残し、残りを +N more in <file> / +N more files にまとめます。大規模な検索ですべての行を省略せずに確認する必要がある場合は、組み込みツールを使ってください。
代替手段との比較
aitermは、ターミナルを操作するMCPサーバーと、「共有ターミナルを介してエージェント同士がやり取りする」という新しい考え方(aitermの位置づけを参照)の交差点に位置しています。それぞれの観点での比較を、他のツールの強みも含めて率直に示します。
| aiterm-mcp | ワンショットのシェルMCP (例: mcp-server-commands) | ターミナル/SSH/tmux MCP (例: iterm-mcp、ssh-mcp、tmux-mcp) | 共有tmuxによるエージェント間連携 (例: smux) | |
|---|---|---|---|---|
| セッションの永続性 | ✅ tmux/psmux。再起動後も維持 | ❌ 呼び出しごとに新しいシェル | ⚠️ ツールによる | ✅ tmux |
| SSH/コンテナー/REPL | 1回のpty_sendで接続をネスト | コマンドごとに再接続 | ⚠️ 多くの場合、別々のツール | ✅ tmux(人間が操作) |
| 1回の呼び出しで別のエージェントを起動 | ✅ agent_launch(harness=…) | ❌ | ❌ | ⚠️ エージェントはCLIとAgent Skillsを使って、人間が起動したtmuxに参加 |
| ヘッドレス(tmuxに人間がいない) | ✅ MCPでプログラムから操作 | ✅ | ⚠️ ツールによる | ❌ tmux内に人間がいることを前提に設計 |
| MCPネイティブ(任意のMCPクライアント) | ✅ claude mcp addを1行実行 | ✅ | ✅(MCPです) | ❌ tmuxの設定+CLI+Agent Skills |
| トークンを削減した読み取り | ✅ コマンドごとのリデューサー | ❌ 生の出力 | ⚠️ まれ | ❌ 生のtmux出力 |
| 完了検出 | 5層:終了コード/mark/until/静止状態/タイムアウト | 該当なし(呼び出しごとにブロック) | ⚠️ プロンプトとの照合。脆弱 | ❌ エージェントがペインを読み取る |
| 人間が一緒に操作可能 | ✅ 共有ソケット/名前空間(attach) | ❌ | ⚠️ ツールによる | ✅(中核となるモデル) |
aitermの位置づけ
「共有ターミナルを介してAI同士がやり取りする」という考え方は、独立したカテゴリになりつつあります。これは実に優れたアイデアです。ターミナルは、あらゆるコーディングエージェントがすでに使える汎用インターフェースなので、エージェント間の専用プロトコルは必要ありません。シェルそのものが共有の場になります。@shawn_panaが開発したsmuxは、人間がセットアップした共有tmux環境に、エージェントがtmux-bridge CLIとAgent Skillsを使って参加するという構成を、1つのコマンドで実現し、この考え方を広めました。開発者が介入する共有ペインのワークフローに強く、実際に多くの支持を得ています。
aitermも「ターミナルを出会いの場とする」という同じ核となる洞察に基づいていますが、意図的に次の3つの異なる選択をしています。
- 構造上、ヘッドレスです。 aitermはMCP経由でプログラムから操作できるため、AIはtmuxに人間がいない状態で、オーケストレーションループ、CIステップ、またはcronジョブから別のエージェントを起動し、操作できます。共有tmuxツールはキーボードを操作する人間を前提としており(ドキュメントでも対話的なペイン操作が中心です)、無人での運用は本来の用途ではありません。aitermは無人での運用を前提としています。
- ワークフローを新たに導入する必要のない、MCPネイティブな仕組みです。 aitermはstdio MCPサーバーです。
claude mcp addを1行実行するだけで、stdioに対応するあらゆるMCPクライアントで構造化ツールとして使えます(Claude Codeでテスト済みです。同じプロトコルに対応するCursor、Cline、Claude Desktopでも同様に動作するはずです)。tmux設定の導入や、ペイン操作の習得、セットアップへのスキルのインストールは必要ありません。クライアントはすでにツールの呼び出し方を把握しています。 - エージェントの起動は1回のツール呼び出しで完了する、オーケストレーションの基本機能です。
agent_launch({ harness: "codex-cli" })は永続ターミナル内でCodexを起動し、すぐに操作できるセッションを返します。ペインを配置したり、手作業でペイン間をコピー&ペーストしたりする必要はありません。起動、操作、読み取りのすべてを、オーケストレーションを担うモデルが自らツール呼び出しで行えます。
さらに、素のtmuxブリッジにはない、製品として整えられた機能も備えています。トークンを削減した読み取りと5層の完了検出です。だからといって、tmux内に人間がいるモデルが間違っているわけではありません。人間がどこにいるのかについて、別の、そして相補的な選択をしているのです。
ツール
セッションの監視と起動
pty_openは、POSIXではbash、WindowsではPowerShell 7をデフォルトで起動します。通常のターミナルとエージェントには、
AITERM_SESSION_IDが渡されます。ターミナルは、起動元のMCPプロセスの環境を引き継ぎます。セッションに登録する環境変数名を
env_varsで渡してください。
pty_list({ env_keys: ["JOB_OWNER"] })は、登録済みの非シークレット値だけをenvironmentで返します。未登録または存在しない値はnullです。
aiterm.pty-list-result.v1のレシートにはobserved_atとsessionsが含まれ、各エントリにはsession_id、
current_command、attached、width、height、harness、environmentが含まれます。既存のテキストも引き続き利用できます。
pty_observe({ session_id, cursor? })は、exists、observed_at、state
(busy/idle/blocked/dead/missing/unknown)、reason、pane_alive、harness_aliveを含むaiterm.pty-observe-result.v1を返します。pane_processとharness_processは別々のプロセス識別情報です。
process_identityは、エージェントの場合はハーネスを、通常のターミナルの場合は一意な子プロセスグループリーダーを選択し、子プロセスグループリーダーが存在しない場合はペインを使用します。識別情報にはpid、process_group_id、
started_identity、argv_digestが含まれます。解決できない識別情報はnullです。WindowsのPIDはネイティブ形式で、プロセスグループのフィールドはnullです。開始時刻の識別には、POSIXではLC_ALL=C ps lstart、Windowsではミリ秒単位のUTC ISO形式を使用します。argvダイジェストはSHA-256の16進数表記です。
activity.cursorを次の観測に渡すと、output_changedとcpu_delta_secondsを取得できます。初回の観測と再作成されたペインでは、差分はnullになります。cpu_secondsは、現在のサブツリーの累積CPU時間です。観測の間にプロセスが消失した場合、差分には観測できた増分のみが含まれ、cpu_delta_completeはfalseになります。
background_cpu_seconds、background_cpu_delta_seconds、background_cpu_delta_completeは、起動時のMCPプロセスを除き、ペインの起動から60秒以上後に作成された子孫プロセスのみを対象に、同じ測定を行います。token_hintは、最後に表示されたトークン数、またはnullです。呼び出し側で生のargvやペインテキストを解析する必要はありません。
エージェントセッションを閉じると失われるものを呼び出し側に知らせるフィールドが2つあります。activity.post_startup_process_countは、agent_launchの起動処理が完了した時点(最初のプロンプトの前)に存在していなかった、セッション内のプロセス数です。そのため、最初の1分以内に開始されたバックグラウンド処理もカウントされます。次の2種類のプロセスはカウントされません。Codex独自のcodex-code-mode-hostヘルパーと、mcp-lazyリレーの直接の子プロセス(初回使用時に起動するMCPサーバーと、その起動判定プロセス)です。これらの下で動作するプロセスはカウントされます。ハーネスが再起動した起動時プロセス(終了した起動時プロセスと同じ親および同じ引数を持つプロセス。再接続されたMCPサーバーなど)もカウントされません。また、Aiterm自体がプロセステーブルの読み取りに起動するプロセス(ps、またはWindowsでのPowerShellとそのコンソールホスト)もカウントされません。pending_child_deliveriesは、呼び出し側が誰であっても、親としてこのセッションが結果を待っているサブエージェントの数です。Aitermで判定できない場合(通常のターミナル、プロセス数の計測では0.48.0以前に起動されたエージェント、または解決できないハーネスプロセス)、どちらの値もnullになります。また、リモートホストで古いAitermが動作している場合は、フィールドが存在しないことがあります。nullまたはフィールドの欠落は、ゼロではなく不明として扱ってください。
Codex 親に回答を届ける必要がある別の製品は、独自のフックを登録する代わりに、aiterm-parent-delivery を使って Aiterm に依頼できます。
Aiterm に登録されたフックと公式キューが回答を届けます。codex verify --thread <uuid> は親を確認し、steer で設定を報告します
(enabled:フックが登録され、信頼されており、インストール後に親が起動している状態。disabled:公式キューのみを使用するため、ターン終了後にテキストが届く状態)。codex submit --thread <uuid> --delivery <uuid> --text-file <file|-> はテキストを一度だけ引き渡し、キューの受付 ID を返します。
codex state --thread <uuid> --delivery <uuid> は、テキストの到着方法を報告します(hook:"emitted" と turn_id:フックがそのターンにテキストを送信した状態。queued:公式キューにまだ残っているかどうか)。各結果は 1 行の JSON です。失敗時は {ok:false, code, message, outcome_unknown} が返され、終了コードは 1 です。再利用された配信 ID は PARENT_DELIVERY_DUPLICATE により拒否されます。サポート対象は Codex の親のみです。aiterm-setup はコマンドの場所を ~/.config/aiterm-mcp/delivery-provider.json に記録します。Node 製品では、たとえば submitCodexParentAnswerViaAiterm のように、aiterm-steer-delivery(0.3.0 以降)経由で呼び出せます。
Aiterm が、初回使用時にのみ MCP サーバーを起動するリレーの背後に登録されている場合、スリープ中のサーバーは、終了した所有者が持つ配信を引き継げません。aiterm-delivery-wake --parent <codex|claude|cursor> は、起動が必要かどうかを判定します。対象の親の種類に対する配信を、終了した所有者がまだ保持している場合は終了コード 0、それ以外は 1、不正な引数の場合は 2 で終了します。何も出力せず、子プロセスも起動しません。同じ確認を実行する複数のシートのうち 1 つだけがサーバーを起動するよう、配信にマークを付けます(誰も配信を引き継がない場合、30 秒後にマークが再取得されます)。mcp-lazy では、サーバーと同じ環境変数を設定して、起動判定コマンドとして渡します。
MCP_LAZY_WAKE_COMMAND='["/absolute/path/to/node","/absolute/path/to/aiterm-mcp/dist/delivery-wake-cli.js","--parent","claude"]'。
Linux では /proc を使って所有者の起動時刻を確認します。それ以外の環境では、既存の PID を持つ所有者を稼働中として扱います。
aiterm-setup は、同じサーバーを mcp-lazy リレーでラップしている登録を書き換えません。登録されたコマンドが mcp-lazy で始まる名前の実行可能ファイルで、その引数が直接登録のコマンドとその引数に続く場合(-- より前のリレーフラグは許容されます)、Claude Code、Codex、Grok、Cursor の登録は変更されません。ラップされたサーバーのパスが異なる場合は、セットアップによって直接登録が書き込まれます。
agent_launch({ harness, cwd, trust_project: true }) completes known workspace, project-hook, and project-MCP startup
consent even without a prompt, then verifies input readiness and harness liveness before returning startup.status="ready".
For Claude Code's first-run text-style menu, it confirms the item already selected on screen before continuing startup.
If the CLI then requests an account login method, the launch reports vendor_onboarding_required; complete that choice in the official interactive Claude Code CLI.
The input-readiness wait is 30 seconds. Only when it expires while the agent has not drawn anything yet (a busy host), the launch
waits up to 50 seconds from startup. If nothing is drawn by then, it still returns initial_prompt=not_sent and keeps the session.
A prompt-free launch without this option retains startup.status="not_checked". initial_prompt.status distinguishes
not_requested, not_sent, submitted_unconfirmed, and started. Failure responses retain structured session information.
WindowsのCodexもhook確認を認識し、npm shim経由の起動を一つのharnessとして識別する。
submitted_unconfirmed (the prompt left the composer, but the turn start was not observed within the confirmation window) is
not a tool error: isError is not set, the text carries an unconfirmed-start note, the session is alive and the parent
delivery stays registered. Do not resend an unconfirmed prompt or relaunch the agent; observe with pty_observe or wait
using its returned cursor.
For a live Codex approval, inspect with agent_approval({ action: "inspect", session_id }), review prompt and choices,
then respond with observed_prompt_digest and approval_choice (approve_once or deny). Unknown or changed dialogs return
status="blocked" and isError:true without sending input. Permanent approval is not exposed. Correlated Claude approvals
continue to use claude_approval.
| Tool | Role | Key args |
|---|---|---|
pty_open | Open one terminal and return a session_id | name?, shell?, env_vars? |
pty_send | Send text. On an agent session Aiterm picks the route when it sends: if the child's turn is running, it steers the text into that turn (agent_steer); otherwise it is a non-blocking dispatch returning an event_cursor (agent_dispatch). Steering fails when Grok does not queue the text or Cursor leaves it in the composer | session_id, text, enter=true, mark, force, require_agent=false, preface, rtk, raw |
pty_read | Read output, token-reduced (incremental by default) | session_id, wait, until, until_regex, timeout, screen, full, lines, line_range, raw, rtk, agent_transcript, operation_id |
pty_key | Send a control key | session_id, key (C-c/Enter/Up…) |
pty_close | Close idempotently; return closed / already_closed | session_id |
pty_list | Text and structured session list (each agent session carries its harness conversation ID as vendor_session_id), with explicitly requested non-secret environment values | env_keys? |
pty_observe | Pane/harness liveness, native process identity, state, and activity | session_id, cursor? |
agent_launch | Canonical agent launch; harness and model are independent | harness, prompt?, model?, reasoning_effort?, fast?, cwd?, write_scope?, trust_project?, env_vars?, throughline_source_session?, throughline_supplement_file? |
agent_auth | 公式CLIの認証を開始・確認・取消し、公式URL・device code・入力待ちを返す | harness, action, session_id?, cwd?, env_vars?, relogin? |
agent_models | List the models and reasoning efforts a harness offers now, read from that harness's own catalog without sending a prompt | harness, cwd?, include_hidden? |
agent_approval | Inspect a Codex approval and submit a one-time approval or denial | action, session_id, approval_choice?, observed_prompt_digest? |
claude_agent / codex_agent / grok_agent | Deprecated compatibility aliases (composer_agent was removed in 0.41.0; run Composer with agent_launch on cursor-cli) | legacy launcher arguments |
agent_configure | Change model/effort/fast in a running Claude, Codex, Grok, or Cursor session without restarting it | session_id, model?, reasoning_effort?, fast? |
claude_turn | Issue (dispatch-only) or recover one correlated Claude operation | action, session_id, operation_id, text? |
claude_approval | Inspect or answer the current correlated Claude approval prompt | action, session_id, operation_id?, approval_choice?, observed_prompt_digest? |
diagnostics | Read-only factory readiness as machine-readable JSON | (none) |
diagnostics never starts a PTY or agent. It reports package version, MCP call readiness, a read-only PTY-list summary, bounded runtime-error-store status, and optional vendor-launcher availability. It deliberately excludes paths, environment values, credentials, command text, PTY output, and raw logs; normal unset optional dependencies are not_applicable, while an indeterminate probe is unverified.
The result carries two text items. The first is the factory JSON described above (aiterm-mcp.factory-diagnostics.v1), whose fields are fixed. The second reports the parent-delivery hooks (aiterm-mcp.parent-delivery-diagnostics.v1): a status (ready / setup_required / not_applicable / unverified) and reason_code for Claude Code and Cursor, plus caller_status for the calling client. When caller_status is setup_required, agent dispatch from that client is rejected; run aiterm-setup. Hook status is not folded into overall in the first item.
Local runtime error snapshot
snapshotのproduct_versionは各recordの最終実発生時の版を表す。store v2は旧v1を読み取り、単発記録の版を保持し、複数回の旧集約の版はunknownにする。読取りでは状態JSONを書き戻さず、次のロック内更新でv2を保存する。consumerを先に更新し、旧writerの終了後に新writerを使う。v2保存後の旧版への切替は、製品のバックアップ復元を伴う。
aiterm-runtime-errors snapshot exposes a machine-readable, product-owned local snapshot for the dotagents factory adapter. Collection is fail-closed unless the canonical dotagents factory-reporter config is schema-exact, its host profile matches the executing OS, and it contains the JSON boolean collection.enabled: true; reporting fields are schema-validated but endpoints and credential files are never contacted, and the store performs no network I/O. The only accepted observations are three fixed codes owned by the core boundary (PTY dependency, persistence, and optional vendor launcher). Stored data is limited to fixed templates and aggregate metadata (SHA-256 fingerprint, count, first/last seen, status, and monotonic sequence); exceptions, stderr/stdout, stacks, prompts, terminal/transcript/event bodies, paths, and arbitrary context cannot enter the API. Persisted JSON is revalidated with exact top/record fields and a recomputed fingerprint before explicit DTO projection.
Consumer flow is aiterm-runtime-errors snapshot, then aiterm-runtime-errors ack --cursor N after durable ingestion. Operators can use resolve|reopen --fingerprint SHA256. MCP collection and diagnostic reads run in timeout-bounded child processes, so a FIFO or stalled filesystem cannot block terminal work; child failure emits only the fixed store diagnostic. Store mutation uses a bounded bakery ticket queue: every waiter owns a never-reused ticket containing PID, process-start identity, and an owner token, so dead owners are removed by unique filename without fixed-path reclaim ABA. The queue deadline measures lack of progress by the same head owner, not total wait behind healthy predecessors; normal polling uses the native process-liveness check and validates process-start identity only when a blocker stalls. Worker deadlines use forced termination so a SIGTERM-ignoring child cannot mutate state after timeout. POSIX state is atomically replaced under $XDG_STATE_HOME/aiterm-mcp/ (default ~/.local/state/aiterm-mcp/) with owner/mode rechecked on every read. Windows native uses %LOCALAPPDATA%\aiterm-mcp\; each DACL is rebuilt and read back as one non-inherited FullControl ACE for the current SID. Windows path/DACL/timeout behavior is covered by pure tests in this change; no new Windows integration success is claimed.
Reporting to BugHub is off by default. Aiterm sends runtime errors nowhere unless both hold: the user ran
aiterm-runtime-errors reporting enable, and a credential file placed by the BugHub owner exists
(~/.config/bughub/product-credentials/aiterm-mcp.json, or %LOCALAPPDATA%\bughub\product-credentials\aiterm-mcp.json on Windows;
it is read only when it is a regular file readable by its owner alone). The destination comes from that file. The payload is the
cumulative error codes, counts, first/last timestamps, versions, severity, and resolution marks; prompts, paths, and stacks are neither
stored nor sent. A separate process does the sending, never the MCP process, and there is no polling: a report is attempted when an error
is recorded, when a record is resolved or reopened, and when the MCP server starts (only while something is unreported, at most once per
hour), or on aiterm-runtime-errors report (once, at most once per minute). A record becomes acknowledged only after the response
signature is verified; otherwise the next attempt resends the then-current totals. aiterm-runtime-errors reporting status shows the
switch, the credential state, the unreported count, and the last attempt; reporting disable turns it off. Enabling reporting also
enables collection on that host.
Interactive agent harnesses
agent_launch starts a selected harness's interactive coding-agent TUI inside a fresh persistent PTY and returns its session_id. The harness owns the agent loop, authentication, hooks, session, and transcript; model is independent. The TUI is a full-screen app, so read it with pty_read({ screen: true }) for the rendered view.
agent_configure({ session_id, model?, reasoning_effort?, fast? }) changes a running Claude, Codex, Grok, or Cursor TUI through the harness's standard controls, preserving the PTY and conversation context.
ファスト(fast)。 各harnessの、費用や利用量を多く払って応答を速くする切り替え(Claude Codeの/fast、Codexの速度の段、GrokとCursorのfast版のmodel)を、席ごとに切り替える(ADR 0114)。
- 席は必ず通常の速度で起きる。
agent_launchのfastを省いた時とfalseは同じで、利用者の設定にファストが残っていても通常で始まる。fast: trueは通常で起こしてから、その席を切り替える。 agent_configure({ session_id, fast })は、動いている席を起こし直さずに切り替える。fastだけでも渡せる。省略は、その席の今の状態を変えない。- 切り替えは席だけに留める。harnessの切り替えの命令は利用者の既定(
~/.claude/settings.jsonのfastMode、~/.codex/config.tomlのservice_tier、~/.grok/config.tomlの[models])も書き換えるので、Aitermが切り替えの直前の値へ戻す。Grokは起動の--modelでも既定を書くので、起動の前後でも戻す。 - 返りの
fastは、切り替えた後の実際の状態。agent_modelsのmodelごとのfastが、そのmodelで切り替えられるかを示す(GrokとCursorのfast版は別のmodelとして並ばず、元のmodelのfast: trueになる)。最上位のfast_unavailableは、アカウントの都合でどのmodelでも使えない時の訳。 - 断りは文の先頭の符号で分かる:
FAST_MODE_NOT_SUPPORTED(そのmodelは対応していない)、FAST_MODE_UNAVAILABLE(アカウントや契約で使えない)、FAST_MODE_MODEL_CONFLICT(modelへfast版のIDを書いてfast: falseを渡した)、FAST_MODE_UNCONFIRMED(切り替えた後の状態を確かめられない)。起動でFAST_MODE_UNAVAILABLE・FAST_MODE_UNCONFIRMEDになった時は、起動を断らずに通常の速度で起こし、返りをfast: false、fast_error: { code, message }にする。
Claude Codeの席では、/modelの選ぶ画面のs(use this session only)で替える。/model <名前>・/effort <値>は選んだ値を端末の~/.claude/settings.jsonへ新しい会話の既定として保存するので、打たない(ADR 0111)。その選び方が無い版、選ぶ画面に無いmodel、そのmodelで選べないeffortは、何も変えずに断る(CLAUDE_MODEL_SESSION_ONLY_UNAVAILABLE・CLAUDE_MODEL_NOT_IN_PICKER・CLAUDE_EFFORT_NOT_SELECTABLE)。席を閉じて立て直すと端末の既定へ戻るので、立て直す時はagent_launchへ同じmodel・reasoning_effortを渡す。
agent_auth({ harness, action:"start"|"status"|"cancel", session_id?, cwd?, env_vars?, relogin? })は、各harnessの公式CLIで認証を進める。Claudeはclaude auth login、CodexとGrokはlogin --device-auth、CursorはNO_OPEN_BROWSER=1 cursor-agent loginを使い、資格情報は各CLIだけが保存する。Aitermは資格情報を読取り・copy・編集せず、独自OAuthも実装しない。remoteは他toolと同じ標準対応。
startは、公式の状態が認証済みなら何も起こさずauthenticated(session_id:null)を返す。relogin:trueを付けると、認証済みに見えても公式ログインを開始し、session_id付きの結果を返す。別のアカウントへ入り直す時と、ログインの期限切れを状態から見抜けない時に使う。Aitermは資格情報を消さず、置き換えは公式CLIが行う。ただし、公式CLIがログインを始めた時点で元のログインを消す事がある(Codex 0.160.0のcodex login --device-authは、始めた時点でauth.jsonを消す。途中でcancelしても戻らない)。Claude CodeとCursorは、途中でcancelすれば元の資格情報が残る(無効な資格情報で確認)。
status(sessionなし)は、公式CLIに今の状態を聞く。
| harness | 聞き方 | 期限が切れたログイン |
|---|---|---|
| Codex | codex login statusが「Logged in」の時、公式App ServerのgetAuthStatus→account/read | blocked(accountがnull)。codex login statusだけでは「Logged in」のままで見抜けない |
| Claude Code | claude auth status --json | 見抜けない(公式の答えがloggedIn:trueのまま)。起動は通り、最初のturnが認証の誤りで終わる |
| Grok | grok models(状態の命令は無い) | blocked(You are not authenticated.)。Grokは使えない資格情報を自分で消す |
| Cursor | cursor-agent status | failed(Logged in (unable to fetch user details)。期限切れか通信できないかは区別できない)。relogin:trueで入り直す |
結果はaiterm.agent-auth-result.v1。statusはwaiting/authenticated/blocked/failed、session_id・url・user_code・input_required・messageを返す。startのsession IDを保存してstatusへ渡す。公式HTTPS URLと明示device codeだけを返し、CLIの生出力・token・OAuth callback codeを結果へ載せない。input_required:trueなら同じsessionのpty_read(screen:true)で公式画面を表示し、pty_send/pty_keyで人の入力を中継する。
Grokには公式認証status commandが無いため、session付きの確認は公式loginのexit 0を正本にし、session無しの確認はblockedを返す。他harnessは公式statusも照合する。Claudeは認証後の公式初回案内を同じsessionで進め、選択待ちはblocked/input_required:trueで返す。authenticatedは認証結果であり、agent_launchの起動準備完了は別途確認する。cancelは指定した認証sessionだけを閉じ、資格情報を削除しない。
PTYが消失した場合は、相関記録の有無にかかわらずstatusがfailed、cancelが既に終了・取消済みを示すblockedを返し、どちらもsession_id:nullとなる。保存したsession IDを解除してstartで再開始できる。生存中の通常PTYやharness不一致、記録の破損・読取り失敗はエラーを返す。
agent_models({ harness, cwd?, include_hidden? }) returns the model IDs and reasoning efforts the installed harness offers right now, so a UI can build its choices from the machine that actually runs the agents. It reads each harness's own catalog and never sends a prompt or starts a turn:
harness | Source | Notes |
|---|---|---|
codex-cli | App Server model/list | Per-model efforts and default effort. include_hidden: true also returns models Codex hides. |
claude-code | stream-json initialize control request (the same models the Agent SDK's supportedModels() returns) | Hooks and MCP servers are disabled and the session is not persisted. ultracode is added to models that support effort and reported in adapter_efforts, because Claude Code accepts --effort ultracode but the catalog does not report it. |
grok-cli | grok agent stdio initialize (_meta.modelState) | Per-model efforts; no session is created. |
cursor-cli | cursor-agent models | Split into base model IDs and the efforts Aiterm can append as <model>-<effort>. -fast variants and IDs with an embedded effort are not offered; pass such a full ID as model without an effort. |
The result (aiterm.agent-models.v1) is { harness, source, harness_version, default_model, efforts, adapter_efforts, models: [{ id, display_name, efforts, default_effort, hidden }] }. Every id and effort can be passed to agent_launch and agent_configure as is; efforts at the top is the union across models. An unavailable catalog is MODEL_CATALOG_UNAVAILABLE and a malformed one is MODEL_CATALOG_INVALID; Aiterm never falls back to another list. With remote, the catalog comes from the harness on that host.
{ "name": "agent_models", "arguments": { "harness": "grok-cli" } }
harness | Launches | Model behavior |
|---|---|---|
claude-code | Claude Code CLI | Claude catalog model; native effort controls |
codex-cli | Codex CLI | OpenAI catalog model; native effort controls |
grok-cli | Grok Build CLI | Grok catalog model |
cursor-cli | Cursor Agent CLI | Cursor catalog model, including GPT/Claude/Grok; effort uses model parameter override |
The selected harness CLI must be installed and authenticated. Use each product owner's official installer and updater; Aiterm does not distribute alternate CLI tarballs. For Cursor Agent CLI, use curl https://cursor.com/install -fsS | bash on macOS/Linux/WSL or irm 'https://cursor.com/install?win32=true' | iex on native Windows, authenticate once with agent login, and update with agent update; Aiterm invokes the unambiguous cursor-agent binary. Missing binaries, invalid model/effort values, unavailable Grok catalog models, and nonexistent cwd fail before a session exists.
Set throughline_source_session together with a non-empty mission in prompt to prepend
Throughline's read-only handoff context. This optional route requires throughline >= 0.9.0,
cannot be combined with launch_operation_id, and leaves the source session's database ownership
unchanged. Optional throughline_supplement_file is passed unchanged to Throughline and requires
throughline_source_session and Throughline 0.10.8 or later; Aiterm does not read or classify the supplement. Throughline is resolved through THROUGHLINE_BIN and then PATH; a missing or invalid
export fails before the PTY exists instead of silently launching clean.
When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lines), callers recover it in full with pty_read({ agent_transcript: true }). It returns the most recently completed turn's final assistant message in plain text with no re-prompting. The existing human-readable content keeps its diagnostic suffix; machine callers read the answer alone from structuredContent.text in aiterm.pty-read-result.v1. Claude reads the bounded owner-only result captured by the launch-correlated Stop hook and verifies its digest/byte count; it never reads Claude's private transcript. Durable machine callers should use claude_turn: issue sends once, recover never sends, pending is distinct from unsafe or malformed state, and only completed carries the exact verified raw_output. Codex uses the normal rollout transcript's task_complete.turn_id; Grok returns the last non-empty assistant message after the last real user row, excluding tool-use preambles; Cursor uses the normal agent transcript bound to the launch ID and current turn. A turn that completed with an empty answer is not an error: text is the empty string and answer_empty is true, so callers can tell it apart from an answer that could not be read. Missing or ambiguous attribution, including an answer that cannot be located in the record, remains an explicit error.
Completion detection (5 layers)
For PowerShell over SSH, mark:true recognizes the current standard PS ...> prompt and emits PowerShell syntax even when Aiterm runs on macOS or Linux. A prompt left in earlier output is not used to select the syntax.
pty_read({ wait: true }) decides "is the command done?" via five layers: process exit / a mark:true sentinel / an until match / output quiescence with shell return / timeout. mark emits the shell's exit status on POSIX shells and 0 (success) or 1 (failure) on PowerShell; fish/csh/tcsh are rejected before send because they do not share either status syntax. When mark or until is active, that requested evidence takes precedence and a momentarily quiet shell cannot complete the read as quiescent. Agent sessions add a sixth exact layer: Codex observes normal rollout task_complete; Grok observes normal session turn_ended; Claude observes its additive launch-correlated Stop event; Cursor observes turn_ended(status:"success") in the launch-bound normal agent transcript. aiterm-wait --cursor performs that harness-specific observation without the parent blocking or polling. Pre-send readiness failures are MCP errors, and late completion remains recoverable without resending.
Completion push for parent agents (aiterm-wait)
Codex/Claude Code/Cursor親には子の回答本文が自動で届く。 Codex/Claude Codeでは、子を起動・dispatchした後は別作業へ進むか親のturnを終える。Aitermが完了を観測し、加工前の本文を保存して親へ渡す。waiter、pty_readによる回答回収、子への返送指示は不要。子は全対応harnessから選べる。
Codex/Claude Codeの自動配送ではreceiptにparent_deliveryが付き、wait_process/wait_commandはnullになる。pty_observeのparent_deliveriesでwaiting、ready、sending、submitted、failed、unknownを確認できる。submittedはCodexの公式受信口での受付またはClaudeのhookへの本文出力を示し、modelの読了ではない。MCP再接続後は未送信の記録を再開し、出力中断で結果が分からない場合は本文を保持してunknownとする。自動再送はしない。
For queue delivery, use a Codex runtime that supplies MCP _meta.threadId and the official thread/queue API (verified with Codex CLI 0.154.0). aiterm-setup checks the installed queue entry point; Aiterm verifies the requesting thread before each dispatch. Codex native sub-agents reject external queue input and cannot be automatic-delivery parents. Steer相当の選択時も公式キューへ投入し、専用hookが同一ターンへ取り込みます。
Claude Codeは2.1.259以上の対話sessionに対応する。aiterm-setupが専用のPreToolUse、PostToolUse、SessionEndを登録するため、Channelsの起動flagは不要。公式asyncRewake hookだけが裏で待ち、親はその間も次のturnへ進める。回答はStop hook feedbackとして届く。hookのexit 2は親の再開信号であり、子の成功・失敗は本文のoutcomeで区別する。
/clear等の会話終了後は未送信の旧回答を送らず、本文を保存する。受信hookの上限は24時間。hookの終了・出力失敗・無効化を成功扱いせず、別の待機経路へ黙って切り替えない。Claude Desktopのチャット、Web、agent_id付きの会話(--agent起動とnative subagent)はこの受信契約に含めない。
hookを持たない旧版、または0.52.2以前へ戻す時は、install前にaiterm-setup --remove-claude-parent-hooksを実行する。Aiterm専用hookだけを解除し、他製品のhookと設定は保持する。
Cursor parents (clientInfo.name of cursor-vscode) use the same completion capture. aiterm-setup adds afterMCPExecution and postToolUse to ~/.cursor/hooks.json and keeps every other hook and its position. A missing registration fails the dispatch with CURSOR_PARENT_HOOK_UNAVAILABLE before the child is sent. While the parent keeps calling tools, the answer is injected through additional_context on the next tool result. If the parent ends the turn, start the receipt wait_process in the background first; that receiver exits when the answer arrives. wait_command is null. submitted means the hook or the receiver claimed the text, not that the model has read it. No claim within 24 hours is failed, the text is kept, and nothing is resent. Remove only Aiterm's entries with aiterm-setup --remove-cursor-parent-hooks. Cursor Cloud Agents and Background Agents are outside this contract.
Claudeをリンク経由のcwdから起動した場合も、実体パスに対応する会話記録を参照する。
For other parent hosts, dispatch and start the receipt's waiter in a separate process:
- Launch the child with
agent_launch({ harness: ... }); every launch shares the normal project/user environment and adds only completion correlation plus lineage. Send a turn with plainpty_send(orclaude_turn issuefor durable Claude operations). The call returns immediately with anevent_cursorin its structured receipt. If the child is still working when you send, Aiterm steers the text into the running turn instead (mode: "agent_steer"); the original request's completion then covers it, so no new cursor or delivery is created. - Pass the receipt's
wait_process.executableandwait_process.argsunchanged to a true argv process API. PowerShell 7'sStart-Processis the exception because it joins-ArgumentListarrays; passwindows_start_process_argument_listas its one ready-made argument string instead. This invokes the bundled waiter through the exact Node runtime that is already running aiterm, including on native Windows where npm's human-facing bin is a PowerShell script shim and install paths may contain spaces.wait_commandremains a compatibility display string for humans. The waiter observes the harness-owned completion source, plus Claude's additive launch hook, as a pure reader and exits with a one-lineaiterm.agent-wait-result.v1receipt. Exit ≠ done: the receipt'soutcomeis authoritative (0=done,3=timeout,4=closed,1= error). - 親自身のforegroundでwaiterを実行しない。 receiptのprocess起動情報を、そのhostが持つバックグラウンドprocess APIへ渡す。親は別作業へ進むかturnを終え、process終了の通知で続行する。
- Collect the result exactly as before:
pty_read(agent_transcript: true), orclaude_turn recoverfor durable Claude operations. The waiter carries the signal, never the payload.
If your host has no completion push (no mechanism that re-invokes the agent when a background process exits), --timeout 0 is a one-shot check instead of a wait: it scans the event file once and returns running (exit 5) when the turn is still in flight, done (exit 0) when it finished, closed (exit 4) when the session is gone. It is deliberately absent from the receipts and tool descriptions — a host that does get pushed should be woken, not poll. An unknown session name is an error, never running, so a typo cannot masquerade as a child that is still working.
aiterm-wait takes no locks, never writes session state, and never dispatches — any number can run beside the MCP server and each other, and pty_close/concurrent sends are unaffected.
1 回の呼び出しで別のマシン上のエージェントを起動する(remote)
agent_launch に remote を追加すると、SSH 経由で接続した別のマシンの Aiterm 上で同じ起動処理を実行できます。マシンへの接続とエージェントの起動が 1 回の呼び出しにまとまり、ローカルの子エージェントと同じように、完了結果が親に届きます。Linux、macOS、Windows のマシンに同じタスクを並行して送信することを想定しています。
agent_launch({ "harness": "codex-cli", "remote": { "host": "rabbit" }, "cwd": "/home/kite/project", "prompt": "..." })
remoteでは、host、user、port、identity_file、passphraseまたはpassphrase_env、およびssh_options(Key=Value)を指定します。hostのみを指定した場合は、~/.ssh/configのエイリアスとして使用されます。Aiterm は接続先を保存または管理しません。接続先と使用する鍵は呼び出し元が管理します。- プレーンテキストの
passphraseは、呼び出し元の AI の会話ログに残ります。ssh-agent またはpassphrase_env(環境変数名)の使用をおすすめします。受け取ったパスフレーズは MCP プロセスのメモリ内にのみ保持され、SSH_ASKPASSを通じて ssh に渡されます。状態ファイルやログに書き込まれることはありません。 - リモートマシンには
aiterm-mcp、tmux(Windows では psmux)、およびハーネスの CLI が必要です。リモートシェルの種類(POSIX、PowerShell、または cmd)は初回接続時に検出されます。POSIX マシンでは、Aiterm はログインシェルから PATH のみを取得するため、~/.local/bin、Homebrew、または nvm 配下の CLI も見つかります。Windows では、ユーザーの PATH をそのまま使用してaiterm-mcpを起動します。 - 後続の
pty_send、pty_read、pty_close、pty_observeなどにも同じremoteを渡します。セッション名はリモートマシンに属し、同じ名前のローカルセッションと衝突することはありません。 - Codex、Claude Code、Cursor の親は、ローカルの子エージェントと同じように回答を自動で受け取ります。完了状況は
ssh <host> aiterm-waitで確認でき、SSH が切断された場合は同じカーソルから再接続します。それ以外の親では、ssh ベースのwait_processが使用されます。 - 同じ接続先への呼び出しでは、ControlMaster を介して SSH 接続を共有します。
remoteではimage添付ファイルとclaude_turn issueはまだサポートされていません。 - POSIX の SSH 制御ソケットには、TMPDIR が長くても短いプライベートディレクトリが使用されます。接続は状態ルートごとに分離されます。OpenSSH は、アイドル状態のマスター接続を 600 秒後に閉じ、そのソケットを削除します。
トークンの削減
pty_readはデフォルトで制御文字を取り除き、繰り返される行をまとめ、長い出力を先頭と末尾に折りたたみます(復元方法のヒントとメタ行が付加されます)。pty_read({ rtk: true })は、コマンドごとのリデューサー(git status/git log/grep/pytestなど)を使って、観測される出力をさらに縮小します。これはrtkバイナリを必要としない、自己完結型の再実装です。pty_send({ rtk: true })は、既知のコマンドを送信前にrtk形式に書き換えます。送信元にrtkがあれば、出力はその時点で削減されます(なければそのまま送信されます)。
入力と出力
pty_send はコマンドやプロンプトの意味を解釈せず、指定されたテキストをターミナルに送信します。デフォルトでは ESC とブラケット付きペーストの終端をサニタイズします。一方、pty_read は返される出力の制御文字を無害化します(raw: true にすると変更されません)。コマンドの実行許可は、シェル、リモートエンドポイント、または起動されたハーネスが管理します。
各 pty_send で送信できる UTF-8 テキストは最大 64 KiB です。同じセッションへの送信は aiterm プロセス間で直列化されるため、チャンクが混在することはありません。各 OS はマルチプレクサーを介して、UTF-8 セーフな 256 バイトのチャンクを 10 ms の排出間隔で貼り付けます。この境界を設けずに長い入力を送信すると、macOS、Linux、WSL2 では途中や末尾が無言で欠落することが確認されています。POSIX シェルまたは PowerShell がフォアグラウンドにあるときにサニタイズ済みの複数行テキストを送信すると、改行を含まない 1 つの入力としてエンコードされます(POSIX シェルでは eval、PowerShell ではドットソースされた UTF-8 Base64 デコード済みのスクリプトブロックを使用)。シェルは最初の行を実行する前にスクリプト全体を受け取るため、スクリプトの途中でページャーや REPL が起動しても、後続の行が対話型キー入力として消費されることはありません。単一行の入力、raw:true、およびシェル以外のフロントエンドでは、直接 PTY に貼り付けます。エージェントへのディスパッチでは、テキスト全体を一度だけ ESC[200~/201~ で囲み、同じチャンクサイズでストリーミングするため、エージェントの TUI には 1 回のペーストとして認識されます(画像パスが複数のペーストに分割されることはありません)。これにより、単語の途中でキーとして解釈されることによるプロンプトインジェクションや、送信内容の欠落を防ぎます。後続のチャンクで失敗した場合、aiterm は部分送信の状態を報告し、Enter キーは自動的に押しません。送信プロセスの終了後にロックが残っている場合は、送信をフェイルクローズします。pty_list で該当セッションを確認し、pty_close で閉じてから、同じセッション ID で再作成してください。すべてを強制終了する公開ツールはありません。
人間が確認できます
セッションは、POSIX では共有 tmux ソケット上に、ネイティブ Windows では共有 psmux 名前空間上にあります。pty_open と agent_launch が出力するアタッチ用の行を使うと、人間が同じターミナルに接続して介入できます。これには Claude/Codex/Grok/Cursor ハーネスのセッションも含まれます。POSIX では tmux -S … attach -t <id>、ネイティブ Windows では psmux -L <namespace> attach -t <id> を使用します。
要件
- Node.js >= 18
- tmux または psmux(プラットフォームのランタイム要件)
- macOS / Linux / WSL2 では tmux を直接実行します。macOS では
brew install tmuxでインストールしてください(macOS の標準環境には含まれていません)。MCP クライアントをターミナルではなく GUI から起動すると、Homebrew の bin(Apple Silicon では/opt/homebrew/bin、Intel では/usr/local/bin)がPATHに含まれない場合があります。aiterm はこれらの場所を自動検索します。または、AITERM_TMUX=/path/to/tmuxを設定して明示的に指定してください。 - ネイティブ Windows には tmux がないため、aiterm はインストールごとの
-L名前空間を使用して、tmux CLI 互換のネイティブなターミナル/セッションマルチプレクサーである psmux を操作します。psmux はシェルではありません。pty_openは既定で PowerShell 7(pwsh.exe)を使用し、Windows PowerShell 5.1、PowerShell 6、cmd.exeにはフォールバックしません。5.1 しかインストールされていない場合は、先に Microsoft の公式インストーラーまたはパッケージマネージャーを使用してください。psmux 3.3.8 以降をインストールしてください(3.3.8 は、psmux のpipe-paneのファイル出力、バイト単位で正確なpaste-bufferの転送、およびフォアグラウンドの#{pane_current_command}が、aiterm のキャプチャ/ディスパッチ処理で必要とされる動作をする最初のリリースです)。ハーネスランチャーが内部で使用する明示的な Bash シェルには、Git for Windows が引き続き必要です。System32 のbash.exeは WSL のランチャーであるため、意図的に使用しません。マルチプレクサー/Bash の解決先はAITERM_PSMUX/AITERM_BASHで上書きできます。他の製品は psmux に直接依存せず、Aiterm のパブリック API を通じて永続ターミナルを利用します。
- macOS / Linux / WSL2 では tmux を直接実行します。macOS では
- エージェントハーネスを使用する場合:選択した CLI を、製品の提供元が定める公式の方法でインストールし、認証してください。対象は
claude、codex、grok、または Cursor のcursor-agentです。Portable fork ではさらにthroughline >= 0.9.0が必要ですが、通常のクリーン起動では不要です。(PTY ツールのみを使用する場合は必要ありません。) - 任意:
rtkバイナリ(pty_sendのrtk: trueによる委譲で使用します。なくても問題なく動作します)
Known constraints (by design, not bugs)
- While nested (ssh / docker / REPL / a launched agent TUI), quiescence cannot fire by design, because the foreground command is no longer in the shell set (bash/sh/zsh/fish/dash). When nested with no
untiland nomark,pty_read({ wait: true })returns early asis_complete=False via nested(rather than burning the fulltimeout, since no signal can confirm completion there) with a note to passuntil(a literal substring by default;until_regex: truefor a regex) ormark: true(an exit-code sentinel, auto-detected) for a confirmed completion. For a full-screen agent TUI, read{ screen: true }once its output settles. is_complete=Falseis not a failure. It means "completion was not observed withintimeout." For long commands, raisetimeoutor useuntil/mark.- Agent harnesses run their real TUI; aiterm doesn't proxy the model API. The selected harness owns model choice, authentication, and behavior. There is no hidden inter-agent protocol; the MCP client drives the Claude/Codex/Grok/Cursor TUI with ordinary send/read operations.
pty_send({ rtk: true })is single-line only and needs the externalrtkbinary (passthrough without it). Thepty_read({ rtk: true })reducer, by contrast, is self-contained and rtk-independent.- The
pytestreducer matches rtk 0.50.0 on test counts andFAILURES-block formatting (locked by regression tests). It deliberately preserves the full failure reason on theFAILEDsummary lines (emitted under-ra/-rf), whereas rtk 0.50.0 truncates the reason at the first" - "— a readability choice, so those lines are intentionally not byte-identical to rtk. The[full output: …]recall-pointer line rtk appends on large output is not reproduced on the read side. - The
grepreducer matches rtk 0.50.0: within the caps it returns the output unchanged, and past a cap its grouped form is byte-identical tortk grepwithout the recall hints.git logkeeps every commit when you set a count (-n) or a range (A..B); otherwise it shows 10 and ends with[+N more commits]. Like rtk'snever_worse, a reducer whose result would cost more tokens than the output is skipped. - tmux is started with
-f /dev/null, so it does not read~/.tmux.conf(to keep behavior reproducible across machines). - All sessions share one multiplexer endpoint (
claude.sockon POSIX, one psmux namespace on native Windows). The platform'skill-servercommand removes them all.
Development
npm install
npm run build # tsc → dist/
npm test # build, then the node:test regression suite (requires tmux or psmux)
npm link # put `aiterm-mcp` on PATH locally
開発中は変更に直結する試験を先に実行する。GitHub Actionsは共通実装・CI自身・未分類の変更をMac・Linux・Windowsで検証し、
Windows固有だけの変更はLinuxとWindowsを選ぶ。版番号だけの変更はLinuxの配布情報・pack確認、文書だけなら文書検査を行う。
試験内容とOSの選択、週次・手動実行の範囲は公開手順に従う。
npm run release -- <version> syncs the version, commits, tags, and publishes the GitHub Release in
one command; tag-triggered npm publishing checks only that the tagged commit is on origin/main and does not
wait for another CI run. The native
Windows runner needs psmux ≥ 3.3.8 and Git for Windows on its PATH, and must run as an
interactive Windows user; NETWORK SERVICE lacks the per-user environment the pane shell and
harness CLIs rely on and is not a valid runner identity.
Logic lives in src/core.ts (tmux control, reduction, completion detection, safety, agent launch) and src/rtk.ts (per-command reducers); src/index.ts is the MCP surface. The current architecture is in docs/DESIGN.md, the release procedure is in docs/RELEASE.md, and prototype/python/ remains the reducer's historical porting source (the pytest and grep reducers are ported to match upstream rtk 0.50.0, except the deliberate FAILED-line difference noted above, and is locked by regression tests).
Try it
公開packageを導入して、検出したAIへ登録する。cloneやビルドは不要:
npm install -g aiterm-mcp@latest
aiterm-setup --json
If aiterm let your AI hand a task to another agent — or saved you a round-trip of tokens — star the repo. It's the cheapest way to help others find it.
- npm: https://www.npmjs.com/package/aiterm-mcp
- Issues / bug reports: https://github.com/kitepon/aiterm-mcp/issues
Shared agent environment
All harnesses use the caller's normal project and user environment. Aiterm does not copy, symlink, filter, or replace harness configuration, authentication, MCP, plugin, skill, permission, trust, memory, or history stores. Cleanup removes only aiterm-owned launch metadata and completion correlation files.
The ordinary environment comes from the MCP process that opened the terminal, not from whichever caller
started the persistent multiplexer server (since 0.50.0). Every harness still accepts
env_vars: ["NAME", ...]; those names are placed on the launch command and registered on the session.
License
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。