aiterm-mcp 0.16.0 → 0.17.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/README.ja.md CHANGED
@@ -24,10 +24,13 @@
24
24
 
25
25
  12 ツール: 6 つの **PTY ツール**(`pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list`)で 1 本の永続端末を開き・操作し・読む。加えて 4 つの **エージェント起動ツール**(`claude_agent` / `codex_agent` / `grok_agent` / `composer_agent`)が別のコーディングエージェントの TUI を新しい端末の中に起動し、`claude_turn`がdurable caller向けの構造化issue/recoveryを、`diagnostics`が安全なfactory readinessを返す。バックエンドは **tmux** なので、MCP サーバや AI クライアントが再起動してもセッションは生き残る。
26
26
 
27
- **v0.12.2 は release candidate(公開待ち)です。** factory diagnostics と local
27
+ **v0.17.0 を 2026-07-18 に公開。** 親エージェントは aiterm 上で一切ブロックしない:
28
+ agent session への send は常に非ブロック dispatch になり、完了待ちは `aiterm-wait` 一本
29
+ (exit code が receipt の outcome を映す: 0=done / 3=timeout=未完了 / 4=closed)、初回 prompt 付き
30
+ launch は structured receipt にコピペ可能な `wait_command` を含む。factory diagnostics と local
28
31
  runtime-error store は canonical dotagents config の `collection.enabled: true` が明示された
29
- 場合だけ収集し、既定OFF、network送信は行いません。npm公開、tag、CI、registry登録、registry由来install
30
- の確認は未実施です。
32
+ 場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
33
+ Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
31
34
 
32
35
  **状態:** 開発継続中 · この分野では新参で、別の形に賭けている([既存手段との比較](#既存手段との比較)参照)· 動作対象は Linux · WSL2 · macOS · Windows ネイティブ(core PTY ツール。`agent_done` は現時点では POSIX/WSL/macOS のみ)· MIT · [変更履歴](CHANGELOG.md)。
33
36
 
@@ -52,7 +55,7 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
52
55
 
53
56
  ### 2. その端末の中に他のコーディングエージェントを起動する — オーケストレーションの旗艦
54
57
 
55
- 同じ primitive が別エージェントの TUI を宿す。4 つの起動ツールが、Claude/Codex/Grok/Composer の対話 TUI を新しい永続端末の中に起動し、`session_id` を返す。既存の人間向けtextに加えて`aiterm.agent-launch-result.v1` structured receiptも返すため、durable callerは表示文字列を解析せずsession handleを取得できる。以後は同じ `pty_read` / `pty_send` で継続操作する。**起動は常に managed**(aiterm 所有の Stop hook 付き)で、agent session への `pty_send` は非ブロックの **dispatch** になり `event_cursor` 入り receipt を即返す。完了通知は `aiterm-wait --session <id> --cursor <event_cursor>` をホストのバックグラウンドタスクとして実行し、その exit で受ける(親はブロックもポーリングもしない)。durable machine callerは`claude_turn({ action:"issue"|"recover", session_id, operation_id, ... })`を使い、人間向けerror文字列を解析せず`accepted`/`pending`/`completed`/`unknown`を判定できる。recoveryは再送せず、検証済み完了だけがexact `raw_output`を持つ。通常の`pty_send`/`pty_read`は対話callerと人間向けに維持する。`C-c`後もmarkerを保持し、Stopが来なければsessionをcloseする。`claude_agent` と `codex_agent` の初回 `prompt` は ready gate 経由で送信して待たずに返る(Grok/Composer は argv 渡し)。手動でキー操作したい場合は `pty_open` で素の端末を開き vendor CLI を自分で起動する。
58
+ 同じ primitive が別エージェントの TUI を宿す。4 つの起動ツールが、Claude/Codex/Grok/Composer の対話 TUI を新しい永続端末の中に起動し、`session_id` を返す。既存の人間向けtextに加えて`aiterm.agent-launch-result.v1` structured receiptも返すため、durable callerは表示文字列を解析せずsession handleを取得できる。以後は同じ `pty_read` / `pty_send` で継続操作する。**起動は常に managed**(aiterm 所有の Stop hook 付き)で、agent session への `pty_send` は非ブロックの **dispatch** になり `event_cursor` 入り receipt を即返す。完了通知は `aiterm-wait --session <id> --cursor <event_cursor>` をホストのバックグラウンドタスクとして実行し、exit 時に receipt の `outcome` で判定する(exit 0=done / 3=timeout=未完了・既定600秒 / 4=closed。親はブロックもポーリングもしない)。起動時 `prompt` を渡した launch は structured receipt にコピペ可能な `wait_command` と `event_cursor` を含む。durable machine callerは`claude_turn({ action:"issue"|"recover", session_id, operation_id, ... })`を使い、人間向けerror文字列を解析せず`accepted`/`pending`/`completed`/`unknown`を判定できる。recoveryは再送せず、検証済み完了だけがexact `raw_output`を持つ。通常の`pty_send`/`pty_read`は対話callerと人間向けに維持する。`C-c`後もmarkerを保持し、Stopが来なければsessionをcloseする。`claude_agent` と `codex_agent` の初回 `prompt` は ready gate 経由で送信して待たずに返る(Grok/Composer は argv 渡し)。手動でキー操作したい場合は `pty_open` で素の端末を開き vendor CLI を自分で起動する。
56
59
 
57
60
  ```text
58
61
  codex_agent({ session_name: "codex1", cwd: "/repo",
@@ -60,7 +63,7 @@ codex_agent({ session_name: "codex1", cwd: "/repo",
60
63
  → { session_id: "codex1", … } # Codex が永続端末で稼働開始
61
64
  pty_read("codex1", { screen: true }) → 何をしているか読む(トークン削減)
62
65
  pty_send("codex1", "also fix the imports it broke") # 非ブロックdispatch=event_cursor入りreceipt
63
- $ aiterm-wait --session codex1 --cursor <event_cursor> # exitが完了push。回収は pty_read(agent_transcript:true)
66
+ $ aiterm-wait --session codex1 --cursor <event_cursor> # exit 0=done / 3=timeout(未完了) / 4=closed。回収は pty_read(agent_transcript:true)
64
67
  → 操舵し、Codex の次の入力境界で返る
65
68
  ```
66
69
 
package/README.md CHANGED
@@ -24,14 +24,16 @@
24
24
 
25
25
  Twelve tools: six **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` — to open, drive, and read one persistent terminal, four **agent launchers** — `claude_agent` / `codex_agent` / `grok_agent` / `composer_agent` — that each start another coding agent's TUI inside a fresh one, `claude_turn` for durable structured issue/recovery, and `diagnostics` for safe factory readiness. The backend is **tmux**, so sessions survive even if the MCP server or the AI client restarts.
26
26
 
27
- **v0.15.0 was published on 2026-07-18.** It brings the interactive agent
28
- launchers, durable `claude_turn` issue/recovery, machine-readable launch and
29
- idempotent close receipts, the hardened TUI readiness gate, and the new
30
- `aiterm-wait` completion-push binary to npm. Factory diagnostics and the local
31
- runtime-error store collect only when canonical dotagents config explicitly sets
32
- `collection.enabled: true`; collection is off by default and performs no network
33
- I/O. It ships via tag-triggered CI with npm provenance (OIDC Trusted Publishing);
34
- the GitHub Release re-registers the Official MCP Registry entry.
27
+ **v0.17.0 was published on 2026-07-18.** A parent agent never blocks on aiterm:
28
+ every send to an agent session is a non-blocking dispatch, completion is one
29
+ universal `aiterm-wait` waiter whose exit codes mirror the receipt outcome
30
+ (`0`=done / `3`=timeout, not finished / `4`=closed), and a launch with an
31
+ initial prompt returns a ready-made `wait_command` in its structured receipt.
32
+ Factory diagnostics and the local runtime-error store collect only when
33
+ canonical dotagents config explicitly sets `collection.enabled: true`;
34
+ collection is off by default and performs no network I/O. It ships via
35
+ tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
36
+ Release re-registers the Official MCP Registry entry.
35
37
 
36
38
  **Status:** actively maintained · the newcomer here, betting on a different shape (see [vs. the alternatives](#vs-the-alternatives)) · runs on Linux · WSL2 · macOS · native Windows for the core PTY tools (managed completion is POSIX/WSL/macOS only for now) · MIT · see the [CHANGELOG](CHANGELOG.md).
37
39
 
@@ -62,7 +64,7 @@ pty_read(id, { wait: true }) → read the token-reduced output, completion
62
64
 
63
65
  ### 2. Launch other coding agents into that terminal — the orchestration flagship
64
66
 
65
- The same primitive hosts another agent's TUI. Four launchers each start one vendor's interactive coding-agent TUI inside a fresh persistent terminal and return a `session_id`. Their existing human-readable text is accompanied by an `aiterm.agent-launch-result.v1` structured receipt, so durable callers never parse display text for the session handle. From there you drive it with the same `pty_read` / `pty_send` you'd use on any shell: read its output token-reduced, send it the next step. (The TUIs are full-screen apps, so `pty_read({ screen: true })` gives you the rendered view.) Every launch is **managed**: aiterm installs its own Stop hook, so turn completion is a first-class event. Sending to an agent session is a non-blocking **dispatch** — the call returns immediately with an `event_cursor`, and completion arrives via [`aiterm-wait`](#completion-push-for-parent-agents-aiterm-wait). Durable machine callers use `claude_turn({ action: "issue" | "recover", session_id, operation_id, ... })`: it returns fixed `accepted` / `pending` / `completed` / `unknown` states without parsing human-facing errors, never resends during recovery, and includes exact `raw_output` only for a verified completion. The same operation ID is carried through the dispatch receipt, active marker, Stop event, and result. The ordinary `pty_send` / `pty_read` surface remains available for interactive callers and humans. `C-c` keeps the marker for a delayed Stop; if no Stop arrives, close the session. An initial `prompt` on `claude_agent`/`codex_agent` is submitted through the same ready gate and the launcher returns without waiting; on Grok/Composer it is passed on the CLI's argv. This needs the vendor's own CLI installed and authenticated — see [Requirements](#requirements).
67
+ The same primitive hosts another agent's TUI. Four launchers each start one vendor's interactive coding-agent TUI inside a fresh persistent terminal and return a `session_id`. Their existing human-readable text is accompanied by an `aiterm.agent-launch-result.v1` structured receipt, so durable callers never parse display text for the session handle; when the launch carries an initial `prompt`, the receipt also includes the `event_cursor` and a ready-made `wait_command` for the completion waiter. From there you drive it with the same `pty_read` / `pty_send` you'd use on any shell: read its output token-reduced, send it the next step. (The TUIs are full-screen apps, so `pty_read({ screen: true })` gives you the rendered view.) Every launch is **managed**: aiterm installs its own Stop hook, so turn completion is a first-class event. Sending to an agent session is a non-blocking **dispatch** — the call returns immediately with an `event_cursor`, and completion arrives via [`aiterm-wait`](#completion-push-for-parent-agents-aiterm-wait). Durable machine callers use `claude_turn({ action: "issue" | "recover", session_id, operation_id, ... })`: it returns fixed `accepted` / `pending` / `completed` / `unknown` states without parsing human-facing errors, never resends during recovery, and includes exact `raw_output` only for a verified completion. The same operation ID is carried through the dispatch receipt, active marker, Stop event, and result. The ordinary `pty_send` / `pty_read` surface remains available for interactive callers and humans. `C-c` keeps the marker for a delayed Stop; if no Stop arrives, close the session. An initial `prompt` on `claude_agent`/`codex_agent` is submitted through the same ready gate and the launcher returns without waiting; on Grok/Composer it is passed on the CLI's argv. This needs the vendor's own CLI installed and authenticated — see [Requirements](#requirements).
66
68
 
67
69
  ```text
68
70
  codex_agent({ session_name: "codex1", cwd: "/repo",
@@ -71,7 +73,7 @@ codex_agent({ session_name: "codex1", cwd: "/repo",
71
73
  pty_read("codex1", { screen: true }) → read what it's doing (token-reduced)
72
74
  pty_send("codex1", "also fix the imports it broke")
73
75
  → non-blocking dispatch; receipt carries event_cursor
74
- $ aiterm-wait --session codex1 --cursor <event_cursor> # host background task; its exit = completion push
76
+ $ aiterm-wait --session codex1 --cursor <event_cursor> # host background task; exit code 0=done, 3=timeout (not done), 4=closed
75
77
  pty_read("codex1", { agent_transcript: true }) → collect the full answer
76
78
  ```
77
79
 
@@ -303,10 +305,10 @@ When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lin
303
305
 
304
306
  ### Completion push for parent agents (`aiterm-wait`)
305
307
 
306
- As of v0.16.0 a parent agent **never blocks** on aiterm — there is no wait parameter anywhere. The whole flow is dispatch + one universal waiter:
308
+ As of v0.16 a parent agent **never blocks** on aiterm — there is no wait parameter anywhere (v0.17 makes the waiter's exit codes mirror its outcome). The whole flow is dispatch + one universal waiter:
307
309
 
308
310
  1. Launch the child (`claude_agent` / `codex_agent` / ...; every launch is managed). Send a turn with plain `pty_send` (or `claude_turn issue` for durable Claude operations). The call passes the TUI ready gate, submits, and returns immediately with an `event_cursor` in its structured receipt.
309
- 2. Run `aiterm-wait --session <id> --cursor <event_cursor> [--operation sha256:<64hex>] [--timeout <sec>]`. It observes the vendor Stop-hook completion event as a **pure reader** and exits with a one-line `aiterm.agent-wait-result.v1` receipt (`done` / `timeout` / `closed`). The `--cursor` boundary makes it start-order independent: no completion can slip past even if the waiter starts late.
311
+ 2. Run `aiterm-wait --session <id> --cursor <event_cursor> [--operation sha256:<64hex>] [--timeout <sec>]` (a launch with an initial `prompt` returns this command ready-made as `wait_command` in its structured receipt). It observes the vendor Stop-hook completion event as a **pure reader** and exits with a one-line `aiterm.agent-wait-result.v1` receipt. **Exit ≠ done**: the receipt's `outcome` is authoritative, and the exit code mirrors it — `0` = `done`, `3` = `timeout` (the turn is **not** finished; default `--timeout` is 600 s), `4` = `closed`, `1` = error. On `timeout` just re-run the waiter with the same cursor. The `--cursor` boundary makes it start-order independent: no completion can slip past even if the waiter starts late.
310
312
  3. Host integration picks the invocation style: a harness that re-invokes its agent when a background task exits (Claude Code) runs the waiter **in the background** and gets woken with zero polling; a host without that mechanism runs it as a foreground shell command. Either way the aiterm MCP call itself never blocks.
311
313
  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.
312
314
 
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // aiterm-wait — agent turn 完了eventの純リーダー観測CLI。
3
3
  // 完了/timeout/close を1行のJSON receiptで返してexitする。lock・PTY・dispatch状態には一切触れない。
4
- // 親AIホストのバックグラウンドタスクとして起動し、exitを「完了通知」として使う。
4
+ // 親AIホストのバックグラウンドタスクとして起動し、exitを「観測終了の通知」として使う。
5
+ // exit≠完了: exit code は outcome を映す(0=done / 3=timeout=未完了 / 4=closed / 1=エラー)。
6
+ // receipt の outcome が正で、done 以外は未完了。timeout の既定は core の DEFAULT_AGENT_DONE_TIMEOUT(600秒)。
5
7
  import { fileURLToPath } from "node:url";
6
8
  import * as fs from "node:fs";
7
9
  import { AitermError, observeAgentDone } from "./core.js";
@@ -62,6 +64,8 @@ export function parseArgs(argv) {
62
64
  function emit(value) {
63
65
  process.stdout.write(JSON.stringify(value) + "\n");
64
66
  }
67
+ // outcome → exit code。exit status しか見えないホストでも done とそれ以外を誤読できないようにする。
68
+ const OUTCOME_EXIT_CODES = { done: 0, timeout: 3, closed: 4 };
65
69
  export async function main(argv) {
66
70
  const cmd = parseArgs(argv);
67
71
  const result = await observeAgentDone(cmd.session, {
@@ -70,6 +74,7 @@ export async function main(argv) {
70
74
  cursor: cmd.cursor,
71
75
  });
72
76
  emit(result);
77
+ process.exitCode = OUTCOME_EXIT_CODES[result.outcome];
73
78
  }
74
79
  function isDirectExecution() {
75
80
  const entry = process.argv[1];
package/dist/core.js CHANGED
@@ -2426,8 +2426,11 @@ function findLatestCodexTranscript(codexHome, vendorSessionId) {
2426
2426
  visit(sessionsDir);
2427
2427
  return latestFile;
2428
2428
  }
2429
+ // 未完了系エラーの共通出口案内。pollingへ誘導せず、正規の完了待ち手段を必ず指す。
2430
+ const AGENT_WAIT_GUIDE = "完了待ちは aiterm-wait --session <session_id> のバックグラウンド実行で受ける(polling不要。" +
2431
+ "receiptのoutcome=doneを確認してから再取得)。";
2429
2432
  function transcriptUnavailable() {
2430
- throw new AitermError("transcript がまだありません。ターン完了後に再取得してください。", 2);
2433
+ throw new AitermError(`transcript がまだありません。ターン完了後に再取得してください。${AGENT_WAIT_GUIDE}`, 2);
2431
2434
  }
2432
2435
  function transcriptNotFound(vendor) {
2433
2436
  throw new AitermError(`最終 assistant メッセージを特定できませんでした(vendor=${vendor})。screen で確認してください。`, 2);
@@ -2494,18 +2497,18 @@ export async function readAgentTranscript(name, o = {}) {
2494
2497
  const active = readClaudeOperationMarker(meta);
2495
2498
  if (active) {
2496
2499
  const label = active.operationId ? `operation ${active.operationId}` : "operation_idなしのClaude turn";
2497
- throw new AitermError(`${label} はまだ完了していません。Stop完了後に同じsessionから再取得してください。`, 2);
2500
+ throw new AitermError(`${label} はまだ完了していません。Stop完了後に同じsessionから再取得してください。${AGENT_WAIT_GUIDE}`, 2);
2498
2501
  }
2499
2502
  }
2500
2503
  // wait timeout は「失敗」ではなく状態不明。後着した同一launchの完了eventから
2501
2504
  // vendor session をbindし、promptを再送せず結果だけ回収できるようにする。
2502
2505
  recoverAgentVendorSession(meta);
2503
2506
  if (!meta.vendor_session_id) {
2504
- throw new AitermError(`agent session '${name}' はまだターンが完了していません。agent_done 完了後に再取得してください。`, 2);
2507
+ throw new AitermError(`agent session '${name}' はまだターンが完了していません。agent_done 完了後に再取得してください。${AGENT_WAIT_GUIDE}`, 2);
2505
2508
  }
2506
2509
  const done = latestAgentDoneEvent(meta, operationId);
2507
2510
  if (operationId && !done) {
2508
- throw new AitermError(`operation ${operationId} はまだ完了していません。同じoperation_idで後から再取得してください。`, 2);
2511
+ throw new AitermError(`operation ${operationId} はまだ完了していません。同じoperation_idで後から再取得してください。${AGENT_WAIT_GUIDE}`, 2);
2509
2512
  }
2510
2513
  const turnId = done?.turn_id ?? null;
2511
2514
  let text = "";
@@ -2648,6 +2651,8 @@ function assertInitialPromptNotPendingForSend(name, force) {
2648
2651
  throw new AitermError(`agent session '${name}' は起動時 prompt の完了待ちです。通常 pty_send は混入防止のため送信しません。` +
2649
2652
  `完了後に pty_send(wait:"agent_done") するか、手動介入が必要な場合だけ force:true を明示してください。`, 2);
2650
2653
  }
2654
+ // aiterm-wait の exit 契約(CLI と各所の案内文で共有する正)。exit≠完了: outcome が done の時だけ完了。
2655
+ export const AITERM_WAIT_OUTCOME_NOTE = `exit 0=done / 3=timeout(既定${DEFAULT_AGENT_DONE_TIMEOUT}秒・未完了) / 4=closed。receiptのoutcomeが正で、done以外は未完了`;
2651
2656
  // 外部waiterプロセス用の純リーダー観測。lock・PTY・metadata書込・dispatch状態には一切触れない。
2652
2657
  // event fileのtail規律(未終端行保持・増分上限)はwaitAgentDoneEventと同一だが、
2653
2658
  // vendor_session_idのbind永続化を行わない点だけ意図的に異なる(waiterは観測者であって所有者でない)。
@@ -2829,8 +2834,11 @@ export async function sendInitialAgentPrompt(name, text, o = {}) {
2829
2834
  setInitialPromptState(meta, "not_sent");
2830
2835
  const ready = await waitAgentTuiReady(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
2831
2836
  if (!ready.ready) {
2832
- return (`initial_prompt=not_sent vendor=${meta.kind} ready=false samples=${ready.samples}\n` +
2833
- `agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。prompt は送信していません。`);
2837
+ return {
2838
+ text: `initial_prompt=not_sent vendor=${meta.kind} ready=false samples=${ready.samples}\n` +
2839
+ `agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。prompt は送信していません。`,
2840
+ event_cursor: null,
2841
+ };
2834
2842
  }
2835
2843
  const startOffset = safeStatSize(meta.event_file);
2836
2844
  try {
@@ -2845,9 +2853,12 @@ export async function sendInitialAgentPrompt(name, text, o = {}) {
2845
2853
  setInitialPromptState(meta, "failed");
2846
2854
  throw e;
2847
2855
  }
2848
- return (`initial_prompt=pending vendor=${meta.kind} event_cursor=${startOffset}\n` +
2849
- `起動時 prompt を送信した。完了通知は aiterm-wait --session ${name} --cursor ${startOffset}(ホストのバックグラウンドタスクとして実行し、exit を完了通知にする)、` +
2850
- `回収は pty_read(agent_transcript:true) を使う。`);
2856
+ return {
2857
+ text: `initial_prompt=pending vendor=${meta.kind} event_cursor=${startOffset}\n` +
2858
+ `起動時 prompt を送信した。完了通知は aiterm-wait --session ${name} --cursor ${startOffset} をホストのバックグラウンドタスクとして実行し、` +
2859
+ `exit 時に receipt の outcome で判定する(${AITERM_WAIT_OUTCOME_NOTE})。回収は pty_read(agent_transcript:true) を使う。`,
2860
+ event_cursor: startOffset,
2861
+ };
2851
2862
  }
2852
2863
  export function isAgentSession(name) {
2853
2864
  assertSessionName(name);
@@ -3329,8 +3340,10 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
3329
3340
  }
3330
3341
  // v0.16.0: launcher は常に managed(Stop hook つき)で立つ。手動運転したい場合は
3331
3342
  // pty_open で素の PTY を開き、vendor CLI を自分で send する。
3343
+ // 第3要素は「起動時点でturnが走っているか」の event_cursor: Grok/Composer の argv prompt は
3344
+ // event file 新規作成直後の起動=境界0、prompt なしの起動は turn なし=null。
3332
3345
  if (!prompt || (kind !== "codex" && kind !== "claude")) {
3333
- return openAgent(kind, {
3346
+ const [sid, hint] = openAgent(kind, {
3334
3347
  session_name: opts.session_name ?? null,
3335
3348
  model: opts.model ?? null,
3336
3349
  reasoning_effort: opts.reasoning_effort ?? null,
@@ -3339,6 +3352,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
3339
3352
  agent_done: true,
3340
3353
  launch_operation_id: opts.launch_operation_id ?? null,
3341
3354
  });
3355
+ return [sid, hint, prompt ? 0 : null];
3342
3356
  }
3343
3357
  const [sid, hint] = openAgent(kind, {
3344
3358
  session_name: opts.session_name ?? null,
@@ -3353,7 +3367,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
3353
3367
  const initial = await sendInitialAgentPrompt(sid, prompt, {
3354
3368
  ready_timeout: opts.ready_timeout ?? undefined,
3355
3369
  });
3356
- return [sid, `${hint}\n${initial}`];
3370
+ return [sid, `${hint}\n${initial.text}`, initial.event_cursor];
3357
3371
  }
3358
3372
  catch (e) {
3359
3373
  const code = e instanceof AitermError ? e.code : 1;
package/dist/index.js CHANGED
@@ -89,7 +89,8 @@ server.registerTool("pty_send", {
89
89
  description: "セッションへテキストを送る。通常PTYへは送信のみ(出力は pty_read で取得)。" +
90
90
  "agent session(launcher起動)への send は自動で dispatch になる: TUI の ready gate と submit 分離を通して即返り、" +
91
91
  "receipt の event_cursor を返す=親はブロックしない。完了通知は `aiterm-wait --session <id> --cursor <event_cursor>` を" +
92
- "ホストのバックグラウンドタスクとして実行し、その exit で受ける(ポーリング不要)。" +
92
+ "ホストのバックグラウンドタスクとして実行し、exit時にreceiptのoutcomeで判定する" +
93
+ `(${core.AITERM_WAIT_OUTCOME_NOTE}。ポーリング不要)。` +
93
94
  "結果回収は pty_read(agent_transcript:true)、Claude の durable turn は claude_turn を使う。" +
94
95
  "force:true は agent session への手動介入用の素送信。",
95
96
  inputSchema: {
@@ -134,7 +135,8 @@ server.registerTool("pty_send", {
134
135
  {
135
136
  type: "text",
136
137
  text: `dispatchした(vendor=${receipt.vendor})。完了通知: aiterm-wait --session ${receipt.session_id} --cursor ${receipt.event_cursor} を` +
137
- "ホストのバックグラウンドタスクとして実行し exit を待つ。回収: pty_read(agent_transcript:true)",
138
+ `ホストのバックグラウンドタスクとして実行し、exit時にreceiptのoutcomeで判定(${core.AITERM_WAIT_OUTCOME_NOTE})。` +
139
+ "回収: pty_read(agent_transcript:true)",
138
140
  },
139
141
  ],
140
142
  structuredContent: {
@@ -342,6 +344,13 @@ const agentEffortDesc = (kind) => kind === "claude"
342
344
  "composer は effort 自体非対応)。指定すると起動前にエラーを返す"
343
345
  : "reasoning effort(思考レベル)。low/medium/high/xhigh/max/ultra(CLI 版依存)。" +
344
346
  "ultra は max 推論+proactive 自動委譲 ON=使用量急増注意(明示要求時のみ)。省略時は端末 config/CLI 既定。";
347
+ // 全launcher共通の完了受信ガイド。launch応答のwait_command(起動時promptあり時)/pty_send dispatchの
348
+ // event_cursorから組んだaiterm-waitをホストのバックグラウンドタスクとして1本実行し、exit時にreceiptの
349
+ // outcomeで判定する。
350
+ const agentCompletionDesc = `完了通知は起動応答の wait_command(初回prompt時)または pty_send dispatch 後の ` +
351
+ `aiterm-wait --session <id> --cursor <event_cursor> をホストのバックグラウンドタスクとして実行し、` +
352
+ `exit時にreceiptのoutcomeで判定する(${core.AITERM_WAIT_OUTCOME_NOTE}。ポーリング不要)。` +
353
+ `結果回収は pty_read(agent_transcript:true)。`;
345
354
  function registerAgentTool(toolName, kind, desc) {
346
355
  const correlatedLaunchSchema = {};
347
356
  if (kind === "claude") {
@@ -368,10 +377,14 @@ function registerAgentTool(toolName, kind, desc) {
368
377
  provider: z.literal(kind),
369
378
  session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
370
379
  managed_completion: z.boolean(),
380
+ // 起動時 prompt でturnが走っている時だけ非null(additive拡張)。wait_command はそのままホストの
381
+ // バックグラウンドタスクとして実行できる完了待ちコマンド。
382
+ event_cursor: z.number().int().nullable(),
383
+ wait_command: z.string().nullable(),
371
384
  },
372
385
  }, async ({ prompt, model, reasoning_effort, cwd, session_name, launch_operation_id }) => {
373
386
  try {
374
- const [sid, hint] = await core.openAgentWithInitialPrompt(kind, {
387
+ const [sid, hint, eventCursor] = await core.openAgentWithInitialPrompt(kind, {
375
388
  prompt: prompt ?? undefined,
376
389
  model: model ?? undefined,
377
390
  reasoning_effort: reasoning_effort ?? undefined,
@@ -384,6 +397,8 @@ function registerAgentTool(toolName, kind, desc) {
384
397
  provider: kind,
385
398
  session_id: sid,
386
399
  managed_completion: true,
400
+ event_cursor: eventCursor,
401
+ wait_command: eventCursor === null ? null : `aiterm-wait --session ${sid} --cursor ${eventCursor}`,
387
402
  };
388
403
  return {
389
404
  content: [{ type: "text", text: `session_id: ${sid}\n${hint}` }],
@@ -396,14 +411,22 @@ function registerAgentTool(toolName, kind, desc) {
396
411
  });
397
412
  }
398
413
  registerAgentTool("claude_agent", "claude", "【Claude Code (Anthropic)】の対話エージェントTUIを永続端末に起動する。`claude -p`ではなく、" +
399
- "同じ利用者可視sessionへpty_sendで継続入力する。常にmanaged(isolated settingsのStop hook)で起動し、完了通知はaiterm-wait、結果はpty_read(agent_transcript)/claude_turnで回収する。");
414
+ "同じ利用者可視sessionへpty_sendで継続入力する。常にmanaged(isolated settingsのStop hook)で起動する。" +
415
+ agentCompletionDesc +
416
+ "Claude の durable turn は claude_turn でも回収できる。");
400
417
  registerAgentTool("codex_agent", "codex", "【Codex (OpenAI)】の対話エージェント TUI を永続端末に起動する。実装・レビュー・調査を対話で回す。" +
401
- "起動後は pty_read で画面を読み pty_send で操作する。model / reasoning_effort を引数で指定可" +
418
+ "turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
419
+ agentCompletionDesc +
420
+ "model / reasoning_effort を引数で指定可" +
402
421
  "(省略時は端末 config/CLI 既定を継承。実効値は起動応答に明示)。");
403
422
  registerAgentTool("grok_agent", "grok", "【Grok Build の Grok モデル (既定 grok-4.5)】の対話エージェント TUI を永続端末に起動する。" +
404
- "起動後は pty_read/pty_send で対話操作。model を引数で指定可。reasoning_effort は対話 TUI 非対応(指定はエラー)。");
423
+ "turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
424
+ agentCompletionDesc +
425
+ "model を引数で指定可。reasoning_effort は対話 TUI 非対応(指定はエラー)。");
405
426
  registerAgentTool("composer_agent", "composer", "【Grok Build の Composer モデル (既定 grok-composer-2.5-fast)】の対話エージェント TUI を永続端末に起動する。" +
406
- "起動後は pty_read/pty_send で対話操作。model を引数で指定可。reasoning_effort は非対応(指定はエラー)。");
427
+ "turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
428
+ agentCompletionDesc +
429
+ "model を引数で指定可。reasoning_effort は非対応(指定はエラー)。");
407
430
  async function main() {
408
431
  const transport = new StdioServerTransport();
409
432
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "mcpName": "io.github.kitepon-rgb/aiterm-mcp",
5
5
  "description": "AI-driven persistent terminal as a local stdio MCP server (tmux-backed). Holds one local PTY; SSH and containers are just commands you send into it. Also launches interactive Claude/Codex/Grok/Composer agent TUIs in a persistent terminal. Token-reducing reads.",
6
6
  "keywords": [