aiterm-mcp 0.37.7 → 0.37.9

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,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.37.9] - 2026-09-21
11
+
12
+ ### 修正
13
+
14
+ - Grok/Composerで前のターンのweekly-limitパネルが残っていても、通常の`pty_send`が一度閉じて今回の本文を送れるようにした。同じsessionを保ち、解除記録をreceiptへ返す。
15
+ - Grokの利用上限は現在の画面から判定する。古い上限logによる新しいターンの待機打切りと、入力欄に重なったprivacy案内による誤停止を修正した。
16
+
17
+ ## [0.37.8] - 2026-09-19
18
+
19
+ ### 修正
20
+
21
+ - WindowsのCodex親hookをPowerShellの呼出し演算子付きで登録する。引用した実行パスが構文エラーになり、承認済みでも同じターンへ回答が届かなかった問題を修正した。
22
+ - 公式hook試験でWindowsの実行環境を保持し、別窓のNode起動を防ぐ。回答の取得記録とhookイベントを親のターンIDまで照合する。
23
+
10
24
  ## [0.37.7] - 2026-09-19
11
25
 
12
26
  ### 修正
@@ -1712,7 +1726,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
1712
1726
  `ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
1713
1727
  provenance.
1714
1728
 
1715
- [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.7...HEAD
1729
+ [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.9...HEAD
1730
+ [0.37.9]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.8...v0.37.9
1731
+ [0.37.8]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.7...v0.37.8
1716
1732
  [0.37.7]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.6...v0.37.7
1717
1733
  [0.37.6]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.5...v0.37.6
1718
1734
  [0.37.5]: https://github.com/kitepon/aiterm-mcp/compare/v0.37.4...v0.37.5
package/README.ja.md CHANGED
@@ -63,6 +63,7 @@ Codexの起動プログラムと通常のstdio通信は変更しません。hook
63
63
  `aiterm-setup`は`CODEX_HOME/hooks.json`へ専用の`PostToolUse`と`Stop`を追加し、公式APIでその2件だけを承認・読戻しします。
64
64
  他のhookや承認は保持します。選択と配送の所有記録は`~/.config/aiterm-mcp/codex-parent-hooks/`へ保存します。
65
65
  同じ設定で再実行しても既存hookの順序を変えず、新たな再起動要求を発生させません。
66
+ WindowsのhookはPowerShell 7で実行します。更新後のsetupで、既存のAiterm hookコマンドも更新します。
66
67
  既存の中継は新しいhookの確認後に解除し、保存していた`CODEX_CLI_PATH`を復元します。macOSの専用LaunchAgentも解除します。
67
68
  移行前から動いているCodexがあれば`restart_required`(終了コード3)を返します。完全終了・再起動後に
68
69
  `aiterm-setup --codex-steer status`で`ready`を確認してください。旧設定は移行を実行するまで維持します。
@@ -201,7 +202,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
201
202
  場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
202
203
  Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
203
204
 
204
- **状態:** 開発継続中 · 現行公開版 **v0.37.7** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
205
+ **状態:** 開発継続中 · 現行公開版 **v0.37.9** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
205
206
 
206
207
  ### 更新と巻き戻し
207
208
 
@@ -243,6 +244,8 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
243
244
 
244
245
  Grok/Composerの無人起動は公式`--trust`で指定された作業フォルダを信頼登録し、確認画面を完了してから初回promptを送る。この登録はGrok CLIの信頼ストアへ保存され、フォルダ内のhook・MCP・LSPにも適用される。read-only sandboxの制限は維持する。画面に残る完了済みhookの結果は実行中と判定しない。
245
246
 
247
+ Grok/Composerで終了済みターンのweekly-limitパネルが残っている場合、次の通常`pty_send`が`Shift+X`で一度閉じ、入力受付を確認して今回の本文を送る。同じsessionと会話を保ち、receiptの`pane_input_recovery`に`grok_rate_limit_dialog_dismissed`を記録する。ターン未終了・harness不在は`GROK_RATE_LIMIT_RECOVERY_BLOCKED`、解除後の入力受付失敗は`GROK_RATE_LIMIT_RECOVERY_FAILED`となり、本文は未送信。上限の継続は`rate_limited`として返し、過去promptは再送しない。Grokの上限観測には現在の画面だけを使う。
248
+
246
249
  Grok/Composerがread-only sandboxの適用を拒否した場合、prompt送信時に`GROK_SANDBOX_STARTUP_FAILED`とCLIの原因を返す。例えばhookのパスにシンボリックリンクがあるとGrok CLIは起動を拒否する。設定の管理元で原因を修正し、対象sessionを`pty_close`して起動し直す。Aitermはsandboxを解除したりhookをコピーしたりしない。
247
250
 
248
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)を参照。
package/README.md CHANGED
@@ -63,6 +63,7 @@ Codexの起動プログラムと通常のstdio通信は変更しません。hook
63
63
  `aiterm-setup`は`CODEX_HOME/hooks.json`へ専用の`PostToolUse`と`Stop`を追加し、公式APIでその2件だけを承認・読戻しします。
64
64
  他のhookや承認は保持します。選択と配送の所有記録は`~/.config/aiterm-mcp/codex-parent-hooks/`へ保存します。
65
65
  同じ設定で再実行しても既存hookの順序を変えず、新たな再起動要求を発生させません。
66
+ WindowsのhookはPowerShell 7で実行します。更新後のsetupで、既存のAiterm hookコマンドも更新します。
66
67
  既存の中継は新しいhookの確認後に解除し、保存していた`CODEX_CLI_PATH`を復元します。macOSの専用LaunchAgentも解除します。
67
68
  移行前から動いているCodexがあれば`restart_required`(終了コード3)を返します。完全終了・再起動後に
68
69
  `aiterm-setup --codex-steer status`で`ready`を確認してください。旧設定は移行を実行するまで維持します。
@@ -215,7 +216,7 @@ collection is off by default and performs no network I/O. It ships via
215
216
  tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
216
217
  Release re-registers the Official MCP Registry entry.
217
218
 
218
- **Status:** actively maintained · current public release **v0.37.7** · 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.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
220
 
220
221
  ### Update and rollback
221
222
 
@@ -263,6 +264,8 @@ The same primitive hosts another agent's TUI. `agent_launch` starts a selected e
263
264
 
264
265
  Grok/Composerの無人起動は公式`--trust`で指定された作業フォルダを信頼登録し、確認画面を完了してから初回promptを送る。この登録はGrok CLIの信頼ストアへ保存され、フォルダ内のhook・MCP・LSPにも適用される。read-only sandboxの制限は維持する。画面に残る完了済みhookの結果は実行中と判定しない。
265
266
 
267
+ Grok/Composerで終了済みターンのweekly-limitパネルが残っている場合、次の通常`pty_send`が`Shift+X`で一度閉じ、入力受付を確認して今回の本文を送る。同じsessionと会話を保ち、receiptの`pane_input_recovery`に`grok_rate_limit_dialog_dismissed`を記録する。ターン未終了・harness不在は`GROK_RATE_LIMIT_RECOVERY_BLOCKED`、解除後の入力受付失敗は`GROK_RATE_LIMIT_RECOVERY_FAILED`となり、本文は未送信。上限の継続は`rate_limited`として返し、過去promptは再送しない。Grokの上限観測には現在の画面だけを使う。
268
+
266
269
  Grok/Composerがread-only sandboxの適用を拒否した場合、prompt送信時に`GROK_SANDBOX_STARTUP_FAILED`とCLIの原因を返す。hookパスのシンボリックリンクなど、CLIが示した原因を設定の管理元で修正し、対象sessionを`pty_close`して起動し直す。Aitermはsandboxを解除したりhookをコピーしたりしない。
267
270
 
268
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)を参照。
@@ -89,8 +89,8 @@ export async function main(argv) {
89
89
  }
90
90
  catch (e) {
91
91
  // Grok は利用上限に達すると auth 正本を自壊させることがあり(2026-08-22 実測)、
92
- // metadata 検証が「未ログイン」という嘘の原因で落ちる。pane log に上限バナーが
93
- // あるなら、誤診でなく rate_limited として typed に回答する。
92
+ // metadata検証が認証不在で止まる。現在のviewportに上限パネルがある場合だけ
93
+ // rate_limitedとして返し、古いlogで現在の認証エラーを置き換えない。
94
94
  if (e instanceof AitermError && /Grok 認証正本/.test(e.message)) {
95
95
  const limited = detectAgentRateLimit("grok", cmd.session);
96
96
  if (limited) {
package/dist/core.js CHANGED
@@ -18,7 +18,7 @@ import { readRuntimeProcesses, processSubtree, processIdentity, backgroundProces
18
18
  import { AitermError, telemetryOwnedFailure, ownTelemetryFailure } from "./errors.js";
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
- import { GROK_MODEL_DEFAULTS, realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokEventsTranscript, latestGrokCompletion, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, grokTuiBusy, grokPaneObservation, grokStartupAction, grokLaunchBlockingDialog, assertGrokSandboxNotRejected, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./harnesses/grok.js";
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
22
  import { bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, 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";
@@ -2590,20 +2590,21 @@ export function agentWaitGuide(session) {
2590
2590
  const cmd = `aiterm-wait --session ${session ?? "<session_id>"} --cursor 0`;
2591
2591
  return `完了通知は ${agentWaitLaunchForm(cmd)} で受ける(親はここで待たない・polling 不要)。receipt の outcome=done を確認してから再取得する。`;
2592
2592
  }
2593
- // harness 別の利用上限バナー。検知は「報告」専用で、完了判定や自動復旧には使わない。
2593
+ // harness別の利用上限観測。Grok/Composerは現在の質問カード、他harnessは既存logを使う。
2594
2594
  // 出典(2026-08-22): grok は live 実バナーで検証、codex/claude はインストール済み実バイナリの
2595
2595
  // 埋込文字列から抽出(codex: "You've hit your usage limit for" / claude: "Usage limit reached ·
2596
2596
  // continuing automatically when it resets"。Claude Code はリセット時に自動継続する設計なので、
2597
2597
  // この報告は「今は上限で止まっている」の観測であり恒久停止を意味しない)。
2598
2598
  const AGENT_RATE_LIMIT_PATTERNS = {
2599
- grok: [/You hit your weekly limit/i, /Weekly limit left:\s*0%/i],
2600
- composer: [/You hit your weekly limit/i, /Weekly limit left:\s*0%/i],
2601
2599
  codex: [/You'?ve hit your usage limit/i],
2602
2600
  claude: [/Usage limit reached/i],
2603
2601
  };
2604
2602
  const AGENT_RATE_LIMIT_SCAN_BYTES = 16 * 1024;
2605
2603
  // pane log の末尾から上限バナーを探す。読めない・無い・対象 harness でないは全て null(誤検知より取りこぼし側へ倒す)。
2606
2604
  export function detectAgentRateLimit(kind, aitermSession) {
2605
+ if (kind === "grok" || kind === "composer") {
2606
+ return grokRateLimitDialog(captureScreen(aitermSession, 0))?.message ?? null;
2607
+ }
2607
2608
  const patterns = AGENT_RATE_LIMIT_PATTERNS[kind];
2608
2609
  if (!patterns)
2609
2610
  return null;
@@ -3440,13 +3441,30 @@ export async function dispatchAgentTurn(name, text, o = {}) {
3440
3441
  && agentCompletionCursor(meta) === 0
3441
3442
  && readClaudeOperationMarker(meta) === null;
3442
3443
  const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
3444
+ const limitDialog = meta.kind === "grok" || meta.kind === "composer"
3445
+ ? grokRateLimitDialog(captureScreen(name, 0)) : null;
3446
+ if (limitDialog) {
3447
+ const live = observeSession(name);
3448
+ const done = latestAgentDoneEvent(meta);
3449
+ if (live.harness_alive !== true || done?.done_status !== "turn_error") {
3450
+ const reason = live.harness_alive !== true ? live.reason : "latest_turn_not_error";
3451
+ throw new AitermError(`GROK_RATE_LIMIT_RECOVERY_BLOCKED: ${reason}。終了済み上限パネルと確認できません。今回の文字列は送信していません。`, 2);
3452
+ }
3453
+ sendKey(name, limitDialog.dismissKey);
3454
+ }
3443
3455
  if (meta.kind !== "claude" || claudeColdStart) {
3444
3456
  const ready = await waitAgentTuiReady(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
3445
3457
  if (!ready.ready) {
3458
+ if (limitDialog) {
3459
+ const reason = grokPaneObservation(ready.lastScreen).reason;
3460
+ throw new AitermError(`GROK_RATE_LIMIT_RECOVERY_FAILED: ${reason}。上限パネル解除後の入力受付を確認できません。今回の文字列は送信していません。`, 2);
3461
+ }
3446
3462
  throw new AitermError(`agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。文字列は送信していません。` +
3447
3463
  "少し後で pty_read(screen:true) を確認し、TUI が起動済みなら再度 pty_send してください。", 2);
3448
3464
  }
3449
3465
  }
3466
+ if (limitDialog)
3467
+ paneInputRecovery.push("grok_rate_limit_dialog_dismissed");
3450
3468
  const startOffset = agentCompletionCursor(meta);
3451
3469
  // promptなしで起動したCursorは、最初のdispatch時点ではtranscriptとの相関markerをまだ持たない。
3452
3470
  // その1回だけlaunch contextを加え、以後はbind済みconversationへ通常textだけを送る。
@@ -210,8 +210,11 @@ export async function observeGrokDone(meta, timeout, requestedCursor, detectRate
210
210
  }
211
211
  try {
212
212
  const done = grokCompletionEvent(meta, JSON.parse(line));
213
- if (done)
214
- return observation(done.done_status === "turn_error" ? "error" : "done", done);
213
+ if (done) {
214
+ const limited = done.done_status === "turn_error"
215
+ ? detectRateLimit(meta.kind, meta.aiterm_session) : null;
216
+ return observation(limited ? "rate_limited" : done.done_status === "turn_error" ? "error" : "done", done, limited);
217
+ }
215
218
  }
216
219
  catch {
217
220
  malformedEvents++;
@@ -307,12 +310,34 @@ export function grokTuiBusy(screen) {
307
310
  || screen.includes("Responding...")
308
311
  || screen.includes("[stop]");
309
312
  }
313
+ // 現在の質問カードだけを認識する。選択肢が空でも操作欄は残る。
314
+ // footer後に描画があれば過去のカードなので、会話履歴からキー操作を起こさない。
315
+ export function grokRateLimitDialog(screen) {
316
+ const clean = screen.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
317
+ const headings = [...clean.matchAll(/^[ \t]*[┃│]?[ \t]*You hit your weekly limit\.[ \t]*[┃│]?[ \t]*$/gm)];
318
+ const heading = headings.at(-1);
319
+ if (!heading)
320
+ return null;
321
+ const afterHeading = clean.slice(heading.index + heading[0].length);
322
+ const footer = /Tab\s*:\s*next\s+answer\s*[│┃]?\s*Esc\s*:\s*scrollback\s*[│┃]?\s*Shift\+x\s*:\s*dismiss/i.exec(afterHeading);
323
+ if (!footer || afterHeading.slice(footer.index + footer[0].length).trim())
324
+ return null;
325
+ const body = afterHeading.slice(0, footer.index);
326
+ if (grokTuiBusy(body) || /(?:^|\n)[ \t]*(?:│[ \t]*)?[❯>]/u.test(body))
327
+ return null;
328
+ return { message: "You hit your weekly limit.", dismissKey: "X" };
329
+ }
330
+ function grokFramedComposer(screen) {
331
+ return /^[ \t]*│[ \t]*[❯>][^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*╰[^\n]*\b(?:Grok|Composer)\s+[\w.()-]+[^\n]*╯[ \t]*(?:\n|$)/mu.test(screen);
332
+ }
310
333
  export function grokPaneObservation(screen) {
311
334
  // 通信失敗後もWaitingが残る実画面を、稼働中として返さない。
312
335
  const tail = screen.split("\n").slice(-32).join("\n");
313
336
  if (/Connection failed|reqwest error stream|Check your network and try again/i.test(tail))
314
337
  return { state: "blocked", reason: "connection_failed" };
315
- if (/Help improve Grok/.test(tail))
338
+ if (grokRateLimitDialog(tail))
339
+ return { state: "blocked", reason: "rate_limited" };
340
+ if (/Help improve Grok/.test(tail) && !grokFramedComposer(tail.slice(tail.lastIndexOf("Help improve Grok"))))
316
341
  return { state: "blocked", reason: "privacy_choice" };
317
342
  if (grokLaunchBlockingDialog(tail))
318
343
  return { state: "blocked", reason: "startup_dialog" };
@@ -18,7 +18,8 @@ export function codexSteerSelected() { return readCodexHookConfig()?.enabled ===
18
18
  export function codexHookCommand(node, hook, directory, platform = process.platform) {
19
19
  if (platform === "win32") {
20
20
  const script = `& ${[node, hook, directory].map(quotePowerShell).join(" ")}; exit $LASTEXITCODE`;
21
- return `"${resolveWindowsPowerShell7()}" -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script, "utf16le").toString("base64")}`;
21
+ // Codexは利用中のPowerShellでhookを評価する。引用した実行パスには呼出し演算子が必要。
22
+ return `& ${quotePowerShell(resolveWindowsPowerShell7())} -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script, "utf16le").toString("base64")}`;
22
23
  }
23
24
  return [node, hook, directory].map(value => "'" + value.replace(/'/g, "'\"'\"'") + "'").join(" ");
24
25
  }
@@ -20,6 +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
+
23
27
  ## 履歴と証拠
24
28
 
25
29
  - [`archive/`](https://github.com/kitepon/aiterm-mcp/tree/main/docs/archive): 完了・棄却・中断・失効・置換により現行制御から外れたsnapshot。
package/docs/DESIGN.md CHANGED
@@ -90,6 +90,8 @@ Steerを選択したmacOS・Windowsでも配送の入口は公式キューとす
90
90
  `aiterm-setup --codex-steer enable|disable|status`は`src/setup-codex-hooks.ts`が所有する。
91
91
  `CODEX_HOME/hooks.json`の他の登録を保持し、専用の同期hookを追加する。
92
92
  再導入ではAitermの既存の登録位置を保ち、内容が同じならhook設定を変更しない。
93
+ hookコマンドはCodexが使うshellで評価される。Windowsでは引用したPowerShell 7の実行パスを
94
+ 呼出し演算子`&`で起動し、標準入出力と終了コードを保持する。回答の取得・配送処理はOS間で共通とする。
93
95
  公式`hooks/list`から得た2件のkeyとhashだけを公式`config/batchWrite`で承認し、再読して有効・承認済みであることを確認する。
94
96
  承認省略flagを恒久設定へ書かない。利用者の別hookを承認しない。
95
97
  選択と配送の所有記録は`~/.config/aiterm-mcp/codex-parent-hooks/`に置く。
@@ -198,6 +200,19 @@ Grok/Composerのmanaged起動は公式`--trust`を渡し、指定cwdの信頼
198
200
  `grokTuiBusy`は応答中の表示だけを実行中の根拠にし、完了後も残る`[hooks: 成功/失敗]`を含めない。
199
201
  これらのCLI固有判定は`src/harnesses/grok.ts`が所有し、共通処理は判定を呼び出す。
200
202
 
203
+ Grok/Composerの終了済みerrorターンにweekly-limit質問カードが残る場合、通常dispatchだけが
204
+ 現在のviewportを読み、harness生存と最新turnのエラー完了を確かめて`X`を一回送る。
205
+ 既存の入力受付待機を通した後に完了cursorを取得し、今回の本文だけを送る。
206
+ 実施した解除は既存receiptの`pane_input_recovery`に`grok_rate_limit_dialog_dismissed`として載る。
207
+ 解除条件の不成立は`GROK_RATE_LIMIT_RECOVERY_BLOCKED`、解除後の入力受付失敗は
208
+ `GROK_RATE_LIMIT_RECOVERY_FAILED`で未送信を返す。読取・観測・設定・steerはこの解除を行わない。
209
+
210
+ `grokRateLimitDialog`が見出しと操作footerの組を所有し、過去logや後続UIのあるカードは採用しない。
211
+ privacy案内は現在の枠付きcomposerとmodel footerが見える場合だけ入力受付を妨げない。
212
+ 完了観測は成功eventを優先し、今回のerror eventと現在のカードが揃えばturn情報付きの`rate_limited`を返す。
213
+ 新turnの完了前に古いlogだけで上限を返さない。購入・再認証・過去prompt再送・定期再試行は行わない。
214
+ 画面判定と模擬CLIの実PTY試験は`test/grok-rate-limit.test.mjs`に置く。
215
+
201
216
  ## Platform contract
202
217
 
203
218
  - macOS/Linux/WSL2: tmux。
package/docs/RELEASE.md CHANGED
@@ -39,6 +39,8 @@ Codex Steerを変更した場合は、公式バイナリを指定した`test/cod
39
39
  通常stdio起動、公式hookの個別承認、同一ターン配送、終了後再開、hook消失、終了との競合、利用者キューの保持を確認する。
40
40
  `AITERM_TEST_CODEX_BINARY`に公式Desktopの同梱実行ファイルを指定し、一時HOMEと模擬モデルで実行する。
41
41
  試験は実認証と稼働中Desktopの設定を使わない。指定のないCIではこの実機試験をskipする。
42
+ 実行途中の受信は読取専用MCPで発火し、取得記録のturn IDと`PostToolUse`の出力を照合する。
43
+ Windowsの隔離環境にも`PATHEXT`を引き継ぎ、試験processの標準入出力が閉じてから一時領域を削除する。
42
44
  公開packageの`aiterm-setup --json --codex-steer enable`で選択導入し、`restart_required`ならDesktopを完全再起動する。
43
45
  再起動後に`aiterm-setup --codex-steer status`が`ready`となり、通常の親が同じターンで子の回答を受け取ることを確認する。
44
46
  アプリ内ツールと通常起動も確認し、公式バイナリの隔離試験だけでDesktop統合を完了扱いしない。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.37.7",
3
+ "version": "0.37.9",
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": [