aiterm-mcp 0.41.0 → 0.41.2

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 CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.41.2] - 2026-09-27
11
+
12
+ ### 修正
13
+
14
+ - Claude Codeのtool処理中に画面の実行中表示が消えると、`pty_send`が新規turnと誤判定し、未解決turnエラーで送信を拒否していた。Stopまで保持されるturnの印で実行中を判定し、同じturnへ差し込む。
15
+
16
+ ## [0.41.1] - 2026-09-27
17
+
18
+ ### 修正
19
+
20
+ - CodexとCursorの回答回収(`pty_read(agent_transcript:true)`と親への自動配送)が、作業途中の報告を回答の前につないで返していた。Codexはturnのassistant本文を`phase`を見ずに全部つなぎ、Cursorは道具呼び出しの前に書いた文も含めていた。Codexは`phase=final_answer`の本文だけを、Cursorは最後の道具呼び出しより後の本文だけを返す。`phase`の無い旧形式のCodex記録と、道具呼び出し後に本文の無いCursorのturnは従来どおり扱う。Claude CodeとGrokは元から最後の回答だけを返している。
21
+
10
22
  ## [0.41.0] - 2026-09-27
11
23
 
12
24
  ### 削除
@@ -1810,7 +1822,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
1810
1822
  `ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
1811
1823
  provenance.
1812
1824
 
1813
- [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.0...HEAD
1825
+ [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.2...HEAD
1826
+ [0.41.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.1...v0.41.2
1827
+ [0.41.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.0...v0.41.1
1814
1828
  [0.41.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.3...v0.41.0
1815
1829
  [0.40.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.2...v0.40.3
1816
1830
  [0.40.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.1...v0.40.2
package/README.ja.md CHANGED
@@ -203,7 +203,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
203
203
  場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
204
204
  Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
205
205
 
206
- **状態:** 開発継続中 · 現行公開版 **v0.41.0** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
206
+ **状態:** 開発継続中 · 現行公開版 **v0.41.2** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
207
207
 
208
208
  ### 更新と巻き戻し
209
209
 
@@ -249,7 +249,7 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
249
249
 
250
250
  同じ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・置換しない。
251
251
 
252
- `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
+ `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が送る時点の画面で見て振り分ける。Claudeは画面の実行中表示ではなくStopまで残るturnの印で判定する。子の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を返さず失敗する。
253
253
 
254
254
  `agent_launch`・`pty_send`(agent session宛て)は任意の`image`(画像ファイルの絶対パスの配列。png/jpg/jpeg/gif/webp)を受ける。aitermが本文末尾へ添付行を付け、どのharnessも自分のfile読取toolでそのpathを画像として開く。呼出し側はharness別の添付手順を覚えない。不正なpathは送信前に拒否する。
255
255
 
package/README.md CHANGED
@@ -217,7 +217,7 @@ collection is off by default and performs no network I/O. It ships via
217
217
  tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
218
218
  Release re-registers the Official MCP Registry entry.
219
219
 
220
- **Status:** actively maintained · current public release **v0.41.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
+ **Status:** actively maintained · current public release **v0.41.2** · 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).
221
221
 
222
222
  ### Update and rollback
223
223
 
@@ -270,7 +270,7 @@ pty_read(id, { wait: true }) → read the token-reduced output, completion
270
270
 
271
271
  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`.
272
272
 
273
- 起動結果には正規`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
+ 起動結果には正規`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`](#completion-push-for-parent-agents-aiterm-wait)を使う。CursorのsubmitはadapterがCLIのextended keyboard protocolへ変換し、送信本文がcomposerへ残る場合は明示errorにする。
274
274
 
275
275
  `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.
276
276
 
package/dist/core.js CHANGED
@@ -3685,10 +3685,10 @@ export async function sendAgentMessage(name, text, o = {}) {
3685
3685
  // 前面回復は busy 判定より先(bash 前面のままだと画面の実行中マーカーを読んでも打鍵が届かない)。
3686
3686
  const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
3687
3687
  let running = isAgentTuiBusy(meta.kind, captureScreen(name, AGENT_TUI_READY_LINES));
3688
- // Claude CodeはStop hookの実行中も実行中の表示を続ける。Aitermのturnの印はStopで消えるので、
3689
- // 印が無ければturnは終わっている。ここで差し込むと新しいturnとして始まり、誰もその完了を待たない。
3690
- if (meta.kind === "claude" && readClaudeOperationMarker(meta) === null)
3691
- running = false;
3688
+ // Claudeのtool処理中は画面のbusy表示が消えることがある。Stopまで保持するturnの印を正とし、
3689
+ // 印がある間は差し込み、Stop後は画面にbusy表示が残っても新しいturnとして送る。
3690
+ if (meta.kind === "claude")
3691
+ running = readClaudeOperationMarker(meta) !== null;
3692
3692
  if (running) {
3693
3693
  return steerRunningTurn(name, meta, text, { raw: o.raw, pane_input_recovery: paneInputRecovery });
3694
3694
  }
@@ -588,7 +588,7 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
588
588
  const lines = readTranscriptLines(transcript);
589
589
  if (exactCompletion) {
590
590
  // 同じturnの本文だけを使う。完了event内の本文と、turn ID付きoutput_textの両形式を扱う。
591
- const matching = [];
591
+ const matching = codexTurnAnswer();
592
592
  for (const line of lines) {
593
593
  let record;
594
594
  try {
@@ -598,24 +598,19 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
598
598
  continue;
599
599
  }
600
600
  const payload = record?.payload;
601
- if (record?.type === "response_item" && payload?.type === "message" && payload?.role === "assistant"
602
- && payload?.internal_chat_message_metadata_passthrough?.turn_id === turnId && Array.isArray(payload?.content)) {
603
- for (const item of payload.content) {
604
- if (item?.type === "output_text" && typeof item.text === "string")
605
- matching.push(item.text);
606
- }
607
- }
601
+ matching.add(record, turnId);
608
602
  if (record?.type === "event_msg" && payload?.type === "task_complete" && payload.turn_id === turnId) {
609
603
  if (typeof payload.last_agent_message === "string")
610
604
  return payload.last_agent_message;
611
- if (matching.length > 0)
612
- return matching.join("\n");
605
+ const text = matching.text();
606
+ if (text)
607
+ return text;
613
608
  transcriptUnavailable();
614
609
  }
615
610
  }
616
611
  transcriptUnavailable();
617
612
  }
618
- const matching = [];
613
+ const matching = codexTurnAnswer();
619
614
  let finalAnswer = "";
620
615
  for (const line of lines) {
621
616
  if (!line.trim())
@@ -628,16 +623,7 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
628
623
  continue;
629
624
  }
630
625
  const payload = record?.payload;
631
- if (record?.type === "response_item" &&
632
- payload?.type === "message" &&
633
- payload?.role === "assistant" &&
634
- payload?.internal_chat_message_metadata_passthrough?.turn_id === turnId &&
635
- Array.isArray(payload?.content)) {
636
- for (const item of payload.content) {
637
- if (item?.type === "output_text" && typeof item.text === "string")
638
- matching.push(item.text);
639
- }
640
- }
626
+ matching.add(record, turnId);
641
627
  if (record?.type === "event_msg" &&
642
628
  payload?.type === "agent_message" &&
643
629
  payload?.phase === "final_answer" &&
@@ -645,7 +631,31 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
645
631
  finalAnswer = payload.message;
646
632
  }
647
633
  }
648
- return matching.join("\n") || finalAnswer;
634
+ return matching.text() || finalAnswer;
635
+ }
636
+ // turnのassistant本文を集める。Codexは作業途中の報告をphase=commentary、回答をphase=final_answerで残すので、
637
+ // final_answerがあるturnは途中の報告を回答に含めない。phaseの無い旧形式はturnの本文を全部つなぐ。
638
+ function codexTurnAnswer() {
639
+ const all = [];
640
+ const finals = [];
641
+ return {
642
+ add(record, turnId) {
643
+ const payload = record?.payload;
644
+ if (record?.type !== "response_item" || payload?.type !== "message" || payload?.role !== "assistant"
645
+ || payload?.internal_chat_message_metadata_passthrough?.turn_id !== turnId || !Array.isArray(payload?.content))
646
+ return;
647
+ for (const item of payload.content) {
648
+ if (item?.type !== "output_text" || typeof item.text !== "string")
649
+ continue;
650
+ all.push(item.text);
651
+ if (payload.phase === "final_answer")
652
+ finals.push(item.text);
653
+ }
654
+ },
655
+ text() {
656
+ return (finals.length > 0 ? finals : all).join("\n");
657
+ },
658
+ };
649
659
  }
650
660
  export function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}, writeScope, lineageContext) {
651
661
  const launchId = randomBytes(16).toString("hex");
@@ -235,7 +235,8 @@ export function cursorTranscriptText(meta, readTranscriptLines, transcriptUnavai
235
235
  const transcript = cursorTranscript(meta);
236
236
  if (!transcript)
237
237
  transcriptUnavailable();
238
- let current = [];
238
+ // tool_useで区切った本文の塊を持つ。道具を呼ぶ前の文は作業途中の報告なので、回答は最後の塊だけにする。
239
+ let current = [[]];
239
240
  let completed = null;
240
241
  for (const line of readTranscriptLines(transcript)) {
241
242
  if (!line.trim())
@@ -248,19 +249,21 @@ export function cursorTranscriptText(meta, readTranscriptLines, transcriptUnavai
248
249
  continue;
249
250
  }
250
251
  if (record?.role === "user")
251
- current = [];
252
+ current = [[]];
252
253
  if (record?.role === "assistant" && Array.isArray(record?.message?.content)) {
253
254
  for (const part of record.message.content) {
254
255
  if (part?.type === "text" && typeof part?.text === "string")
255
- current.push(part.text);
256
+ current.at(-1).push(part.text);
257
+ else if (part?.type === "tool_use" && current.at(-1).length > 0)
258
+ current.push([]);
256
259
  }
257
260
  }
258
261
  if (record?.type === "turn_ended")
259
- completed = [...current];
262
+ completed = current.map((chunk) => [...chunk]);
260
263
  }
261
264
  if (completed === null)
262
265
  transcriptUnavailable();
263
- return completed.join("\n");
266
+ return (completed.filter((chunk) => chunk.length > 0).at(-1) ?? []).join("\n");
264
267
  }
265
268
  export function createCursorAgentMetadata(name, cwd, initialPrompt, writeScope, lineage) {
266
269
  const launchId = randomBytes(16).toString("hex");
package/docs/DESIGN.md CHANGED
@@ -67,7 +67,7 @@ Grokの記録先はCLIと同じOS絶対パスへcwdを正規化して導出し
67
67
  agent sessionへの送信口は`pty_send`だけとする。子の状態は呼び出し側に選ばせず、Aitermが送る時点の画面で振り分ける。
68
68
  状態を見てから呼ぶまでの間に子のturnが変わるため、呼び出し側が入口を選ぶ形では外れる。
69
69
  実行中なら現在のturnへ追加textを差し込み、新しい完了境界と配送は作らない。完了境界は差し込み後も1つに保つ。
70
- Claude CodeはStop hookの実行中も実行中の表示を続けるため、Aitermのturnの印(Stopで消える)が無ければ実行中と数えない。
70
+ Claude Codeはtool処理中に画面の実行中表示が消え、Stop hookの実行中には表示が残る。Stopで消えるturnの印だけを実行中判定の正本とする。
71
71
  それ以外は新しいturnとしてdispatchする。
72
72
  CodexとClaude Codeは次のtool境界で同じturnへ取り込む。Cursorは「follow-ups」枠へ入った文を「enter steer」で現在turnへ移し、`turn_ended`は最後に1回書く。
73
73
  Grokは待ち行列へ入れた後に「send now」を押す。旧turnは`cancelled`(`cancellation_context.trigger=send_now`)で閉じ、
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.41.0",
3
+ "version": "0.41.2",
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": [