aiterm-mcp 0.41.2 → 0.41.3

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,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.41.3] - 2026-09-28
11
+
12
+ ### 修正
13
+
14
+ - Claude CodeがAPIエラーや安全判定の拒否でターンを終了し、Stop hookが発火しなかった場合、実行中の印が残り、次の`pty_send`を差し込みと誤判定していた。送信時に現在の印の作成後に記録された`isApiErrorMessage`を確認し、終了したターンの印だけを解除して新しいターンを開始する。過去のエラーで次のターンの印を解除せず、waiterは引き続き読取専用とする。上流APIの拒否はそのままエラーとして返す。
15
+
10
16
  ## [0.41.2] - 2026-09-27
11
17
 
12
18
  ### 修正
@@ -1822,7 +1828,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
1822
1828
  `ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
1823
1829
  provenance.
1824
1830
 
1825
- [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.2...HEAD
1831
+ [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.3...HEAD
1832
+ [0.41.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.2...v0.41.3
1826
1833
  [0.41.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.1...v0.41.2
1827
1834
  [0.41.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.0...v0.41.1
1828
1835
  [0.41.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.3...v0.41.0
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.2** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
206
+ **状態:** 開発継続中 · 現行公開版 **v0.41.3** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
207
207
 
208
208
  ### 更新と巻き戻し
209
209
 
@@ -251,6 +251,8 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
251
251
 
252
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
+ ClaudeがAPIエラーや安全判定の拒否で終了した時は、Stop hookが発火しなくても次の`pty_send`を新しいturnとして扱う。現在のturn開始後のエラー記録だけを確認し、過去のエラーで実行中のturnを解除しない。上流の拒否はエラーのまま返す。
255
+
254
256
  `agent_launch`・`pty_send`(agent session宛て)は任意の`image`(画像ファイルの絶対パスの配列。png/jpg/jpeg/gif/webp)を受ける。aitermが本文末尾へ添付行を付け、どのharnessも自分のfile読取toolでそのpathを画像として開く。呼出し側はharness別の添付手順を覚えない。不正なpathは送信前に拒否する。
255
257
 
256
258
  `agent_launch`は任意の`write_scope`も受ける。Codex/Grokのread-onlyは`--sandbox read-only`、Cursorは公式`--mode ask`で実効化する。path説明は同等CLI引数がないためdeclaration-only。
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.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).
220
+ **Status:** actively maintained · current public release **v0.41.3** · 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
 
@@ -251,6 +251,8 @@ aiterm predates Build Week, so the event work is kept visible in dated commits.
251
251
 
252
252
  I used **Codex with GPT-5.6** as an engineering collaborator: it inspected the implementation, challenged the API and recovery contracts, generated focused regression cases, and helped verify race, security, timeout, and malformed-event paths. I reviewed the diffs and test evidence and retained the final product and architecture decisions. At that Build Week checkpoint, the regression suite contained 262 tests covering normal operation as well as failure and recovery behavior; current release receipts live in the [CHANGELOG](CHANGELOG.md) and release ADRs.
253
253
 
254
+ ClaudeがAPIエラーや安全判定の拒否で終了した時は、Stop hookが発火しなくても次の`pty_send`を新しいturnとして扱う。現在のturn開始後のエラー記録だけを確認し、過去のエラーで実行中のturnを解除しない。上流の拒否はエラーのまま返す。
255
+
254
256
  ## Two ways to use it
255
257
 
256
258
  ### 1. Drive SSH, containers, and REPLs in one persistent terminal — the primitive
package/dist/core.js CHANGED
@@ -20,7 +20,7 @@ import { isWin, SOCKDIR, tmuxCommand, sendPsmuxPayload, loadPtyBufferChunk, past
20
20
  import { sleep, currentUid, runtimeStateBase, safeStatSize, readFileRange, writeJson0600, createEmpty0600, shq, LAUNCH_ID_RE, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, assertSessionName, agentsDir, agentEventPath, agentMetadataPath, writeAgentMetadata, AGENT_EVENT_TAIL_BYTES, agentLabel, agentHarness, subagentInstruction, agentLineageFields, } from "./agent-shared.js";
21
21
  import { realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokEventsTranscript, latestGrokCompletion, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, grokTuiBusy, grokPaneObservation, grokRateLimitDialog, grokStartupAction, grokLaunchBlockingDialog, assertGrokSandboxNotRejected, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./harnesses/grok.js";
22
22
  import { bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexRateLimitModelSwitchDialog, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, } from "./harnesses/codex.js";
23
- import { OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, claudePaneObservation, claudeStartupAction, claudeLoginMethodMenu, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, claudeSessionTranscriptPath, claudeApiErrorFromLine, } from "./harnesses/claude.js";
23
+ import { OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, claudePaneObservation, claudeStartupAction, claudeLoginMethodMenu, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, claudeSessionTranscriptPath, claudeApiErrorFromLine, claudeApiErrorAfter, } from "./harnesses/claude.js";
24
24
  import { bindCursorTranscriptSession, cursorTurnBoundary, latestCursorCompletion, observeCursorDone, cursorTranscriptText, assertCursorAuthenticationReady, assertCursorModelAvailable, buildCursorAgentCmd, cursorAgentArgv, cursorPwshLaunchLine, cursorPromptWithLineage, createCursorAgentMetadata, cursorLaunchNote, cursorEffortNavigation, cursorTuiReady, cursorPaneObservation, cursorPromptHooksRunning, cursorUsageLimit, CURSOR_SUBMIT_SEQUENCE, CURSOR_COMPOSER_CONTENT_MARKER_RE, validateCursorModelEffort, } from "./harnesses/cursor.js";
25
25
  import { resolveAgentBin, resolveThroughlineBin, runThroughlineHandoffContext, isWindowsNativeExecutable, agentBinForPaneShell, resolveWinPaneShell } from "./agent-resolver.js";
26
26
  export { AitermError } from "./errors.js";
@@ -1562,6 +1562,30 @@ function managedClaudeOperation(name) {
1562
1562
  return undefined;
1563
1563
  return readClaudeOperationMarker(meta);
1564
1564
  }
1565
+ function clearClaudeApiErrorOperation(meta) {
1566
+ const file = agentClaudeOperationPath(meta.aiterm_session, meta.launch_id);
1567
+ let marker;
1568
+ try {
1569
+ marker = fs.statSync(file);
1570
+ }
1571
+ catch (error) {
1572
+ if (error.code === "ENOENT")
1573
+ return;
1574
+ throw error;
1575
+ }
1576
+ if (!claudeApiErrorAfter(meta, marker.mtimeMs))
1577
+ return;
1578
+ // 読取中にStopや次のturnが印を置換した場合は、その新しい印を消さない。
1579
+ try {
1580
+ const current = fs.statSync(file);
1581
+ if (current.dev === marker.dev && current.ino === marker.ino)
1582
+ fs.unlinkSync(file);
1583
+ }
1584
+ catch (error) {
1585
+ if (error.code !== "ENOENT")
1586
+ throw error;
1587
+ }
1588
+ }
1565
1589
  function canonicalClaudeApprovalScreen(screen) {
1566
1590
  return stripControl(screen)
1567
1591
  .split("\n")
@@ -3686,9 +3710,12 @@ export async function sendAgentMessage(name, text, o = {}) {
3686
3710
  const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
3687
3711
  let running = isAgentTuiBusy(meta.kind, captureScreen(name, AGENT_TUI_READY_LINES));
3688
3712
  // Claudeのtool処理中は画面のbusy表示が消えることがある。Stopまで保持するturnの印を正とし、
3689
- // 印がある間は差し込み、Stop後は画面にbusy表示が残っても新しいturnとして送る。
3690
- if (meta.kind === "claude")
3713
+ // APIエラーでStopが来なかった印は、会話記録の終了時刻を確認して消す。
3714
+ // 有効な印がある間は差し込み、終了後は画面にbusy表示が残っても新しいturnとして送る。
3715
+ if (meta.kind === "claude") {
3716
+ clearClaudeApiErrorOperation(meta);
3691
3717
  running = readClaudeOperationMarker(meta) !== null;
3718
+ }
3692
3719
  if (running) {
3693
3720
  return steerRunningTurn(name, meta, text, { raw: o.raw, pane_input_recovery: paneInputRecovery });
3694
3721
  }
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
9
9
  import { AitermError } from "../errors.js";
10
10
  import { modeBitsWorldAccessible } from "../tmux-runtime.js";
11
11
  import { spawnAgentControlCommand } from "../agent-resolver.js";
12
- import { currentUid, writeJson0600, shq, subagentInstruction, writeScopeLaunchNote, agentEventPath, createEmpty0600, writeAgentMetadata, agentLineageFields, assertSessionName, agentsDir, LAUNCH_ID_RE, } from "../agent-shared.js";
12
+ import { currentUid, writeJson0600, shq, subagentInstruction, writeScopeLaunchNote, agentEventPath, createEmpty0600, writeAgentMetadata, agentLineageFields, assertSessionName, agentsDir, LAUNCH_ID_RE, AGENT_EVENT_TAIL_BYTES, readFileRange, } from "../agent-shared.js";
13
13
  export const OPERATION_ID_RE = /^sha256:[0-9a-f]{64}$/;
14
14
  export const CLAUDE_RESULT_MAX_BYTES = 4 * 1024 * 1024;
15
15
  export const CLAUDE_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
@@ -310,3 +310,26 @@ export function claudeApiErrorFromLine(line) {
310
310
  at: typeof record?.timestamp === "string" ? record.timestamp : null,
311
311
  };
312
312
  }
313
+ // Stopが発火しないエラー終了を、現在のturn開始後の会話記録だけから判定する。
314
+ export function claudeApiErrorAfter(meta, startedAtMs) {
315
+ const file = claudeSessionTranscriptPath(meta);
316
+ if (!file)
317
+ return null;
318
+ let size;
319
+ try {
320
+ size = fs.statSync(file).size;
321
+ }
322
+ catch (error) {
323
+ if (error.code === "ENOENT")
324
+ return null;
325
+ throw error;
326
+ }
327
+ const lines = readFileRange(file, Math.max(0, size - AGENT_EVENT_TAIL_BYTES), size).toString("utf8").split("\n");
328
+ lines.pop(); // 改行まで書かれた記録だけを扱う。
329
+ for (const line of lines.reverse()) {
330
+ const error = claudeApiErrorFromLine(line);
331
+ if (error?.at && Date.parse(error.at) >= startedAtMs)
332
+ return error;
333
+ }
334
+ return null;
335
+ }
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はtool処理中に画面の実行中表示が消え、Stop hookの実行中には表示が残る。Stopで消えるturnの印だけを実行中判定の正本とする。
70
+ Claude Codeはtool処理中に画面の実行中表示が消え、Stop hookの実行中には表示が残る。turnの印を実行中判定の正本とする。Stopが発火しないAPIエラー終了では、次の送信時に印の作成後の会話記録にある`isApiErrorMessage`を確認し、終了したturnの印だけを解除する。過去のエラーで新しい印を解除しない。waiterは印を変更せず、読取専用のままエラーを返す。
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.2",
3
+ "version": "0.41.3",
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": [