aiterm-mcp 0.37.9 → 0.37.10

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.37.10] - 2026-09-21
11
+
12
+ ### 修正
13
+
14
+ - Codex 0.155.1 の「Approaching rate limits」model切替dialogを専用に認識する。通常の`pty_send`と`agent_configure`は同じsessionで一時的な「Keep current model」だけを選び、入力受付を再確認してから進める。model変更と今後の表示抑止は選ばない。復旧不能時はtyped errorで本文・設定変更を送らない。
15
+
10
16
  ## [0.37.9] - 2026-09-21
11
17
 
12
18
  ### 修正
@@ -1726,7 +1732,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
1726
1732
  `ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
1727
1733
  provenance.
1728
1734
 
1729
- [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.9...HEAD
1735
+ [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.10...HEAD
1736
+ [0.37.10]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.9...v0.37.10
1730
1737
  [0.37.9]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.8...v0.37.9
1731
1738
  [0.37.8]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.7...v0.37.8
1732
1739
  [0.37.7]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.6...v0.37.7
package/README.ja.md CHANGED
@@ -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.37.9** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
205
+ **状態:** 開発継続中 · 現行公開版 **v0.37.10** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
206
206
 
207
207
  ### 更新と巻き戻し
208
208
 
@@ -250,6 +250,8 @@ Grok/Composerがread-only sandboxの適用を拒否した場合、prompt送信
250
250
 
251
251
  この判定はGrok専用アダプターが所有し、同じCLIを使うComposerにも適用する。初回prompt付きの`agent_launch`と通常の`pty_send`で、入力受付待ち中に拒否を検出すると未送信のエラーを返す。promptなし・`trust_project`指定なしの起動応答は入力受付を保証しない。`trust_project:true`では入力受付まで確認し、`startup.status`を返す。Grokのprivacy notice起動設定も同アダプターが所有する。実装の責務分担は[DESIGN](docs/DESIGN.md#failure-and-recovery)を参照。
252
252
 
253
+ Codex 0.155.1の「Approaching rate limits」model切替dialogは、通常の`pty_send`と`agent_configure`で同じsessionのまま一時的な**2. Keep current model**だけを選ぶ。入力受付を再確認してから本文または設定変更を進め、dispatch receiptの`pane_input_recovery`には`codex_rate_limit_model_switch_kept_current`を記録する。model切替と今後の表示抑止は選ばない。入力受付へ戻らなければ`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILED`となり、本文・設定変更は未送信。このdialogは`agent_approval`の対象ではなく、inspectは`reason="rate_limit_model_switch"`だけを返し、prompt digestとchoicesを出さない。
254
+
253
255
  ```text
254
256
  agent_launch({ harness: "codex-cli", session_name: "codex1", cwd: "/repo",
255
257
  prompt: "port test/legacy.py to vitest",
package/README.md CHANGED
@@ -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.37.9** · 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).
219
+ **Status:** actively maintained · current public release **v0.37.10** · 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
 
@@ -270,6 +270,8 @@ Grok/Composerがread-only sandboxの適用を拒否した場合、prompt送信
270
270
 
271
271
  この判定はGrok専用アダプターが所有し、同じCLIを使うComposerにも適用する。初回prompt付きの`agent_launch`と通常の`pty_send`で、入力受付待ち中に拒否を検出すると未送信のエラーを返す。promptなし・`trust_project`指定なしの起動応答は入力受付を保証しない。`trust_project:true`では入力受付まで確認し、`startup.status`を返す。Grokのprivacy notice起動設定も同アダプターが所有する。実装の責務分担は[DESIGN](docs/DESIGN.md#failure-and-recovery)を参照。
272
272
 
273
+ Codex 0.155.1の「Approaching rate limits」model切替dialogは、通常の`pty_send`と`agent_configure`で同じsessionのまま一時的な**2. Keep current model**だけを選ぶ。入力受付を再確認してから本文または設定変更を進め、dispatch receiptの`pane_input_recovery`には`codex_rate_limit_model_switch_kept_current`を記録する。model切替と今後の表示抑止は選ばない。入力受付へ戻らなければ`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILED`となり、本文・設定変更は未送信。このdialogは`agent_approval`の対象ではなく、inspectは`reason="rate_limit_model_switch"`だけを返し、prompt digestとchoicesを出さない。
274
+
273
275
  For a correlated Claude turn stopped at `Do you want to proceed?`, use `claude_approval(action: "inspect", ...)` to capture the active operation and SHA-256 screen digest, review the displayed command, then call `respond` with that exact digest and either `approve_once` or `deny`. The relay rechecks the operation and screen under the send lock, never exposes arbitrary input or permanent approval, keeps the active marker intact, and records a prompt-free owner-only receipt. `pty_send(force: true)` does not bypass this boundary.
274
276
 
275
277
  ```text
package/dist/core.js CHANGED
@@ -19,7 +19,7 @@ import { AitermError, telemetryOwnedFailure, ownTelemetryFailure } from "./error
19
19
  import { isWin, SOCKDIR, tmuxCommand, sendPsmuxPayload, loadPtyBufferChunk, pasteBufferBaseArgs, TMUX_EMPTY_CONFIG, attachCommand, normalizePaneCommand, atomicShellMultiline, appendMarkSentinel, markShellCommand, settlePaneLog, paneCwdArgument, sessionEnvironmentLaunch, } from "./tmux-runtime.js";
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 { GROK_MODEL_DEFAULTS, 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
- import { bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, } from "./harnesses/codex.js";
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
23
  import { OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, claudePaneObservation, claudeStartupAction, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, claudeSessionTranscriptPath, claudeApiErrorFromLine, } from "./harnesses/claude.js";
24
24
  import { bindCursorTranscriptSession, cursorTurnBoundary, latestCursorCompletion, observeCursorDone, cursorTranscriptText, assertCursorAuthenticationReady, assertCursorModelAvailable, buildCursorAgentCmd, cursorPromptWithLineage, createCursorAgentMetadata, cursorLaunchNote, cursorEffortNavigation, cursorTuiReady, cursorPaneObservation, 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";
@@ -1414,7 +1414,7 @@ export function runAgentApproval(options) {
1414
1414
  const dialog = codexApprovalDialog(screen);
1415
1415
  if (!dialog) {
1416
1416
  result.status = state.state === "blocked" || state.state === "unknown" || action === "respond" ? "blocked" : "none";
1417
- result.reason = state.state === "blocked" ? "unknown_dialog" : state.state === "unknown" ? "unrecognized_screen" : "no_current_dialog";
1417
+ result.reason = state.state === "blocked" ? state.reason : state.state === "unknown" ? "unrecognized_screen" : "no_current_dialog";
1418
1418
  return result;
1419
1419
  }
1420
1420
  result.kind = dialog.kind;
@@ -3242,6 +3242,31 @@ function sendMenuChoice(name, choice) {
3242
3242
  throw new AitermError(`agent設定の選択を送れませんでした: ${sent.stderr.trim() || `code=${sent.code}`}`, 2);
3243
3243
  }
3244
3244
  }
3245
+ /** Codexの上限接近modalは承認APIへ出さず、一時keepの2だけを選んで共通ready gateへ戻す。 */
3246
+ async function recoverCodexRateLimitModelSwitch(name, meta) {
3247
+ const dialog = meta.kind === "codex"
3248
+ ? codexRateLimitModelSwitchDialog(captureScreen(name, AGENT_TUI_READY_LINES))
3249
+ : null;
3250
+ if (!dialog)
3251
+ return false;
3252
+ const live = observeSession(name);
3253
+ if (live.harness_alive !== true) {
3254
+ throw new AitermError(`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_BLOCKED: ${live.reason}。今回の文字列は送信していません。`, 2);
3255
+ }
3256
+ sendMenuChoice(name, String(dialog.keepCurrentIndex));
3257
+ return true;
3258
+ }
3259
+ /** ready待機中に現れたCodex利用上限modalも、同じ一時keep復旧へ戻す。 */
3260
+ async function waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, timeoutMs) {
3261
+ let codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
3262
+ let ready = await waitAgentTuiReady(name, meta, timeoutMs);
3263
+ if (!ready.ready && !codexRateLimitModelSwitch) {
3264
+ codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
3265
+ if (codexRateLimitModelSwitch)
3266
+ ready = await waitAgentTuiReady(name, meta, timeoutMs);
3267
+ }
3268
+ return { ready, codexRateLimitModelSwitch };
3269
+ }
3245
3270
  /** 同じ対話sessionを保ったまま、harness標準の操作でmodel/effortを変更する。 */
3246
3271
  export async function configureAgent(name, opts) {
3247
3272
  assertSessionName(name);
@@ -3254,9 +3279,13 @@ export async function configureAgent(name, opts) {
3254
3279
  }
3255
3280
  const meta = loadAgentMetadata(name);
3256
3281
  bindCompletedInitialPrompt(meta);
3257
- const ready = await waitAgentTuiReady(name, meta, AGENT_TUI_READY_TIMEOUT_MS);
3258
- if (!ready.ready)
3282
+ const { ready, codexRateLimitModelSwitch } = await waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, AGENT_TUI_READY_TIMEOUT_MS);
3283
+ if (!ready.ready) {
3284
+ if (codexRateLimitModelSwitch) {
3285
+ throw new AitermError(`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILED: ${codexPaneObservation(ready.lastScreen).reason}。設定変更は送信していません。`, 2);
3286
+ }
3259
3287
  throw new AitermError(`agent session '${name}' は入力待ちではありません`, 2);
3288
+ }
3260
3289
  if (meta.kind === "claude") {
3261
3290
  if (model) {
3262
3291
  await sendAgentPromptText(name, `/model ${model}`);
@@ -3441,6 +3470,7 @@ export async function dispatchAgentTurn(name, text, o = {}) {
3441
3470
  && agentCompletionCursor(meta) === 0
3442
3471
  && readClaudeOperationMarker(meta) === null;
3443
3472
  const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
3473
+ let codexRateLimitModelSwitch = false;
3444
3474
  const limitDialog = meta.kind === "grok" || meta.kind === "composer"
3445
3475
  ? grokRateLimitDialog(captureScreen(name, 0)) : null;
3446
3476
  if (limitDialog) {
@@ -3453,8 +3483,13 @@ export async function dispatchAgentTurn(name, text, o = {}) {
3453
3483
  sendKey(name, limitDialog.dismissKey);
3454
3484
  }
3455
3485
  if (meta.kind !== "claude" || claudeColdStart) {
3456
- const ready = await waitAgentTuiReady(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
3486
+ const recovery = await waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
3487
+ codexRateLimitModelSwitch = recovery.codexRateLimitModelSwitch;
3488
+ const { ready } = recovery;
3457
3489
  if (!ready.ready) {
3490
+ if (codexRateLimitModelSwitch) {
3491
+ throw new AitermError(`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILED: ${codexPaneObservation(ready.lastScreen).reason}。今回の文字列は送信していません。`, 2);
3492
+ }
3458
3493
  if (limitDialog) {
3459
3494
  const reason = grokPaneObservation(ready.lastScreen).reason;
3460
3495
  throw new AitermError(`GROK_RATE_LIMIT_RECOVERY_FAILED: ${reason}。上限パネル解除後の入力受付を確認できません。今回の文字列は送信していません。`, 2);
@@ -3463,6 +3498,8 @@ export async function dispatchAgentTurn(name, text, o = {}) {
3463
3498
  "少し後で pty_read(screen:true) を確認し、TUI が起動済みなら再度 pty_send してください。", 2);
3464
3499
  }
3465
3500
  }
3501
+ if (codexRateLimitModelSwitch)
3502
+ paneInputRecovery.push("codex_rate_limit_model_switch_kept_current");
3466
3503
  if (limitDialog)
3467
3504
  paneInputRecovery.push("grok_rate_limit_dialog_dismissed");
3468
3505
  const startOffset = agentCompletionCursor(meta);
@@ -390,7 +390,7 @@ function currentCodexDialog(screen) {
390
390
  // 過去の完了footerより前の質問・選択番号を、現在のdialogへ持ち込まない。
391
391
  const previousFooter = footers.at(-2);
392
392
  const current = previousFooter ? screen.slice(previousFooter.index + previousFooter[0].length) : screen;
393
- const heading = [...current.matchAll(/Would you like to run the following command\?|Allow the [^\n]+ MCP server to run tool|Hooks need review|Do you trust the contents of this directory|Update available!/g)].at(-1);
393
+ const heading = [...current.matchAll(/Would you like to run the following command\?|Allow the [^\n]+ MCP server to run tool|Hooks need review|Do you trust the contents of this directory|Update available!|Approaching rate limits/g)].at(-1);
394
394
  return heading ? current.slice(heading.index) : current;
395
395
  }
396
396
  export function codexPaneObservation(screen) {
@@ -400,7 +400,7 @@ export function codexPaneObservation(screen) {
400
400
  const tail = screen.split("\n").slice(-24).join("\n");
401
401
  // 現在のmodal footerがある時だけ、折返しで上へ出た質問を画面全体から探す。
402
402
  const lastComposer = [...tail.matchAll(/(?:^|\n)[ \t]*[›>](?![ \t]*\d+\.)/g)].at(-1)?.index ?? -1;
403
- const lastDialog = [...tail.matchAll(/Press enter to confirm or esc to (?:cancel|go back)|enter to submit\s*\|\s*esc to cancel|Would you like to run the following command\?|Allow the [^\n]+ MCP server to run tool|Hooks need review|Do you trust the contents of this directory|Update available!/gi)].at(-1)?.index ?? -1;
403
+ const lastDialog = [...tail.matchAll(/Press enter to confirm or esc to (?:cancel|go back)|enter to submit\s*\|\s*esc to cancel|Would you like to run the following command\?|Allow the [^\n]+ MCP server to run tool|Hooks need review|Do you trust the contents of this directory|Update available!|Approaching rate limits/gi)].at(-1)?.index ?? -1;
404
404
  const modal = lastDialog > lastComposer;
405
405
  if (!modal) {
406
406
  if (/esc to interrupt/i.test(tail))
@@ -415,12 +415,35 @@ export function codexPaneObservation(screen) {
415
415
  return { state: "blocked", reason: "mcp_approval" };
416
416
  if (/Hooks need review/.test(current))
417
417
  return { state: "blocked", reason: "hooks_review" };
418
+ if (codexRateLimitModelSwitchDialog(current))
419
+ return { state: "blocked", reason: "rate_limit_model_switch" };
418
420
  if (codexLaunchBlockingDialog(current))
419
421
  return { state: "blocked", reason: "startup_dialog" };
420
422
  if (modal)
421
423
  return { state: "blocked", reason: "unknown_dialog" };
422
424
  return { state: "unknown", reason: "unrecognized_screen" };
423
425
  }
426
+ /**
427
+ * Codex 0.155.1 の利用上限接近modal。agent_approvalの対象ではないため、選択肢は公開しない。
428
+ * 1はmodel切替、3は今後の表示抑止なので、Aitermが選んでよいのは一時keepの2だけである。
429
+ */
430
+ export function codexRateLimitModelSwitchDialog(screen) {
431
+ const current = currentCodexDialog(screen);
432
+ const footer = current.lastIndexOf("Press enter to confirm or esc to go back");
433
+ const composer = [...current.matchAll(/(?:^|\n)[ \t]*[›>](?![ \t]*\d+\.)/g)].at(-1)?.index ?? -1;
434
+ // 現在のcomposerがmodal footerより後なら、scrollbackの古いmodalである。
435
+ if (composer > footer)
436
+ return null;
437
+ if (!/(?:^|\n)\s*Approaching rate limits\s*(?:\n|$)/.test(current)
438
+ || !/(?:^|\n)\s*Switch to gpt-5\.6-luna for lower credit usage\?\s*(?:\n|$)/.test(current)
439
+ || !/(?:^|\n)[ \t]*(?:›[ \t]*)?1\. Switch to gpt-5\.6-luna(?:[ \t]{2,}[^\n]*)?[ \t]*(?:\n|$)/.test(current)
440
+ || !/(?:^|\n)[ \t]*(?:›[ \t]*)?2\. Keep current model[ \t]*(?:\n|$)/.test(current)
441
+ || !/(?:^|\n)[ \t]*(?:›[ \t]*)?3\. Keep current model \(never show again\)(?:[ \t]{2,}[^\n]*)?[ \t]*(?:\n|$)/.test(current)
442
+ || !/Press enter to confirm or esc to go back/i.test(current))
443
+ return null;
444
+ const selected = current.match(/(?:^|\n)[ \t]*›[ \t]*([123])\. (?:Switch to gpt-5\.6-luna|Keep current model(?: \(never show again\))?)(?:[ \t]{2,}[^\n]*)?[ \t]*(?:\n|$)/)?.[1];
445
+ return { keepCurrentIndex: 2, selectedIndex: selected ? Number(selected) : null };
446
+ }
424
447
  export function codexStartupFailure(screen) {
425
448
  return /(?:^|\n)[ \t]*[›>]?[ \t]*Error loading config\.toml:/m.test(screen)
426
449
  ? "configuration_error" : null;
@@ -20,12 +20,10 @@ Grok/Composerのsandbox起動拒否については、[DESIGNの失敗と復旧
20
20
  Codex親への回答の自動配送は[DESIGN](DESIGN.md#codex親への自動配送)に、対応範囲と失敗時の状態を置く。
21
21
  Claude Code親の非同期hookによる受信は[DESIGN](DESIGN.md#claude-code親への自動配送)を参照する。
22
22
 
23
- ## 実装待ちの設計
24
-
25
- - [Grokの過去の利用上限表示からの復帰](https://github.com/kitepon/aiterm-mcp/blob/main/docs/grok-rate-limit-recovery-plan.md): 設計完了・実装未着手。通常dispatchでのパネル解除、現在の画面による上限観測、試験と実機受入を定める。
26
-
27
23
  ## 履歴と証拠
28
24
 
25
+ - [Grokの上限パネル復帰の受入](https://github.com/kitepon/aiterm-mcp/blob/main/docs/adr/0069-grok-rate-limit-recovery.md): 通常送信による解除、公開package試験、本番導入と実機未検証範囲。
26
+
29
27
  - [`archive/`](https://github.com/kitepon/aiterm-mcp/tree/main/docs/archive): 完了・棄却・中断・失効・置換により現行制御から外れたsnapshot。
30
28
  - [`adr/`](https://github.com/kitepon/aiterm-mcp/tree/main/docs/adr): 採択・棄却した設計判断とrelease受入。
31
29
  - [`evidence/`](https://github.com/kitepon/aiterm-mcp/tree/main/docs/evidence): Lattice/campaignの検証証拠。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.37.9",
3
+ "version": "0.37.10",
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": [