Aitermは、AIがローカルshell、SSH、container、REPL、別agentの対話TUIを、再接続可能な永続PTYとして 操作するstdio MCP serverである。install、session、state、schema、diagnostics、recovery、releaseは このrepositoryが所有し、外部の工場管理製品がなくても単独で動く。
aiterm-setupはglobal packageからだけ実行し、依存準備、公開MCP経由の端末実行、
検出したAIのユーザー設定への登録と読戻しを連続実行する。npm lifecycleでユーザー設定を変更しない。
共通の順序と結果はsrc/setup.ts、公式package managerとOS差はsrc/setup-platform.ts、
各AIの登録形式はsrc/setup-integrations.tsが所有する。既存の他サーバーは保持し、
Claude/CursorのJSONは参照先を原子的に更新して変更前backupを残す。Codex/Grokは公式CLIで登録・確認する。
各AIの読戻しは登録内容の確認であり、端末の実動作はその前の公開MCP試験で確認する。
失敗は理由付きJSONと非ゼロ終了で返す。対応外の自動導入と全AI未検出を成功扱いしない。
プリミティブはlocal PTYを1つ開き、text/keyを送り、画面を読み、閉じることだけである。 SSHやcontainerを別toolにせず、PTY内で実行するcommandとして扱う。sessionはPOSIXではtmux、 Windows nativeではpsmux 3.3.8以上に保存され、MCP serverやclientの再起動をまたぐ。
sanitize済みの複数行は、POSIX shellでもPowerShellでもscript全体を取り込んでから記述順に実行する。
src/tmux-runtime.tsが改行を持たない1回の入力へ適合させ、POSIXはeval、PowerShellは
UTF-8のBase64を復元したscriptblockのdot-sourceを使う。変数と作業場所は現在のshellに残す。
生LFをPowerShellへキー入力として流さないため、WindowsのCtrl+Enterによる行順の反転を防ぐ。
単一行、raw:true、非shell前面は直接PTYへ送る。enter:falseでは後続のEnterまで実行しない。
pty_readは制御文字除去、反復圧縮、head+tail、command別reducerでcontext量を減らす。
完了はprocess exit、shell sentinel、literal/regex until、shell復帰を伴うquiescence、timeoutを区別する。
要求されたsentinel/untilを静止判定より優先し、nested shellで証拠がない状態を完了へ丸めない。
sentinelの方言は端末runtimeが実効shellから決める。SSH先の現在の標準PowerShell promptを
検出した場合は、Aiterm hostのOSに関係なくPowerShell構文を使う。過去のpromptでは切り替えない。
pty_listはtextと構造化したsession一覧を返す。env_keysは帰属等の非秘密キーの明示照会だけで、
psmuxが出力した余分な環境値を返さない。通常PTYとagentへAITERM_SESSION_IDを注入し、
env_varsの継承とsessionへの登録もAitermが所有する。古いtmuxは子へのenv注入とsession登録を使う。
pty_observeは存在、pane/harnessの生存、画面状態と理由、native process identityを分ける。
PIDは開始識別子・argv digestと組にし、paneとharnessを同一視しない。特定できないidentityはnull。
同じlaunchに属するnpm shimとnative本体は、中間の非候補processも含めた祖先関係から一つの起動として扱う。
祖先を共有しない候補は別々に残し、複数候補を一つと推測しない。
POSIXの停止状態はOSのprocess表から取得し、SIGSTOP中は残画面より優先してblocked/harness_stoppedを返す。
画面本文とargv本文は返さず、活動cursorには画面digestとprocess別CPUだけを持たせる。
初回とpane再作成後の差分はnull。区間中にprocessが消えた時は観測できたCPU増分だけを返し、
cpu_delta_complete=falseを付ける。background活動はpane開始から60秒以降に生成された子孫だけを数える。
token_hintは画面の直近表示であり、usageの累積正本ではない。
標準入口agent_launchはclaude-code、codex-cli、grok-cli、cursor-cliのharnessとmodelを別軸で選ぶ。
harnessがagent loop、認証、hook、session、transcript、model catalogを所有する。Aitermは通常の
project/user環境を置換せず、launch相関と完了回収に必要なstateだけを加える。
Throughlineの補足記憶はpathを透過搬送するだけで、内容、project束縛、context予算はThroughlineが所有する。
agent turnは常に非ブロックdispatchであり、receiptのevent_cursorがturn境界になる。
Codex親にはAitermのMCP processが完了を観測し、選択設定に応じて公式Steerまたはqueueへ本文を自動配送する。
Claude Code親は公式の非同期hookで本文を受け取り、待機中も新しいturnへ進める。
それ以外の親にはwait_processがplatform nativeな別process起動情報を返す。
waiterは純readerで、親のforeground turnを塞がない。
回答はharness所有transcriptから同じturnへ相関して回収し、欠落・曖昧・timeout時にpromptを再送しない。
Grokの記録先はCLIと同じOS絶対パスへcwdを正規化して導出し、完了通知と回答で同じ関数を使う。
配送用のGrok回答はturn_ended.tsから同じturnのturn_started.turn_numberを取得し、
chat_history.jsonlのuser.prompt_indexと相関する。次turnが既に始まっていても対象回答だけを回収する。
agent sessionへの送信口はpty_sendだけとする。子の状態は呼び出し側に選ばせず、Aitermが送る時点の画面で振り分ける。
状態を見てから呼ぶまでの間に子のturnが変わるため、呼び出し側が入口を選ぶ形では外れる。
実行中なら現在のturnへ追加textを差し込み、新しい完了境界と配送は作らない。完了境界は差し込み後も1つに保つ。
Claude Codeはtool処理中に画面の実行中表示が消え、Stop hookの実行中には表示が残る。turnの印を実行中判定の正本とする。Stopが発火しないAPIエラー終了では、次の送信時に印の作成後の会話記録にあるisApiErrorMessageを確認し、終了したturnの印だけを解除する。過去のエラーで新しい印を解除しない。waiterは印を変更せず、読取専用のままエラーを返す。
それ以外は新しいturnとしてdispatchする。
CodexとClaude Codeは次のtool境界で同じturnへ取り込む。Cursorは「follow-ups」枠へ入った文を「enter steer」で現在turnへ移し、turn_endedは最後に1回書く。
Grokは待ち行列へ入れた後に「send now」を押す。旧turnはcancelled(cancellation_context.trigger=send_now)で閉じ、
新turnが作業を継ぐので、完了判定はこの継ぎ目を完了と数えない。Grokが待ち行列へ入れない時とCursorの入力欄に本文が残る時はsteeredを返さない。
Cursorのsubmitはadapterがextended keyboard protocolのEnterへ変換し、呼び出し側は通常のdispatchだけを使う。
起動直後のClaude sessionへの初回dispatchは、他harnessと同じくTUIの入力受付を確認してから貼付とEnterを送る。
src/parent-delivery.tsが依頼の送信前に宛先と完了境界を保存し、完了観測、加工前の回答保存、配送を所有する。
宛先はCodexのMCP handshakeと各要求の_meta.threadIdから取得する。modelが指定したIDや環境変数で代用しない。
単品導入のsrc/codex-parent-receiver.tsは同じCODEX_HOMEの公式app-serverへstdioで接続し、
thread/readとthread/queue/listで宛先を確認してからthread/queue/addへ本文をJSONで渡す。
Steerを選択したmacOS・Windowsでも配送の入口は公式キューとする。
src/codex-parent-hooks.tsの同期PostToolUseがAitermの回答を同じターンの文脈へ渡し、
Stopが最終応答の生成中に届いた回答で同じターンを継続する。
取り込まれていない回答は公式キューに残り、親がidleになった時に通常配送される。終了後の再開には約10秒かかる場合がある。
親を別processでload/resumeせず、modelや権限のoverrideを渡さない。native sub-agentは自動配送の親にしない。
src/codex-hook-state.tsは同じCodex home・thread・配送UUID・本文hashへの所有記録を保持する。
キューの全ページを読んでから所有分を取り出し、他の利用者入力には触れない。
同時hookの取得は本人専用ディレクトリ内の排他的hard linkで一つに決め、公式の削除結果がtrueの本文だけを出力する。
削除中はprocess開始識別子を記録する。中断と出力失敗はunknownで本文を残し、自動再送しない。
parent_deliveriesはその状態とCODEX_HOOK_DELIVERY_UNCONFIRMEDを表示する。
emittedはhookへの出力完了でありmodelの読了ではない。公式キューが先に通常配送した入力の所有記録は次のhookで整理する。
aiterm-setup --codex-steer enable|disable|statusはsrc/setup-codex-hooks.tsが所有する。
CODEX_HOME/hooks.jsonの他の登録を保持し、専用の同期hookを追加する。
再導入ではAitermの既存の登録位置を保ち、内容が同じならhook設定を変更しない。
hookコマンドはCodexが使うshellで評価される。Windowsでは引用したPowerShell 7の実行パスを
呼出し演算子&で起動し、標準入出力と終了コードを保持する。回答の取得・配送処理はOS間で共通とする。
公式hooks/listから得た2件のkeyとhashだけを公式config/batchWriteで承認し、再読して有効・承認済みであることを確認する。
承認省略flagを恒久設定へ書かない。利用者の別hookを承認しない。
選択と配送の所有記録は~/.config/aiterm-mcp/codex-parent-hooks/に置く。
Nodeはsrc/setup-node.tsでHomebrewの同じformulaのoptへ正規化し、更新で消えるCellar実体を保存しない。
新規の起動差し替えは作らない。旧中継の設定は新しいhookの確認後に従来の解除処理で復元する。
macOSの専用LaunchAgent、Windowsのユーザー環境変数、所有外の値の保持は旧adapterが担う。
旧中継の互換読取りと移行用コードは残し、setupを実行するまでは旧設定の配送を維持する。
移行が中断した場合は保存済みの所有情報を使って次のsetupで継続し、未完了をreadyにしない。
有効化前から動いている公式processのPIDと開始識別子を保存し、それが残っている間はrestart_requiredとする。
readyは公式APIによるhook登録・承認の読戻しと、更新前processの終了を確認した状態である。
Codexの通常起動はAitermのNode、module、socketに依存しない。
Windowsでは公式Desktopが展開した実行ファイルを照合して配送用の公式APIへ接続する。Desktop更新後はsetupで再検出する。
LinuxのSteer選択はunsupportedとし、単品のキュー配送は全対応OSで維持する。
parent_deliveryは配送IDと状態を返し、自動配送時のwait_process/wait_commandはnullとなる。
submittedは公式キューの受付またはhook出力の完了を示す。キューIDはSteer相当の選択時も保持する。
長いStop継続入力はCodex自身のhook処理で抜粋と全文ファイルへの参照になる場合がある。
次の依頼へ進める前に前回の回答を保存し、harness所有記録を後の回答と取り違えない。
Codexの配送記録と本文はAiterm stateのparent-deliveriesへ保存する。ownerのPIDと開始識別子で生存を判定し、
再接続後は終了したownerの記録だけを原子的に引き継ぐ。waitingは同じ境界から観測を再開し、
readyは保存した本文を送る。送信中断はunknownとして本文を残し、自動再送しない。
受信口が明示拒否した場合はfailed、子の異常終了はそのoutcomeを配送する。
このstateは既存のPTY/harness stateと独立し、旧版は配送を再開しない。
aiterm-setupはClaude Code 2.1.259以上を確認し、ユーザー設定へAiterm専用のPreToolUse、
PostToolUse、SessionEndを追加する。他製品のhookと設定は保持し、disableAllHooksの解除は行わない。
hookはNodeの実行ファイルと引数配列で直接起動し、shellやWindowsのnpm shimを介さない。
PreToolUseのtool_use_id/session_idとMCP要求の_meta["claudecode/toolUseId"]を照合する。
起動時のsession環境変数は/clearで古くなるため宛先に使わない。hookがない場合とagent_id付きの会話は
子への送信前に明示errorにする。親がIDや待機方法を引数で指定する必要はない。
本文の保存と同じ子への連続依頼の制御はparent-delivery.tsを共有する。Claude用記録は
claude-parent-deliveriesへ分け、旧版のCodex readerに未知のparentを読ませない。
子の予約は既存のparent-deliveries/claimsで共有し、親の種類をまたぐ並行送信を防ぐ。
hookとの受け渡しはAiterm stateのclaude-parent-hooksへ置く。
PostToolUseはasyncRewake:trueで待機し、保存済み本文をstderrへ出してexit 2を返す。
exit 2はClaudeが規定する再開信号であり、子の成功・失敗は本文のoutcomeで区別する。
親は待機中も別作業や次のturnへ進める。Claudeの画面ではStop hook feedbackとして届く。
submittedはhookへの本文出力を確認した状態であり、modelの読了を示さない。
hook出力の切断・中断はfailedまたはunknownとし、本文を残して自動再送しない。
SessionEndはその会話の未送信の依頼を終了させる。/clear後の新しい会話へ古い回答を出さず、
確定本文は保存する。受信hookのtimeoutは24時間であり、timeoutとprocess終了は配送失敗として観測する。
CLIを終了した後に自動再開するdaemon、Channelsの有効化flag、子の返送コマンドは使わない。
hookを持たない旧版へ戻す時は、install前にaiterm-setup --remove-claude-parent-hooksで専用hookだけを解除する。
Claude Desktopのチャット、Web、agent_id付きの会話(--agentで選んだ主会話とnative subagent)は
この受信契約の対象に含めない。
対応対象は公式command hookとMCP metadataを提供するClaude Codeの対話sessionである。
Claudeの起動metadataには指定cwdの実体パスを保存する。Claude Codeが実体パスから作るproject slugと APIエラー監視の参照先を一致させ、監視中のリンク変更で保存場所を取り違えない。
aiterm-setupは~/.cursor/hooks.jsonへAiterm専用のafterMCPExecutionとpostToolUseを追加する。
他製品のhookと順序は保持し、commandにcursor-parent-hook.jsを含むentryだけを更新する。
親の識別はMCP initializeのclientInfo.nameがcursor-vscode(またはその後ろに空白を挟む派生名)であることだけで、
会話IDはMCPの_metaに来ない。hook未登録は子への送信前にCURSOR_PARENT_HOOK_UNAVAILABLEで止め、waiterへ切り替えない。
afterMCPExecutionまたはdispatch toolのpostToolUseが、tool返りのparent_delivery.delivery_idとhook入力の
conversation_idを結ぶ。delivery_idはstructuredContentか、contentのtextをJSONとして読んだ中から取る。
完了観測と本文保存はparent-delivery.tsが所有し、記録はcursor-parent-deliveriesへ分ける。子の予約claimsは共有する。
親が次のツールを呼ぶとpostToolUseが未受領の本文をadditional_contextで会話へ差し込む。
親がターンを終えている場合は、receiptのwait_processで起動したcursor-parent-receiveが本文をstdoutへ出して終了する。
wait_commandはnull。受け取りはclaim.jsonの排他作成で一つに決め、hookと受け口の両方へ本文を出さない。
submittedはどちらかがclaimした状態であり、modelの読了ではない。24時間以内にclaimが無ければfailedとし、
本文は残して自動再送しない。Cursor Cloud Agent/Background Agentはこの受信契約に含めない。
hookを外す時はaiterm-setup --remove-cursor-parent-hooksで専用entryだけを解除する。
trust_project:trueは対象projectの既知のworkspace、hooks、MCP初期同意を起動準備として進める意図である。
Claude Codeの初回テーマ選択は選択済みの項目を確定し、後続の起動準備へ進める。
ログイン方式の選択はユーザーのアカウント設定として扱い、composerと誤認せずvendor_onboarding_requiredで止める。
promptなしでも入力受付とharness生存を確認してstartup.readyを返す。指定なしのpromptなし起動は
従来どおりstartup.not_checkedで返す。初手receiptは未要求・未送信・送信済み未確認・開始確認を分ける。
開始の証拠は送信後の実行表示、実行中の既知承認、または同じcursor以降の完了だけとし、残存なしでは代用しない。
Codexの設定エラー等でCLIが終了した場合は、残った画面へ送らず未送信で止める。
agent_approvalはCodexの現在のcommand/MCP承認を検査し、launch IDを含むdigestと単発の選択へ束縛する。
応答はsend lock内で再観測し、変更・未知・取得失敗では入力しない。Claudeの既存claude_approvalは維持する。
src/remote.tsが所有する。remote付きの呼び出しは、ssh <host>で現地のaiterm-mcpを起動し、
同じtoolをMCPのままSSHのstdioへ中継する。sshdが起動するshellの系統は、どのshellでも実行できる
echo aiterm-probe %OS% $PSHOMEの展開結果で最初に見分け、MCP processの間だけ覚える。cmdは%OS%を、
PowerShellは$PSHOMEを展開し、POSIX系はどちらも展開しない。POSIX系では利用者のログインshellから
envのPATHだけを受け取り、処理は/bin/shで行う。profileの出力やfish等の文法差をMCPのstdoutへ持ち込まない。
WindowsはユーザーのPATHでaiterm-mcpとaiterm-waitをそのまま呼ぶ。起動、hook、transcript、multiplexerは現地のAitermが所有し、
呼び出し側は結果を返すだけにする。現地にポートは開けない。
接続先はtool引数で毎回受け取り、Aitermは一覧も既定値も持たない(オーナー裁定 2026-09-26。管理まで持つと重くなるため)。
配送記録には、パスフレーズ本文を除いた接続情報だけを残す。平文のパスフレーズはMCP processのメモリにだけ置き、
SSH_ASKPASSでsshへ渡す。受け取っていない時はBatchModeで止め、端末での対話入力はしない。
同じ接続先への呼び出しはControlMasterで1本のSSHに相乗りする。
自動配送はParentDeliveryManagerを別インスタンスで使い、記録をremote-付きの保存場所へ分ける。
旧版のreaderはboundary.remoteを知らないため、同じ保存場所に置くと旧processの照会が壊れる。
子の予約と照会の単位はdeliveryKeyで、接続先のhashを前置きして、この端末の同名sessionと衝突させない。
完了は現地のaiterm-waitで観測し、ssh自身の失敗(exit 255)では同じcursorで観測し直す。
現地のaiterm-waitが失敗を返した場合はつなぎ直さずに失敗とする。回答は現地のpty_read(agent_transcript:true, raw:true)から取り、
完了と別のturnの本文は配送しない。起動と送信は現地で済んだ後に配送を登録するため、送信前に前回の本文を確保する。
MCP schema (index)
-> PTY/共通進行 (core)
-> harness固有 adapter (harnesses)
-> 相関state (agent-shared/state-root)
-> multiplexer/OS adapter (tmux-runtime/agent-resolver)
harness固有のready、auth、catalog、transcriptはsrc/harnesses/、OSとmultiplexer差は
src/tmux-runtime.ts/src/agent-resolver.ts/src/process-runtime.ts、共通進行はsrc/core.tsに置く。
stdio stdoutはJSON-RPC専用とし、diagnostic logを混ぜない。
Aitermはtransport、schema、turn相関だけを検証する。command/prompt本文の意味を分類して拒否せず、 harness所有credentialの内容・権限・linkも検査しない。command policyとcredential policyは、実行する shell、接続先、各harnessの公式CLIが所有する。
入力が64KiBを超える、送信lockが残る、harnessがblocking UIにいる、model catalogが一致しない等の
境界失敗は明示errorにする。retry、別model、別harness、別backendへ自動fallbackしない。
stale send lockは並行processとのABAを避けるため自動削除せず、公開APIでは対象sessionをpty_closeして
同じIDで再作成する。全session一括停止は公開しない。
Grokのread-only sandbox起動拒否は、src/harnesses/grok.tsの
assertGrokSandboxNotRejectedがCLIのエラー表示から検出する。src/core.tsの共通入力受付待機は
Grokの場合だけこの判定を呼び、GROK_SANDBOX_STARTUP_FAILEDで原因と未送信を返す。
初回prompt付き起動と通常dispatchに適用され、他harnessの入力受付判定には適用しない。
trust_project指定なしのpromptなし起動応答はPTYへの起動要求を示し、入力受付の確認は後続の送信時に行う。
hookパスのシンボリックリンク等を拒否する判断はGrok CLIが所有する。AitermはCLIが出した拒否を伝え、
hookのコピー、設定の置換、sandboxの解除は行わない。原因を設定の管理元で修正した後、対象sessionを
閉じて起動し直す。検出の回帰試験はtest/grok-startup.test.mjsに置く。
Grokのmanaged起動は公式--trustを渡し、指定cwdの信頼状態はGrok CLIが管理する。
grokLaunchBlockingDialogは信頼確認を入力受付から除外し、scrollbackのshell promptを取り違えない。
grokTuiBusyは応答中の表示だけを実行中の根拠にし、完了後も残る[hooks: 成功/失敗]を含めない。
これらのCLI固有判定はsrc/harnesses/grok.tsが所有し、共通処理は判定を呼び出す。
Grokの終了済みerrorターンにweekly-limit質問カードが残る場合、通常dispatchだけが
現在のviewportを読み、harness生存と最新turnのエラー完了を確かめてXを一回送る。
既存の入力受付待機を通した後に完了cursorを取得し、今回の本文だけを送る。
実施した解除は既存receiptのpane_input_recoveryにgrok_rate_limit_dialog_dismissedとして載る。
解除条件の不成立はGROK_RATE_LIMIT_RECOVERY_BLOCKED、解除後の入力受付失敗は
GROK_RATE_LIMIT_RECOVERY_FAILEDで未送信を返す。読取・観測・設定・steerはこの解除を行わない。
grokRateLimitDialogが見出しと操作footerの組を所有し、過去logや後続UIのあるカードは採用しない。
privacy案内は現在の枠付きcomposerとmodel footerが見える場合だけ入力受付を妨げない。
完了観測は成功eventを優先し、今回のerror eventと現在のカードが揃えばturn情報付きのrate_limitedを返す。
新turnの完了前に古いlogだけで上限を返さない。購入・再認証・過去prompt再送・定期再試行は行わない。
画面判定と模擬CLIの実PTY試験はtest/grok-rate-limit.test.mjsに置く。
- macOS/Linux/WSL2: tmux。
- Windows native: psmux 3.3.8以上、PowerShell 7、harness内部用Git for Windows。
- Windows PowerShell 5.1、PowerShell 6、
cmd.exe、WSL bridgeへfallbackしない。 - multiplexer serverをまたぐ入力はUTF-8安全な256-byte chunkとdrainで直列化する。
diagnosticsはread-onlyで、PTY backendとagent dependencyを検査する。runtime error aggregateは
製品所有のlocal stateに固定codeと集約metadataだけを保存し、network I/Oを持たない。工場reporterとの
連携は明示opt-inの任意adapterであり、未設定時もAiterm本体は単独動作する。raw error、prompt、出力、
transcript、path、credentialを保存・公開しない。
公開schema、完了境界、state ownership、platform backendを変える時は、原因の最小再現、focused test、
関連ADR、日英README、CHANGELOGを同じ変更で同期する。現行制御から外れた完了・棄却・中断・失効・置換済みの設計planはcurrentへ残さずarchiveへ移す。
旧設計draftはarchive/01_design-plan.mdに保存する。