aiterm-mcp 0.39.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/README.ja.md +7 -8
- package/README.md +9 -10
- package/dist/core.js +28 -13
- package/dist/harnesses/grok.js +1 -1
- package/dist/index.js +34 -34
- package/dist/update-cli.js +2 -1
- package/dist/update.js +18 -3
- package/docs/DESIGN.md +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.40.0] - 2026-09-27
|
|
11
|
+
|
|
12
|
+
### 変更(互換性なし)
|
|
13
|
+
|
|
14
|
+
- `agent_steer`を廃止し、agent sessionへの送信を`pty_send`に一本化する。子のturnが実行中かどうかは呼び出し側に選ばせず、Aitermが送る時点の画面で見て、実行中なら現在のturnへ差し込み(`mode=agent_steer`)、それ以外は新しいturnとしてdispatchする(`mode=agent_dispatch`)。これまでは状態を知らない呼び出し側が入口を選ぶ必要があり、idleの子への`agent_steer`は何も送らず`idle`を返し、実行中の子への`pty_send`はCodex/Grok/Cursorで入力受付を30秒待って失敗、Claude Codeでは来ない完了を待つ新しいturnとして記録していた。差し込みでは新しい`event_cursor`と回答配送を作らず、完了は元の依頼へ1回だけ届く。公開toolは17になる。
|
|
15
|
+
- Claude Codeは、Aitermが送ったturnの印(Stop hookで消える)が残っている時だけ実行中と数える。Stop hookの実行中は画面が実行中の表示のままなので、回答直後に送った文を差し込みと取り違え、誰も完了を待たない新しいturnにしていた。
|
|
16
|
+
|
|
17
|
+
## [0.39.1] - 2026-09-26
|
|
18
|
+
|
|
19
|
+
### 修正
|
|
20
|
+
|
|
21
|
+
- `aiterm-update --host`が別端末の結果を読めず、更新に成功しても`remote_update_failed`を返していた。現地の`aiterm-update`が返す全体の結果から、現地自身の1件を取り出す。
|
|
22
|
+
- npmのbinをPATHへ通さず`aiterm-mcp`だけをリンクしている端末では、別端末の更新で導入した`aiterm-update`を呼べなかった。PATHに無ければ`aiterm-mcp`の実体と同じpackageの`update-cli.js`を使う。
|
|
23
|
+
|
|
10
24
|
## [0.39.0] - 2026-09-26
|
|
11
25
|
|
|
12
26
|
### 追加
|
|
@@ -1771,7 +1785,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
1771
1785
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
1772
1786
|
provenance.
|
|
1773
1787
|
|
|
1774
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.
|
|
1788
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.0...HEAD
|
|
1789
|
+
[0.40.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.39.1...v0.40.0
|
|
1790
|
+
[0.39.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.39.0...v0.39.1
|
|
1775
1791
|
[0.39.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.38.2...v0.39.0
|
|
1776
1792
|
[0.38.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.38.1...v0.38.2
|
|
1777
1793
|
[0.38.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.38.0...v0.38.1
|
package/README.ja.md
CHANGED
|
@@ -143,7 +143,7 @@ diagnostics、recovery、update、releaseを所有します。このREADMEと[
|
|
|
143
143
|
|
|
144
144
|
**言葉でなく実測で:** 記録済み203テストのベンチマークでは、`pty_read` はコンテキストに載るトークンを生ログの **約 7.1 分の 1** に減らす。しかも pass/fail の判定は畳んでも残る。→ [組み込みシェルツールとの使い分け](#組み込みシェルツールとの使い分け)
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
17ツール: 7つのPTYツール、正規のagent起動入口`agent_launch`、移行用の旧4alias、`agent_configure`、`agent_approval`、`claude_turn`、`claude_approval`、`diagnostics`。backendはPOSIXのtmux/Windows nativeのpsmuxなので、MCPサーバやAIクライアントが再起動してもsessionは生き残る。
|
|
147
147
|
|
|
148
148
|
**v0.28.0では実行基盤harnessとmodelを分離した。** harnessはagent loop・認証・hook・session・transcriptを所有し、modelはその上で選ぶ。Cursor Agent CLIでGPT/Claude/Grokを選んでも完了契約はCursor方式のまま。Composerは別harnessではなく、`harness:"grok-cli", model:"grok-composer-2.5-fast"`で表す。旧4起動ツールは同じ実装へ流れる互換alias。
|
|
149
149
|
|
|
@@ -202,7 +202,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
|
|
|
202
202
|
場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
|
|
203
203
|
Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
|
|
204
204
|
|
|
205
|
-
**状態:** 開発継続中 · 現行公開版 **v0.
|
|
205
|
+
**状態:** 開発継続中 · 現行公開版 **v0.40.0** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
206
206
|
|
|
207
207
|
### 更新と巻き戻し
|
|
208
208
|
|
|
@@ -248,9 +248,9 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
|
|
|
248
248
|
|
|
249
249
|
同じprimitiveが別エージェントのTUIを宿す。`agent_launch`の`harness`はagent loop・認証・hook・session・transcriptを所有する実行基盤、`model`は独立した選択。起動processは直接CLIと同じproject/user環境を使い、通常config、MCP、plugin、skill、permission、trust、memory、historyをcopy・filter・置換しない。
|
|
250
250
|
|
|
251
|
-
`aiterm.agent-launch-result.v1`は正規`harness`を返し、旧`provider`は互換fieldとして残す。同じ`harness`はagent dispatch、`aiterm-wait`、`agent_configure`、`pty_list`のagent行にも載り、旧vendor/provider/agent fieldは互換用に残る。Codexは通常rollout、Grok CLIは通常session event、Claudeはlaunch固有Stop hook、Cursorは通常agent transcript末尾の`turn_ended
|
|
251
|
+
`aiterm.agent-launch-result.v1`は正規`harness`を返し、旧`provider`は互換fieldとして残す。同じ`harness`はagent dispatch、`aiterm-wait`、`agent_configure`、`pty_list`のagent行にも載り、旧vendor/provider/agent fieldは互換用に残る。Codexは通常rollout、Grok CLIは通常session event、Claudeはlaunch固有Stop hook、Cursorは通常agent transcript末尾の`turn_ended`を完了正本に使う。agent sessionへの送信は`pty_send`だけで行い、子の状態はAitermが送る時点の画面で見て振り分ける。子のturnが実行中なら各harness標準の操作で現在のturnへ差し込み(`mode=agent_steer`)、完了は差し込み後の作業の終わりに元の依頼へ1回だけ届く。新しい`event_cursor`と配送は作らない。それ以外は非ブロックdispatch(`mode=agent_dispatch`)で、vendor別完了境界を表すopaqueな整数`event_cursor`を返す。Codex親は選択に応じて公式Steerまたは受信キュー、Claude Code親は公式非同期hookで回答本文を自動受信する。それ以外の親は`aiterm-wait`を親のターンを塞がない別processで受ける。Cursorのsubmitキーはadapterが現行CLIのextended keyboard protocolへ変換する。送信textがCursorのcomposerへ残った場合は成功receiptを返さず失敗する。
|
|
252
252
|
|
|
253
|
-
`agent_launch`・`pty_send`(agent
|
|
253
|
+
`agent_launch`・`pty_send`(agent session宛て)は任意の`image`(画像ファイルの絶対パスの配列。png/jpg/jpeg/gif/webp)を受ける。aitermが本文末尾へ添付行を付け、どのharnessも自分のfile読取toolでそのpathを画像として開く。呼出し側はharness別の添付手順を覚えない。不正なpathは送信前に拒否する。
|
|
254
254
|
|
|
255
255
|
`agent_launch`は任意の`write_scope`も受ける。Codex/Grokのread-onlyは`--sandbox read-only`、Cursorは公式`--mode ask`で実効化する。path説明は同等CLI引数がないためdeclaration-only。
|
|
256
256
|
|
|
@@ -403,7 +403,7 @@ MCP クライアントが aiterm を stdio 越しにプログラムから駆動
|
|
|
403
403
|
|
|
404
404
|
```mermaid
|
|
405
405
|
flowchart LR
|
|
406
|
-
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch ·
|
|
406
|
+
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>旧launcher alias · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 17 tools"]
|
|
407
407
|
S -->|"pty_read<br/>token-reduced"| AI
|
|
408
408
|
S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>再起動を跨ぐ"]
|
|
409
409
|
P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
|
|
@@ -512,7 +512,7 @@ Claudeの相関済み承認は既存の`claude_approval`を使う。
|
|
|
512
512
|
| ツール | 役割 | 主な引数 |
|
|
513
513
|
| --- | --- | --- |
|
|
514
514
|
| `pty_open` | 端末を1個開き`session_id`を返す | `name?`, `shell?`, `env_vars?` |
|
|
515
|
-
| `pty_send` | テキストを送る。agent session
|
|
515
|
+
| `pty_send` | テキストを送る。agent sessionでは子のturnが実行中なら現在のturnへ差し込み(`agent_steer`)、それ以外は非ブロックdispatchとして`event_cursor`を返す(`agent_dispatch`)。差し込みでGrokが待ち行列へ入れない時とCursorの入力欄に残った時は失敗する | `session_id`, `text`, `enter=true`, `mark`, `force`, `rtk`, `raw` |
|
|
516
516
|
| `pty_read` | 出力を削減して読む(既定は増分) | `session_id`, `wait`, `until`, `until_regex`, `timeout`, `screen`, `full`, `lines`, `line_range`, `raw`, `rtk`, `agent_transcript`, `operation_id` |
|
|
517
517
|
| `pty_key` | 制御キーを送る | `session_id`, `key`(`C-c`/`Enter`/`Up`…) |
|
|
518
518
|
| `pty_close` | 冪等に閉じ、`closed` / `already_closed`を返す | `session_id` |
|
|
@@ -520,7 +520,6 @@ Claudeの相関済み承認は既存の`claude_approval`を使う。
|
|
|
520
520
|
| `pty_observe` | pane/harnessの生存、native process identity、状態と活動 | `session_id`, `cursor?` |
|
|
521
521
|
| `agent_launch` | harnessとmodelを別軸で選ぶ正規agent起動入口 | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
|
|
522
522
|
| `agent_approval` | Codexの現在の承認を検査し、単発許可・拒否を送る | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
|
|
523
|
-
| `agent_steer` | 実行中のClaude/Codex/Grok/Cursor turnへ、各harness標準の操作でtextを差し込む。差し込み後の作業の完了は1回だけ届く。idleなら送信せず`idle`を返す。Grokが待ち行列へ入れない時とCursorの入力欄に残った時は`steered`を返さず失敗する | `session_id`, `text` |
|
|
524
523
|
| `claude_agent` / `codex_agent` / `grok_agent` / `composer_agent` | deprecated互換alias | 旧launcher引数 |
|
|
525
524
|
| `agent_configure` | 起動中のClaude/Codex/Grok/Composer/Cursorを再起動せずmodel/effort変更 | `session_id`, `model?`, `reasoning_effort?` |
|
|
526
525
|
| `claude_turn` | 相関済みClaude operationをdispatch(issue)または回収(recover) | `action`, `session_id`, `operation_id`, `text?` |
|
|
@@ -594,7 +593,7 @@ agent_launch({ "harness": "codex-cli", "remote": { "host": "rabbit" }, "cwd": "/
|
|
|
594
593
|
- `remote`は`host`、`user`、`port`、`identity_file`、`passphrase`または`passphrase_env`、`ssh_options`(`Key=Value`)を受け取る。`host`だけなら`~/.ssh/config`の接続名として使う。Aitermは接続先を保存・管理しない。どこへどの鍵で入るかは呼び出し側が持つ。
|
|
595
594
|
- 平文の`passphrase`は呼び出したAIの会話記録に残る。ssh-agentか`passphrase_env`(環境変数名)を推奨する。受け取ったパスフレーズはMCP processのメモリにだけ置き、`SSH_ASKPASS`でsshへ渡す。状態ファイルやログには書かない。
|
|
596
595
|
- 現地には`aiterm-mcp`、tmux(Windowsはpsmux)、使うharnessのCLIが要る。接続先のshell(POSIX系、PowerShell、cmd)は最初の接続で見分ける。POSIX系ではログインshellからPATHだけを受け取るので、`~/.local/bin`、Homebrew、nvmなどに置いたCLIも使える。WindowsはユーザーのPATHのまま`aiterm-mcp`を起動する。
|
|
597
|
-
- 以後の`pty_send`、`pty_read`、`pty_close`、`pty_observe
|
|
596
|
+
- 以後の`pty_send`、`pty_read`、`pty_close`、`pty_observe`などにも同じ`remote`を付ける。session名は現地のもので、この端末の同名sessionとは別に扱う。
|
|
598
597
|
- Codex/Claude Code/Cursor親には、この端末の子と同じく回答本文が自動で届く。完了は`ssh <host> aiterm-wait`で観測し、SSHが切れても同じcursorでつなぎ直す。それ以外の親には、sshを使う`wait_process`を返す。
|
|
599
598
|
- 同じ接続先への呼び出しはControlMasterで1本のSSHに相乗りする。`remote`付きの`image`添付と`claude_turn issue`は未対応。
|
|
600
599
|
|
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ Aiterm and is not a runtime dependency.
|
|
|
145
145
|
|
|
146
146
|
**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](#when-to-reach-for-it-vs-the-built-in-shell)
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
Seventeen 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; four deprecated launcher aliases kept for migration; `agent_configure`; `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.
|
|
149
149
|
|
|
150
150
|
**v0.28.0 separates the execution harness from the model.** The harness owns the agent loop, authentication, hooks, session, and transcript; `model` is what that harness runs. Cursor Agent CLI can therefore select GPT, Claude, or Grok without changing the completion contract from Cursor hooks to another harness's. Grok Composer is a Grok CLI model preset, not another harness: use `harness: "grok-cli", model: "grok-composer-2.5-fast"`. The old four launcher tools are thin compatibility aliases over the same implementation.
|
|
151
151
|
|
|
@@ -216,7 +216,7 @@ collection is off by default and performs no network I/O. It ships via
|
|
|
216
216
|
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
|
|
217
217
|
Release re-registers the Official MCP Registry entry.
|
|
218
218
|
|
|
219
|
-
**Status:** actively maintained · current public release **v0.
|
|
219
|
+
**Status:** actively maintained · current public release **v0.40.0** · runs on Linux · WSL2 · macOS · native Windows (tmux on POSIX, the tmux-CLI-compatible [psmux](https://github.com/psmux/psmux) on native Windows — no WSL required) · MIT · see the [CHANGELOG](CHANGELOG.md).
|
|
220
220
|
|
|
221
221
|
### Update and rollback
|
|
222
222
|
|
|
@@ -269,9 +269,9 @@ pty_read(id, { wait: true }) → read the token-reduced output, completion
|
|
|
269
269
|
|
|
270
270
|
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`.
|
|
271
271
|
|
|
272
|
-
起動結果には正規`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
|
|
272
|
+
起動結果には正規`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が送る時点で子の状態を見て振り分ける。実行中のturnへは差し込み(`mode=agent_steer`、新しい`event_cursor`と配送は作らない)、それ以外は非ブロックdispatch(`mode=agent_dispatch`)で、harnessごとの完了境界を表す整数`event_cursor`を返す。Codex親は選択に応じて公式Steerまたはqueue、Claude Code親は公式非同期hookで本文を自動受信する。他の親は[`aiterm-wait`](#completion-push-for-parent-agents-aiterm-wait)を使う。CursorのsubmitはadapterがCLIのextended keyboard protocolへ変換し、送信本文がcomposerへ残る場合は明示errorにする。
|
|
273
273
|
|
|
274
|
-
`agent_launch
|
|
274
|
+
`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.
|
|
275
275
|
|
|
276
276
|
`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.
|
|
277
277
|
|
|
@@ -391,7 +391,7 @@ The only edits to the captures above are the two `⋮` lines (a long head/tail r
|
|
|
391
391
|
`aiterm-setup --json`が`ready`になったら、利用するMCP clientを再起動して接続を確認する。Claude Codeの場合:
|
|
392
392
|
|
|
393
393
|
```bash
|
|
394
|
-
/mcp # aiterm should show as connected, exposing
|
|
394
|
+
/mcp # aiterm should show as connected, exposing 17 tools
|
|
395
395
|
```
|
|
396
396
|
|
|
397
397
|
Your first session — four calls, one persistent terminal:
|
|
@@ -432,7 +432,7 @@ The terminal is real and shared, so a human *can* jump in ([A human can watch](#
|
|
|
432
432
|
|
|
433
433
|
```mermaid
|
|
434
434
|
flowchart LR
|
|
435
|
-
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch ·
|
|
435
|
+
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 17 tools"]
|
|
436
436
|
S -->|"pty_read<br/>token-reduced"| AI
|
|
437
437
|
S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>survive restarts"]
|
|
438
438
|
P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
|
|
@@ -545,7 +545,7 @@ continue to use `claude_approval`.
|
|
|
545
545
|
| Tool | Role | Key args |
|
|
546
546
|
| --- | --- | --- |
|
|
547
547
|
| `pty_open` | Open one terminal and return a `session_id` | `name?`, `shell?`, `env_vars?` |
|
|
548
|
-
| `pty_send` | Send text
|
|
548
|
+
| `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`, `rtk`, `raw` |
|
|
549
549
|
| `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` |
|
|
550
550
|
| `pty_key` | Send a control key | `session_id`, `key` (`C-c`/`Enter`/`Up`…) |
|
|
551
551
|
| `pty_close` | Close idempotently; return `closed` / `already_closed` | `session_id` |
|
|
@@ -553,7 +553,6 @@ continue to use `claude_approval`.
|
|
|
553
553
|
| `pty_observe` | Pane/harness liveness, native process identity, state, and activity | `session_id`, `cursor?` |
|
|
554
554
|
| `agent_launch` | Canonical agent launch; harness and model are independent | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
|
|
555
555
|
| `agent_approval` | Inspect a Codex approval and submit a one-time approval or denial | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
|
|
556
|
-
| `agent_steer` | Inject text into the active Claude, Codex, Grok, or Cursor turn with each harness's own steering control, so the steered work still ends in one completion; return `idle` without sending when no turn is active, and fail instead of returning `steered` when Grok does not queue the text or Cursor leaves it in the composer | `session_id`, `text` |
|
|
557
556
|
| `claude_agent` / `codex_agent` / `grok_agent` / `composer_agent` | Deprecated compatibility aliases | legacy launcher arguments |
|
|
558
557
|
| `agent_configure` | Change model/effort in a running Claude, Codex, Grok, Composer, or Cursor session without restarting it | `session_id`, `model?`, `reasoning_effort?` |
|
|
559
558
|
| `claude_turn` | Issue (dispatch-only) or recover one correlated Claude operation | `action`, `session_id`, `operation_id`, `text?` |
|
|
@@ -620,7 +619,7 @@ Claudeをリンク経由の`cwd`から起動した場合も、実体パスに対
|
|
|
620
619
|
|
|
621
620
|
**For other parent hosts**, dispatch and start the receipt's waiter in a separate process:
|
|
622
621
|
|
|
623
|
-
1. 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 plain `pty_send` (or `claude_turn issue` for durable Claude operations). The call returns immediately with an `event_cursor` in its structured receipt.
|
|
622
|
+
1. 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 plain `pty_send` (or `claude_turn issue` for durable Claude operations). The call returns immediately with an `event_cursor` in 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.
|
|
624
623
|
2. Pass the receipt's `wait_process.executable` and `wait_process.args` unchanged to a true argv process API. PowerShell 7's `Start-Process` is the exception because it joins `-ArgumentList` arrays; pass `windows_start_process_argument_list` as 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_command` remains 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-line `aiterm.agent-wait-result.v1` receipt. **Exit ≠ done**: the receipt's `outcome` is authoritative (`0` = `done`, `3` = `timeout`, `4` = `closed`, `1` = error).
|
|
625
624
|
3. **親自身のforegroundでwaiterを実行しない。** receiptのprocess起動情報を、そのhostが持つバックグラウンドprocess APIへ渡す。親は別作業へ進むかturnを終え、process終了の通知で続行する。
|
|
626
625
|
4. Collect the result exactly as before: `pty_read(agent_transcript: true)`, or `claude_turn recover` for durable Claude operations. The waiter carries the signal, never the payload.
|
|
@@ -640,7 +639,7 @@ agent_launch({ "harness": "codex-cli", "remote": { "host": "rabbit" }, "cwd": "/
|
|
|
640
639
|
- `remote` takes `host`, `user`, `port`, `identity_file`, `passphrase` or `passphrase_env`, and `ssh_options` (`Key=Value`). A bare `host` is used as an `~/.ssh/config` alias. Aiterm does not store or manage destinations; the caller owns where to connect and with which key.
|
|
641
640
|
- A plain `passphrase` stays in the calling AI's conversation log. Prefer ssh-agent or `passphrase_env` (an environment variable name). A received passphrase lives only in the MCP process memory and reaches ssh through `SSH_ASKPASS`; it is never written to state files or logs.
|
|
642
641
|
- The remote machine needs `aiterm-mcp`, tmux (psmux on Windows), and the harness CLI. The remote shell family (POSIX, PowerShell, or cmd) is detected on first contact. On POSIX machines Aiterm takes only PATH from the login shell, so CLIs under `~/.local/bin`, Homebrew, or nvm are found; on Windows it starts `aiterm-mcp` with the user's PATH as is.
|
|
643
|
-
- Pass the same `remote` to later `pty_send`, `pty_read`, `pty_close`, `pty_observe`,
|
|
642
|
+
- Pass the same `remote` to later `pty_send`, `pty_read`, `pty_close`, `pty_observe`, and so on. Session names belong to the remote machine and never collide with local sessions of the same name.
|
|
644
643
|
- Codex, Claude Code, and Cursor parents receive the answer automatically, as with a local child. Completion is observed with `ssh <host> aiterm-wait`, reconnecting at the same cursor if SSH drops. Other parents get an ssh-based `wait_process`.
|
|
645
644
|
- Calls to the same destination share one SSH connection through ControlMaster. `image` attachments and `claude_turn issue` are not yet supported with `remote`.
|
|
646
645
|
|
package/dist/core.js
CHANGED
|
@@ -3528,7 +3528,7 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
3528
3528
|
const claudeColdStart = meta.kind === "claude"
|
|
3529
3529
|
&& agentCompletionCursor(meta) === 0
|
|
3530
3530
|
&& readClaudeOperationMarker(meta) === null;
|
|
3531
|
-
const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
3531
|
+
const paneInputRecovery = o.pane_input_recovery ?? await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
3532
3532
|
let codexRateLimitModelSwitch = false;
|
|
3533
3533
|
const limitDialog = meta.kind === "grok" || meta.kind === "composer"
|
|
3534
3534
|
? grokRateLimitDialog(captureScreen(name, 0)) : null;
|
|
@@ -3640,6 +3640,26 @@ async function waitSteerScreen(name, predicate) {
|
|
|
3640
3640
|
export function __testSteerQueued(kind, screen) {
|
|
3641
3641
|
return kind === "cursor" ? cursorSteerQueued(screen) : grokSteerQueued(screen);
|
|
3642
3642
|
}
|
|
3643
|
+
/**
|
|
3644
|
+
* agent sessionへの唯一の送信口。呼び出し側は子の状態を知らないまま呼び、Aitermがこの時点の画面で振り分ける。
|
|
3645
|
+
* 実行中なら現在のturnへ差し込み(steer)、そうでなければ新しいturnとしてdispatchする。
|
|
3646
|
+
* 振り分けを呼び出し側へ任せると、状態を見てから呼ぶまでの間に子のturnが変わり、選んだ入口が外れる。
|
|
3647
|
+
*/
|
|
3648
|
+
export async function sendAgentMessage(name, text, o = {}) {
|
|
3649
|
+
assertSessionName(name);
|
|
3650
|
+
const meta = loadAgentMetadata(name);
|
|
3651
|
+
// 前面回復は busy 判定より先(bash 前面のままだと画面の実行中マーカーを読んでも打鍵が届かない)。
|
|
3652
|
+
const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
3653
|
+
let running = isAgentTuiBusy(meta.kind, captureScreen(name, AGENT_TUI_READY_LINES));
|
|
3654
|
+
// Claude CodeはStop hookの実行中も実行中の表示を続ける。Aitermのturnの印はStopで消えるので、
|
|
3655
|
+
// 印が無ければturnは終わっている。ここで差し込むと新しいturnとして始まり、誰もその完了を待たない。
|
|
3656
|
+
if (meta.kind === "claude" && readClaudeOperationMarker(meta) === null)
|
|
3657
|
+
running = false;
|
|
3658
|
+
if (running) {
|
|
3659
|
+
return steerRunningTurn(name, meta, text, { raw: o.raw, pane_input_recovery: paneInputRecovery });
|
|
3660
|
+
}
|
|
3661
|
+
return dispatchAgentTurn(name, text, { raw: o.raw, before_send: o.before_send, pane_input_recovery: paneInputRecovery });
|
|
3662
|
+
}
|
|
3643
3663
|
/**
|
|
3644
3664
|
* 実行中turnへ追加の指示を渡す。各harnessの標準操作だけを使い、完了境界は1つに保つ。
|
|
3645
3665
|
* - Codex: 次のtool呼出し後に同じturnへ入る(rolloutのturn_idが同じ)。
|
|
@@ -3648,28 +3668,22 @@ export function __testSteerQueued(kind, screen) {
|
|
|
3648
3668
|
* - Grok: 待ち行列へ入れた後に「send now」を押す。旧turnは`cancelled`(trigger=send_now)で閉じ、
|
|
3649
3669
|
* 新turnが作業を継ぐ。完了判定はこの継ぎ目を完了と数えない(grokCompletionEvent)。
|
|
3650
3670
|
*/
|
|
3651
|
-
|
|
3652
|
-
assertSessionName(name);
|
|
3653
|
-
const meta = loadAgentMetadata(name);
|
|
3671
|
+
async function steerRunningTurn(name, meta, text, o) {
|
|
3654
3672
|
const receipt = {
|
|
3655
3673
|
schema: "aiterm.agent-steer.v1",
|
|
3656
3674
|
session_id: meta.aiterm_session,
|
|
3657
3675
|
launch_id: meta.launch_id,
|
|
3658
3676
|
vendor: meta.kind,
|
|
3659
3677
|
harness: agentHarness(meta.kind),
|
|
3678
|
+
pane_input_recovery: o.pane_input_recovery,
|
|
3660
3679
|
};
|
|
3661
|
-
|
|
3662
|
-
const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
3663
|
-
if (!isAgentTuiBusy(meta.kind, captureScreen(name, AGENT_TUI_READY_LINES))) {
|
|
3664
|
-
return { ...receipt, delivery: "idle", pane_input_recovery: paneInputRecovery };
|
|
3665
|
-
}
|
|
3666
|
-
prepareSendText(text, { raw: false });
|
|
3680
|
+
prepareSendText(text, { raw: o.raw });
|
|
3667
3681
|
// Claudeのoperation相関は変えない。差し込みは実行中turnの一部であり、新しいturnを予約しない。
|
|
3668
3682
|
const preserveAgentOperation = meta.kind === "claude";
|
|
3669
3683
|
send(name, text, {
|
|
3670
3684
|
enter: false,
|
|
3671
3685
|
force: true,
|
|
3672
|
-
raw:
|
|
3686
|
+
raw: o.raw,
|
|
3673
3687
|
mark: false,
|
|
3674
3688
|
rtk: false,
|
|
3675
3689
|
preserveAgentOperation,
|
|
@@ -3693,7 +3707,8 @@ export async function steerAgentTurn(name, text) {
|
|
|
3693
3707
|
const label = meta.kind === "cursor" ? "Cursor" : "Grok";
|
|
3694
3708
|
if (!await waitSteerScreen(name, queued)) {
|
|
3695
3709
|
throw new AitermError(`STEER_NOT_QUEUED vendor=${meta.kind} session=${name}\n` +
|
|
3696
|
-
`差し込む文が${label}
|
|
3710
|
+
`差し込む文が${label}の待ち行列へ入ったことを確認できません。子のturnが送る直前に終わっていた時は、` +
|
|
3711
|
+
`文は新しいturnとして始まっており、その完了は通知されません。再送する前にpty_read(screen:true)で確かめてください。`, 2);
|
|
3697
3712
|
}
|
|
3698
3713
|
sendKey(name, "Enter");
|
|
3699
3714
|
if (!await waitSteerScreen(name, screen => !queued(screen))) {
|
|
@@ -3711,7 +3726,7 @@ export async function steerAgentTurn(name, text) {
|
|
|
3711
3726
|
"差し込む文がCursorの入力欄に残っており、実行中のturnへ渡せませんでした。", 2);
|
|
3712
3727
|
}
|
|
3713
3728
|
}
|
|
3714
|
-
return
|
|
3729
|
+
return receipt;
|
|
3715
3730
|
}
|
|
3716
3731
|
export async function runClaudeOperation({ session_id: name, action, operation_id: operationIdInput, text, before_send, }) {
|
|
3717
3732
|
assertSessionName(name);
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -84,7 +84,7 @@ export function grokCompletionEvent(meta, record) {
|
|
|
84
84
|
record?.type !== "turn_ended" ||
|
|
85
85
|
(record?.outcome !== "completed" && record?.outcome !== "cancelled" && record?.outcome !== "error"))
|
|
86
86
|
return null;
|
|
87
|
-
//
|
|
87
|
+
// 実行中turnへの差し込み(pty_send)の「send now」は旧turnを閉じて同じ作業を新turnで続ける継ぎ目であり、完了ではない
|
|
88
88
|
// (実測 grok 1.0.41: outcome=cancelled, cancellation_context.trigger=send_now の直後にturn_started)。
|
|
89
89
|
if (record.outcome === "cancelled" && record.cancellation_context?.trigger === "send_now")
|
|
90
90
|
return null;
|
package/dist/index.js
CHANGED
|
@@ -187,15 +187,18 @@ async function sendRemote(target, args, extra) {
|
|
|
187
187
|
throw new core.AitermError("REMOTE_IMAGE_UNSUPPORTED: 別端末への画像添付には未対応です", 2);
|
|
188
188
|
const key = deliveryKey(args.session_id, target);
|
|
189
189
|
const delivery = args.force ? null : await deliveryForRequest(extra, true);
|
|
190
|
-
//
|
|
190
|
+
// 終わった依頼の回答は送る前に保存する。実行中の依頼は残る。現地が差し込みを選べばその依頼が回答を受け取る。
|
|
191
191
|
if (delivery)
|
|
192
|
-
await remoteParentDelivery.beforeChange(key
|
|
192
|
+
await remoteParentDelivery.beforeChange(key);
|
|
193
193
|
const result = await callRemoteTool(target, "pty_send", args);
|
|
194
194
|
const structured = result.structuredContent;
|
|
195
195
|
if (result.isError || !structured || structured.mode !== "agent_dispatch") {
|
|
196
196
|
return { content: [...result.content, remoteNote(target, result)], ...(structured ? { structuredContent: structured } : {}), ...(result.isError ? { isError: true } : {}) };
|
|
197
197
|
}
|
|
198
198
|
try {
|
|
199
|
+
// 現地がdispatchを選んだのは子がidleだった時なので、前の依頼はもう保存できる。残っていれば登録しない。
|
|
200
|
+
if (delivery)
|
|
201
|
+
await remoteParentDelivery.beforeChange(key, true);
|
|
199
202
|
if (delivery && structured.event_cursor !== null)
|
|
200
203
|
await registerRemoteDelivery(delivery, target, structured.session_id, structured.event_cursor);
|
|
201
204
|
}
|
|
@@ -248,7 +251,10 @@ registerRemoteAwareTool("pty_open", {
|
|
|
248
251
|
});
|
|
249
252
|
registerRemoteAwareTool("pty_send", {
|
|
250
253
|
description: "セッションへテキストを送る。通常PTYへは送信のみ(出力は pty_read で取得)。" +
|
|
251
|
-
"agent session(launcher
|
|
254
|
+
"agent session(launcher起動)への送信はこのtoolだけで行い、子の状態はAitermが送る時点で見て振り分ける。" +
|
|
255
|
+
"子のturnが実行中なら各harness標準の操作で現在のturnへ差し込み(mode=agent_steer)、完了は差し込み後の作業の終わりに" +
|
|
256
|
+
"元の依頼への1回だけ届く。新しいevent_cursorと配送は作らない。" +
|
|
257
|
+
"それ以外は新しいturnとしてdispatchし(mode=agent_dispatch)、TUI の ready gate と submit 分離を通して即返り、" +
|
|
252
258
|
"receipt の event_cursor を返す。" +
|
|
253
259
|
NON_BLOCKING_RULE +
|
|
254
260
|
"自動配送以外の結果回収は pty_read(agent_transcript:true)、Claude の durable turn は claude_turn を使う。" +
|
|
@@ -280,7 +286,7 @@ registerRemoteAwareTool("pty_send", {
|
|
|
280
286
|
},
|
|
281
287
|
outputSchema: {
|
|
282
288
|
schema: z.literal("aiterm.pty-send-result.v1"),
|
|
283
|
-
mode: z.enum(["sent", "agent_dispatch"]),
|
|
289
|
+
mode: z.enum(["sent", "agent_dispatch", "agent_steer"]),
|
|
284
290
|
session_id: z.string(),
|
|
285
291
|
event_cursor: z.number().int().nullable(),
|
|
286
292
|
wait_process: waitProcessOutputSchema,
|
|
@@ -305,7 +311,30 @@ registerRemoteAwareTool("pty_send", {
|
|
|
305
311
|
if (rtk)
|
|
306
312
|
throw new Error("agent session への dispatch は rtk:true と併用できません");
|
|
307
313
|
delivery = await deliveryForRequest(extra);
|
|
308
|
-
const receipt = await core.
|
|
314
|
+
const receipt = await core.sendAgentMessage(session_id, core.attachImages(text, image), { raw, before_send: delivery?.before_send });
|
|
315
|
+
if (receipt.schema === "aiterm.agent-steer.v1") {
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: "text",
|
|
320
|
+
text: `実行中のturnへ差し込んだ(harness=${receipt.harness}, vendor=${receipt.vendor})。` +
|
|
321
|
+
"完了は差し込み後の作業の終わりに、元の依頼への通知として1回だけ届く。",
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
structuredContent: {
|
|
325
|
+
schema: "aiterm.pty-send-result.v1",
|
|
326
|
+
mode: "agent_steer",
|
|
327
|
+
session_id: receipt.session_id,
|
|
328
|
+
event_cursor: null,
|
|
329
|
+
wait_process: null,
|
|
330
|
+
launch_id: receipt.launch_id,
|
|
331
|
+
vendor: receipt.vendor,
|
|
332
|
+
harness: receipt.harness,
|
|
333
|
+
submit_residue: null,
|
|
334
|
+
pane_input_recovery: receipt.pane_input_recovery,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
309
338
|
const waited = completionWait(delivery, receipt.session_id, receipt.event_cursor);
|
|
310
339
|
return {
|
|
311
340
|
content: [
|
|
@@ -356,35 +385,6 @@ registerRemoteAwareTool("pty_send", {
|
|
|
356
385
|
return fail(e);
|
|
357
386
|
}
|
|
358
387
|
});
|
|
359
|
-
registerRemoteAwareTool("agent_steer", {
|
|
360
|
-
description: "実行中のClaude/Codex/Grok/Cursor agentへ追加メッセージを差し込み、現在のターンを誘導する。" +
|
|
361
|
-
"完了は差し込み後の作業の終わりに1回だけ届く。独立した次ターンを始める用途ではなく、idle時は文字を送らずdelivery=idleを返す。" +
|
|
362
|
-
"Grokが待ち行列へ入れない時とCursorの入力欄に残った時は、steeredを返さずエラーにする。",
|
|
363
|
-
inputSchema: {
|
|
364
|
-
session_id: z.string(),
|
|
365
|
-
text: z.string().describe("現在のターンへ追加する文字列。UTF-8で最大64KiB"),
|
|
366
|
-
image: z.array(z.string()).optional().describe("添付する画像ファイルの絶対パス(png/jpg/jpeg/gif/webp)"),
|
|
367
|
-
},
|
|
368
|
-
outputSchema: {
|
|
369
|
-
schema: z.literal("aiterm.agent-steer.v1"),
|
|
370
|
-
session_id: z.string(),
|
|
371
|
-
launch_id: z.string(),
|
|
372
|
-
vendor: z.enum(["claude", "codex", "grok", "composer", "cursor"]),
|
|
373
|
-
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
|
|
374
|
-
delivery: z.enum(["steered", "idle"]),
|
|
375
|
-
},
|
|
376
|
-
}, async ({ session_id, text, image }) => {
|
|
377
|
-
try {
|
|
378
|
-
const receipt = await core.steerAgentTurn(session_id, core.attachImages(text, image));
|
|
379
|
-
return {
|
|
380
|
-
content: [{ type: "text", text: `${receipt.delivery} ${receipt.session_id}` }],
|
|
381
|
-
structuredContent: receipt,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
catch (e) {
|
|
385
|
-
return fail(e);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
388
|
registerRemoteAwareTool("pty_read", {
|
|
389
389
|
description: "セッションの出力をトークン削減して読む(既定は前回読取位置からの増分)。" +
|
|
390
390
|
"削減: 制御文字除去 / 反復圧縮 / head+tail 折りたたみ+復元ヒント+メタ併記。" +
|
package/dist/update-cli.js
CHANGED
|
@@ -56,7 +56,8 @@ try {
|
|
|
56
56
|
for (const r of results) {
|
|
57
57
|
const versions = r.from_version && r.to_version && r.from_version !== r.to_version ? `${r.from_version} → ${r.to_version}` : (r.to_version ?? r.from_version ?? "?");
|
|
58
58
|
const setup = r.setup_status ? ` setup=${r.setup_status}${r.setup_reason_code ? `(${r.setup_reason_code})` : ""}` : "";
|
|
59
|
-
|
|
59
|
+
// 入れ替えた時だけ、動いているaiterm-mcpは旧版のcodeで動いている。
|
|
60
|
+
const servers = r.running_servers ? ` ${r.status === "updated" ? "旧版で" : ""}動いているaiterm-mcp=${r.running_servers}` : "";
|
|
60
61
|
const reason = r.reason_code ? ` reason=${r.reason_code}` : "";
|
|
61
62
|
process.stdout.write(`${r.target}: ${r.status} ${versions}${setup}${servers}${reason}${r.message ? `\n ${r.message}` : ""}\n`);
|
|
62
63
|
}
|
package/dist/update.js
CHANGED
|
@@ -183,7 +183,22 @@ export function remoteUpdateCommand(shell, version, check) {
|
|
|
183
183
|
const missing = check
|
|
184
184
|
? 'echo "{\\"schema\\":\\"aiterm.update-result.v1\\",\\"status\\":\\"checked\\",\\"reason_code\\":\\"update_command_missing\\"}"; exit 0'
|
|
185
185
|
: `${install} 1>&2 || exit $?`;
|
|
186
|
-
|
|
186
|
+
// npmのbinをPATHへ通さず aiterm-mcp だけリンクしている端末がある(rabbit実測)。PATHに無ければ、
|
|
187
|
+
// aiterm-mcpの実体と同じpackageのupdate-cli.jsをnodeで起動する。
|
|
188
|
+
const resolve = 'u=$(command -v aiterm-update) || { m=$(command -v aiterm-mcp) && u="$(dirname "$(readlink -f "$m")")/update-cli.js"; }';
|
|
189
|
+
const has = `{ command -v aiterm-update >/dev/null 2>&1 || { m=$(command -v aiterm-mcp) && [ -f "$(dirname "$(readlink -f "$m")")/update-cli.js" ]; }; }`;
|
|
190
|
+
return `/bin/sh -c '${importPath}; ${has} || { ${missing}; }; ${resolve}; case "$u" in *.js) exec node "$u" ${args};; *) exec "$u" ${args};; esac'`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 現地の出力から現地自身の結果を取り出す。現地のaiterm-updateは全体の結果(aiterm.update-run.v1)を、
|
|
194
|
+
* 更新機能の無い版向けの--checkは1件の結果(aiterm.update-result.v1)を最後のJSON行に出す。
|
|
195
|
+
*/
|
|
196
|
+
export function remoteUpdateResult(stdout) {
|
|
197
|
+
const last = parseLastJson(stdout);
|
|
198
|
+
const result = last?.schema === "aiterm.update-run.v1" && Array.isArray(last.results)
|
|
199
|
+
? last.results.find((item) => item?.target === "local")
|
|
200
|
+
: last;
|
|
201
|
+
return result?.schema === "aiterm.update-result.v1" ? result : null;
|
|
187
202
|
}
|
|
188
203
|
/** 別端末のAitermを同じ版へ更新する。現地の結果JSONへ接続先の表示名を付けて返す。 */
|
|
189
204
|
export async function updateRemote(target, version, check) {
|
|
@@ -209,8 +224,8 @@ export async function updateRemote(target, version, check) {
|
|
|
209
224
|
child.on("error", (error) => resolve({ status: null, stdout, stderr, error: error.message }));
|
|
210
225
|
child.on("close", (status) => resolve({ status, stdout, stderr }));
|
|
211
226
|
});
|
|
212
|
-
const result =
|
|
213
|
-
if (!result
|
|
227
|
+
const result = remoteUpdateResult(outcome.stdout);
|
|
228
|
+
if (!result) {
|
|
214
229
|
return failed("remote_update_failed", `現地の更新が結果を返しません: ${tail(outcome.stderr) || outcome.error || `exit ${outcome.status}`}`);
|
|
215
230
|
}
|
|
216
231
|
return {
|
package/docs/DESIGN.md
CHANGED
|
@@ -64,7 +64,11 @@ waiterは純readerで、親のforeground turnを塞がない。
|
|
|
64
64
|
Grok/Composerの記録先はCLIと同じOS絶対パスへcwdを正規化して導出し、完了通知と回答で同じ関数を使う。
|
|
65
65
|
配送用のGrok回答は`turn_ended.ts`から同じturnの`turn_started.turn_number`を取得し、
|
|
66
66
|
`chat_history.jsonl`の`user.prompt_index`と相関する。次turnが既に始まっていても対象回答だけを回収する。
|
|
67
|
-
|
|
67
|
+
agent sessionへの送信口は`pty_send`だけとする。子の状態は呼び出し側に選ばせず、Aitermが送る時点の画面で振り分ける。
|
|
68
|
+
状態を見てから呼ぶまでの間に子のturnが変わるため、呼び出し側が入口を選ぶ形では外れる。
|
|
69
|
+
実行中なら現在のturnへ追加textを差し込み、新しい完了境界と配送は作らない。完了境界は差し込み後も1つに保つ。
|
|
70
|
+
Claude CodeはStop hookの実行中も実行中の表示を続けるため、Aitermのturnの印(Stopで消える)が無ければ実行中と数えない。
|
|
71
|
+
それ以外は新しいturnとしてdispatchする。
|
|
68
72
|
CodexとClaude Codeは次のtool境界で同じturnへ取り込む。Cursorは「follow-ups」枠へ入った文を「enter steer」で現在turnへ移し、`turn_ended`は最後に1回書く。
|
|
69
73
|
Grokは待ち行列へ入れた後に「send now」を押す。旧turnは`cancelled`(`cancellation_context.trigger=send_now`)で閉じ、
|
|
70
74
|
新turnが作業を継ぐので、完了判定はこの継ぎ目を完了と数えない。Grokが待ち行列へ入れない時とCursorの入力欄に本文が残る時は`steered`を返さない。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"mcpName": "io.github.kitepon/aiterm-mcp",
|
|
5
5
|
"description": "Persistent terminal MCP with one harness-based launcher for Claude Code, Codex CLI, Grok CLI, and Cursor Agent CLI, plus durable PTYs for SSH, containers, and REPLs.",
|
|
6
6
|
"keywords": [
|