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 +17 -1
- package/README.ja.md +4 -1
- package/README.md +4 -1
- package/dist/aiterm-wait-cli.js +2 -2
- package/dist/core.js +22 -4
- package/dist/harnesses/grok.js +28 -3
- package/dist/setup-codex-hooks.js +2 -1
- package/docs/00_overview.md +4 -0
- package/docs/DESIGN.md +15 -0
- package/docs/RELEASE.md +2 -0
- package/package.json +1 -1
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.
|
|
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.
|
|
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.
|
|
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)を参照。
|
package/dist/aiterm-wait-cli.js
CHANGED
|
@@ -89,8 +89,8 @@ export async function main(argv) {
|
|
|
89
89
|
}
|
|
90
90
|
catch (e) {
|
|
91
91
|
// Grok は利用上限に達すると auth 正本を自壊させることがあり(2026-08-22 実測)、
|
|
92
|
-
// metadata
|
|
93
|
-
//
|
|
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だけを送る。
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
}
|
package/docs/00_overview.md
CHANGED
|
@@ -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.
|
|
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": [
|