aiterm-mcp 0.40.2 → 0.41.0
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 +15 -1
- package/README.ja.md +15 -14
- package/README.md +18 -17
- package/dist/agent-resolver.js +1 -1
- package/dist/agent-shared.js +6 -8
- package/dist/core.js +31 -33
- package/dist/cursor-parent-receiver.js +1 -1
- package/dist/grok-stop-hook.js +2 -2
- package/dist/harnesses/grok.js +12 -15
- package/dist/index.js +8 -17
- package/dist/parent-delivery.js +1 -1
- package/dist/setup-integrations.js +1 -1
- package/docs/DESIGN.md +5 -5
- package/package.json +1 -1
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.0] - 2026-09-27
|
|
11
|
+
|
|
12
|
+
### 削除
|
|
13
|
+
|
|
14
|
+
- 旧互換alias `composer_agent` と、agent種別の`composer`を削除した。ComposerはCursorのmodelの一つで、現行のGrok CLIにはComposerが無いため、このaliasは常にcatalogエラーで起動できなかった。Composerは`agent_launch(harness=cursor-cli, model=composer-2.5-fast)`で起動する。`provider`/`vendor`の列挙から`composer`を外し、Claudeの親hookのmatcherからも`composer_agent`を外した。
|
|
15
|
+
|
|
16
|
+
## [0.40.3] - 2026-09-27
|
|
17
|
+
|
|
18
|
+
### 文書
|
|
19
|
+
|
|
20
|
+
- Composerの位置づけを改めた。ComposerはCursorのmodelの一つで、harnessでもGrokのmodelでもない。README・AGENTS.md・CONTRIBUTING.md・DESIGN・ツール説明から「Grok/Composer」の併記とGrok CLI presetという説明を外し、`agent_launch(harness=cursor-cli, model=composer-2.5-fast)`を案内する。旧互換alias `composer_agent`は旧Grok CLI presetのままで、現行Grok CLIでは起動できないことを説明に明記した。
|
|
21
|
+
|
|
10
22
|
## [0.40.2] - 2026-09-27
|
|
11
23
|
|
|
12
24
|
### 修正
|
|
@@ -1798,7 +1810,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
1798
1810
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
1799
1811
|
provenance.
|
|
1800
1812
|
|
|
1801
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.
|
|
1813
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.41.0...HEAD
|
|
1814
|
+
[0.41.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.3...v0.41.0
|
|
1815
|
+
[0.40.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.2...v0.40.3
|
|
1802
1816
|
[0.40.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.1...v0.40.2
|
|
1803
1817
|
[0.40.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.40.0...v0.40.1
|
|
1804
1818
|
[0.40.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.39.1...v0.40.0
|
package/README.ja.md
CHANGED
|
@@ -143,9 +143,9 @@ diagnostics、recovery、update、releaseを所有します。このREADMEと[
|
|
|
143
143
|
|
|
144
144
|
**言葉でなく実測で:** 記録済み203テストのベンチマークでは、`pty_read` はコンテキストに載るトークンを生ログの **約 7.1 分の 1** に減らす。しかも pass/fail の判定は畳んでも残る。→ [組み込みシェルツールとの使い分け](#組み込みシェルツールとの使い分け)
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
16ツール: 7つのPTYツール、正規のagent起動入口`agent_launch`、移行用の旧3alias、`agent_configure`、`agent_approval`、`claude_turn`、`claude_approval`、`diagnostics`。backendはPOSIXのtmux/Windows nativeのpsmuxなので、MCPサーバやAIクライアントが再起動してもsessionは生き残る。
|
|
147
147
|
|
|
148
|
-
**v0.28.0では実行基盤harnessとmodelを分離した。** harnessはagent loop・認証・hook・session・transcriptを所有し、modelはその上で選ぶ。Cursor Agent CLIでGPT/Claude/Grokを選んでも完了契約はCursor方式のまま。Composer
|
|
148
|
+
**v0.28.0では実行基盤harnessとmodelを分離した。** harnessはagent loop・認証・hook・session・transcriptを所有し、modelはその上で選ぶ。Cursor Agent CLIでGPT/Claude/Grokを選んでも完了契約はCursor方式のまま。ComposerはCursorのmodelの一つで、harnessでもGrokのmodelでもない。`harness:"cursor-cli", model:"composer-2.5-fast"`(または`composer-2.5`)で表す。旧起動ツールは同じ実装へ流れる互換alias。
|
|
149
149
|
|
|
150
150
|
**v0.25.2ではGrok 4.6を含む同一sessionの連続設定変更を安定化。** Grok Build 1.0.3で
|
|
151
151
|
`/model`の成功通知が再描画により消えても、変更前には無かった要求model/effortが常駐footerへ現れた
|
|
@@ -156,6 +156,7 @@ diagnostics、recovery、update、releaseを所有します。このREADMEと[
|
|
|
156
156
|
`write_scope:"read-only"`の`--sandbox read-only`強制、`agent_configure`による同一session内の
|
|
157
157
|
model/effort変更に対応した。明示したGrok/Composer modelとComposer既定modelはPTY作成前に
|
|
158
158
|
現在の`grok models` catalogへ照合し、不在時は別modelへ黙ってfallbackせず明示失敗する。
|
|
159
|
+
その後ComposerはGrok CLIから外れ、現在はCursorのmodelの一つである。
|
|
159
160
|
|
|
160
161
|
**v0.24.3ではlauncherへ渡す環境変数を現在のMCP processから明示選択できる。** `env_vars`へ
|
|
161
162
|
変数名だけを指定すると、aitermは起動時の現在値を読み、存在する値だけをそのagentへ渡す。永続multiplexer
|
|
@@ -202,7 +203,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
|
|
|
202
203
|
場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
|
|
203
204
|
Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
|
|
204
205
|
|
|
205
|
-
**状態:** 開発継続中 · 現行公開版 **v0.
|
|
206
|
+
**状態:** 開発継続中 · 現行公開版 **v0.41.0** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
206
207
|
|
|
207
208
|
### 更新と巻き戻し
|
|
208
209
|
|
|
@@ -254,15 +255,15 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
|
|
|
254
255
|
|
|
255
256
|
`agent_launch`は任意の`write_scope`も受ける。Codex/Grokのread-onlyは`--sandbox read-only`、Cursorは公式`--mode ask`で実効化する。path説明は同等CLI引数がないためdeclaration-only。
|
|
256
257
|
|
|
257
|
-
Grok
|
|
258
|
+
Grokの無人起動は公式`--trust`で指定された作業フォルダを信頼登録し、確認画面を完了してから初回promptを送る。この登録はGrok CLIの信頼ストアへ保存され、フォルダ内のhook・MCP・LSPにも適用される。read-only sandboxの制限は維持する。画面に残る完了済みhookの結果は実行中と判定しない。
|
|
258
259
|
|
|
259
|
-
Grok
|
|
260
|
+
Grokで終了済みターンの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の上限観測には現在の画面だけを使う。
|
|
260
261
|
|
|
261
262
|
Cursorの送信前hook(`beforeSubmitPrompt`と、互換読込するClaude Codeの`UserPromptSubmit`)がpromptを拒否すると、Cursorはpromptを捨て、turnも完了も起きない。Aitermはこの拒否の表示を見分け、起動時promptは`initial_prompt=failed`、`pty_send`は成功receiptを返さず、どちらも`USER_HOOK_BLOCKED`とhookの出力を返す。確認時間(3秒)より後の拒否は、完了待ちが`outcome=error`(`aiterm-wait`はexit 7)で返す。
|
|
262
263
|
|
|
263
|
-
Grok
|
|
264
|
+
Grokがread-only sandboxの適用を拒否した場合、prompt送信時に`GROK_SANDBOX_STARTUP_FAILED`とCLIの原因を返す。例えばhookのパスにシンボリックリンクがあるとGrok CLIは起動を拒否する。設定の管理元で原因を修正し、対象sessionを`pty_close`して起動し直す。Aitermはsandboxを解除したりhookをコピーしたりしない。
|
|
264
265
|
|
|
265
|
-
この判定はGrok
|
|
266
|
+
この判定はGrok専用アダプターが所有する。初回prompt付きの`agent_launch`と通常の`pty_send`で、入力受付待ち中に拒否を検出すると未送信のエラーを返す。promptなし・`trust_project`指定なしの起動応答は入力受付を保証しない。`trust_project:true`では入力受付まで確認し、`startup.status`を返す。Grokのprivacy notice起動設定も同アダプターが所有する。実装の責務分担は[DESIGN](docs/DESIGN.md#failure-and-recovery)を参照。
|
|
266
267
|
|
|
267
268
|
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を出さない。
|
|
268
269
|
|
|
@@ -285,7 +286,7 @@ $ aiterm-wait --session codex1 --cursor <event_cursor> # exit 0=done / 3=timeo
|
|
|
285
286
|
| --- | --- | --- |
|
|
286
287
|
| `claude-code` | Claude Code CLI | Claude model/effort |
|
|
287
288
|
| `codex-cli` | Codex CLI | OpenAI model/effort |
|
|
288
|
-
| `grok-cli` | Grok Build CLI | Grok model、live catalog
|
|
289
|
+
| `grok-cli` | Grok Build CLI | Grok model、live catalog照合 |
|
|
289
290
|
| `cursor-cli` | Cursor Agent CLI | Cursor catalog上のGPT/Claude/Grok等 |
|
|
290
291
|
|
|
291
292
|
Cursorの`model`は`gpt-5.6-luna`のようなbase model、`reasoning_effort`は`high`のように別指定する。adapterは現行`model-effort` IDを`cursor-agent models`へ照合し、起動中変更はCursor標準model pickerのparameter editorを使う。不在時は別modelへfallbackしない。
|
|
@@ -395,7 +396,7 @@ claude mcp add --scope user --transport stdio aiterm -- aiterm-mcp
|
|
|
395
396
|
|
|
396
397
|
MCP クライアントが aiterm を stdio 越しにプログラムから駆動するので、上のすべては **端末に誰も座らないまま**動く。任意のMCP対応統括役が、自分と同じharnessを含む`agent_launch`を呼び、`pty_read`で結果を読んで次へ進める——無人で。これは、人が操作する端末が向かない場所にこそ aiterm が合うということ:
|
|
397
398
|
|
|
398
|
-
- **複数エージェントのオーケストレーション** — 統括役がサブタスクを Claude Code / Codex / Grok / Cursor harnessへ渡し、各々を専用の永続セッションに置き、全部を読み戻す。ComposerはCursor
|
|
399
|
+
- **複数エージェントのオーケストレーション** — 統括役がサブタスクを Claude Code / Codex / Grok / Cursor harnessへ渡し、各々を専用の永続セッションに置き、全部を読み戻す。ComposerはCursorが選ぶmodelの一つ(`composer-2.5-fast`)。
|
|
399
400
|
- **CI** — ジョブのステップがエージェントを起こし、操作し、片付けられる。
|
|
400
401
|
- **cron** — スケジュール実行がエージェントを起動して出力を回収できる。
|
|
401
402
|
|
|
@@ -405,7 +406,7 @@ MCP クライアントが aiterm を stdio 越しにプログラムから駆動
|
|
|
405
406
|
|
|
406
407
|
```mermaid
|
|
407
408
|
flowchart LR
|
|
408
|
-
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>旧launcher alias · diagnostics"| S["aiterm-mcp<br/>stdio MCP ·
|
|
409
|
+
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>旧launcher alias · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 16 tools"]
|
|
409
410
|
S -->|"pty_read<br/>token-reduced"| AI
|
|
410
411
|
S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>再起動を跨ぐ"]
|
|
411
412
|
P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
|
|
@@ -522,8 +523,8 @@ Claudeの相関済み承認は既存の`claude_approval`を使う。
|
|
|
522
523
|
| `pty_observe` | pane/harnessの生存、native process identity、状態と活動 | `session_id`, `cursor?` |
|
|
523
524
|
| `agent_launch` | harnessとmodelを別軸で選ぶ正規agent起動入口 | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
|
|
524
525
|
| `agent_approval` | Codexの現在の承認を検査し、単発許可・拒否を送る | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
|
|
525
|
-
| `claude_agent` / `codex_agent` / `grok_agent`
|
|
526
|
-
| `agent_configure` | 起動中のClaude/Codex/Grok/
|
|
526
|
+
| `claude_agent` / `codex_agent` / `grok_agent` | deprecated互換alias(`composer_agent`は0.41.0で削除。Composerは`agent_launch`の`cursor-cli`で使う) | 旧launcher引数 |
|
|
527
|
+
| `agent_configure` | 起動中のClaude/Codex/Grok/Cursorを再起動せずmodel/effort変更 | `session_id`, `model?`, `reasoning_effort?` |
|
|
527
528
|
| `claude_turn` | 相関済みClaude operationをdispatch(issue)または回収(recover) | `action`, `session_id`, `operation_id`, `text?` |
|
|
528
529
|
| `claude_approval` | 現在表示中の相関済みClaude承認UIを検査または応答 | `action`, `session_id`, `operation_id?`, `approval_choice?`, `observed_prompt_digest?` |
|
|
529
530
|
| `diagnostics` | 機械可読 JSON による read-only factory readiness | (なし) |
|
|
@@ -540,13 +541,13 @@ consumer は `aiterm-runtime-errors snapshot` を読み、durable ingestion 後
|
|
|
540
541
|
|
|
541
542
|
`agent_launch`は選んだharnessの対話TUIを新しい永続PTYに起動し、`session_id`を返す。harnessはagent loop・認証・hook・session・transcriptを所有し、modelは独立。以後は他sessionと同じ`pty_read`/`pty_send`で操作する。
|
|
542
543
|
|
|
543
|
-
`agent_configure({ session_id, model?, reasoning_effort? })`はharness標準操作で起動中のClaude/Codex/Grok/
|
|
544
|
+
`agent_configure({ session_id, model?, reasoning_effort? })`はharness標準操作で起動中のClaude/Codex/Grok/Cursorを変更し、PTYと会話contextを維持する。
|
|
544
545
|
|
|
545
546
|
| `harness` | 起動するもの | modelの扱い |
|
|
546
547
|
| --- | --- | --- |
|
|
547
548
|
| `claude-code` | Claude Code CLI | Claude model/effort |
|
|
548
549
|
| `codex-cli` | Codex CLI | OpenAI model/effort |
|
|
549
|
-
| `grok-cli` | Grok Build CLI | Grok model、live catalog
|
|
550
|
+
| `grok-cli` | Grok Build CLI | Grok model、live catalog照合 |
|
|
550
551
|
| `cursor-cli` | Cursor Agent CLI | Cursor catalog上のGPT/Claude/Grok等 |
|
|
551
552
|
|
|
552
553
|
対応するCLI(`claude`/`codex`/`grok`/`cursor-agent`)の公式導入・認証が必要。前提違反はsession作成前に明示失敗する。全harnessが通常project/user環境と同じ非ブロックdispatch契約を使う。
|
package/README.md
CHANGED
|
@@ -145,9 +145,9 @@ Aiterm and is not a runtime dependency.
|
|
|
145
145
|
|
|
146
146
|
**Measured, not claimed:** in the recorded 203-test benchmark, a `pty_read` puts **~7.1× fewer tokens** in your context than the raw log — and the pass/fail verdict survives the fold. → [When to reach for it vs. the built-in shell](#when-to-reach-for-it-vs-the-built-in-shell)
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
Sixteen tools: seven **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` / `pty_observe` — to open, drive, read, and observe one persistent terminal; one canonical **agent launcher**, `agent_launch`, which selects `claude-code`, `codex-cli`, `grok-cli`, or `cursor-cli` as the execution harness; three deprecated launcher aliases kept for migration; `agent_configure`; `agent_approval`; `claude_turn`; `claude_approval`; and `diagnostics`. The backend is **tmux on POSIX and psmux on native Windows**, so sessions survive even if the MCP server or the AI client restarts.
|
|
149
149
|
|
|
150
|
-
**v0.28.0 separates the execution harness from the model.** The harness owns the agent loop, authentication, hooks, session, and transcript; `model` is what that harness runs. Cursor Agent CLI can therefore select GPT, Claude, or Grok without changing the completion contract from Cursor hooks to another harness's. Composer is
|
|
150
|
+
**v0.28.0 separates the execution harness from the model.** The harness owns the agent loop, authentication, hooks, session, and transcript; `model` is what that harness runs. Cursor Agent CLI can therefore select GPT, Claude, or Grok without changing the completion contract from Cursor hooks to another harness's. Composer is one of Cursor's models, not a harness and not a Grok model: use `harness: "cursor-cli", model: "composer-2.5-fast"` (or `composer-2.5`). The old launcher tools are thin compatibility aliases over the same implementation.
|
|
151
151
|
|
|
152
152
|
**v0.25.2 stabilizes repeated in-place configuration changes, including Grok 4.6.** If Grok Build
|
|
153
153
|
1.0.3 redraws before its `/model` success notice can be observed, aiterm confirms the requested model/effort
|
|
@@ -159,6 +159,7 @@ round a failure into success; explicit `grok-4.6` launch and configuration still
|
|
|
159
159
|
in-place model/effort changes through `agent_configure`. Before creating a PTY, aiterm checks an
|
|
160
160
|
explicit Grok/Composer model—and Composer's default model—against the live `grok models` catalog.
|
|
161
161
|
An unavailable model fails visibly instead of letting the harness CLI fall back to another model.
|
|
162
|
+
Composer has since left the Grok CLI and is now one of Cursor's models.
|
|
162
163
|
|
|
163
164
|
**v0.24.3 forwards explicitly selected launcher environment variables from the current MCP process.**
|
|
164
165
|
Pass variable names in `env_vars`; aiterm reads their current values at launch and injects only the
|
|
@@ -216,7 +217,7 @@ collection is off by default and performs no network I/O. It ships via
|
|
|
216
217
|
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
|
|
217
218
|
Release re-registers the Official MCP Registry entry.
|
|
218
219
|
|
|
219
|
-
**Status:** actively maintained · current public release **v0.
|
|
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
221
|
|
|
221
222
|
### Update and rollback
|
|
222
223
|
|
|
@@ -275,15 +276,15 @@ The same primitive hosts another agent's TUI. `agent_launch` starts a selected e
|
|
|
275
276
|
|
|
276
277
|
`agent_launch` accepts an optional `write_scope`: either `"read-only"` or a human-readable description of writable paths. Codex/Grok use `--sandbox read-only`; Cursor uses its official read-only `--mode ask`. A path description remains declaration-only because these CLI launch surfaces provide no equivalent path allowlist flag.
|
|
277
278
|
|
|
278
|
-
Grok
|
|
279
|
+
Grokの無人起動は公式`--trust`で指定された作業フォルダを信頼登録し、確認画面を完了してから初回promptを送る。この登録はGrok CLIの信頼ストアへ保存され、フォルダ内のhook・MCP・LSPにも適用される。read-only sandboxの制限は維持する。画面に残る完了済みhookの結果は実行中と判定しない。
|
|
279
280
|
|
|
280
|
-
Grok
|
|
281
|
+
Grokで終了済みターンの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の上限観測には現在の画面だけを使う。
|
|
281
282
|
|
|
282
283
|
When a Cursor pre-submit hook (`beforeSubmitPrompt`, or a Claude Code `UserPromptSubmit` hook that Cursor loads for compatibility) rejects the prompt, Cursor drops it and no turn or completion follows. Aiterm recognizes the rejection: an initial prompt returns `initial_prompt=failed`, and `pty_send` returns an error instead of a success receipt, both with `USER_HOOK_BLOCKED` and the hook's output. A rejection that comes after the 3-second start check is reported by the completion wait as `outcome=error` (`aiterm-wait` exit 7).
|
|
283
284
|
|
|
284
|
-
Grok
|
|
285
|
+
Grokがread-only sandboxの適用を拒否した場合、prompt送信時に`GROK_SANDBOX_STARTUP_FAILED`とCLIの原因を返す。hookパスのシンボリックリンクなど、CLIが示した原因を設定の管理元で修正し、対象sessionを`pty_close`して起動し直す。Aitermはsandboxを解除したりhookをコピーしたりしない。
|
|
285
286
|
|
|
286
|
-
この判定はGrok
|
|
287
|
+
この判定はGrok専用アダプターが所有する。初回prompt付きの`agent_launch`と通常の`pty_send`で、入力受付待ち中に拒否を検出すると未送信のエラーを返す。promptなし・`trust_project`指定なしの起動応答は入力受付を保証しない。`trust_project:true`では入力受付まで確認し、`startup.status`を返す。Grokのprivacy notice起動設定も同アダプターが所有する。実装の責務分担は[DESIGN](docs/DESIGN.md#failure-and-recovery)を参照。
|
|
287
288
|
|
|
288
289
|
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を出さない。
|
|
289
290
|
|
|
@@ -309,7 +310,7 @@ The canonical harness choices are:
|
|
|
309
310
|
| --- | --- | --- |
|
|
310
311
|
| `claude-code` | Claude Code CLI | Claude model and effort controls; correlated Stop hook |
|
|
311
312
|
| `codex-cli` | Codex CLI | OpenAI model and effort controls; durable rollout completion |
|
|
312
|
-
| `grok-cli` | Grok Build CLI | Grok model selected with `model`; live catalog check
|
|
313
|
+
| `grok-cli` | Grok Build CLI | Grok model selected with `model`; live catalog check |
|
|
313
314
|
| `cursor-cli` | Cursor Agent CLI | GPT, Claude, Grok, or another Cursor catalog model; normal transcript completion |
|
|
314
315
|
|
|
315
316
|
`env_vars` is an allowlist of environment-variable **names**, not a name/value map. At launch,
|
|
@@ -393,7 +394,7 @@ The only edits to the captures above are the two `⋮` lines (a long head/tail r
|
|
|
393
394
|
`aiterm-setup --json`が`ready`になったら、利用するMCP clientを再起動して接続を確認する。Claude Codeの場合:
|
|
394
395
|
|
|
395
396
|
```bash
|
|
396
|
-
/mcp # aiterm should show as connected, exposing
|
|
397
|
+
/mcp # aiterm should show as connected, exposing 16 tools
|
|
397
398
|
```
|
|
398
399
|
|
|
399
400
|
Your first session — four calls, one persistent terminal:
|
|
@@ -424,7 +425,7 @@ This registers it in `~/.claude.json`; you'll get an approval prompt the first t
|
|
|
424
425
|
|
|
425
426
|
Because an MCP client drives aiterm programmatically over stdio, everything above can run with **nobody sitting at the terminal**. Any MCP-capable orchestrator can call `agent_launch` — including a harness matching itself — then `pty_read` the result and act on it unattended. That makes aiterm a fit for exactly the places a human-driven terminal isn't:
|
|
426
427
|
|
|
427
|
-
- **Multi-agent orchestration** — an orchestrator hands sub-tasks to Claude Code / Codex / Grok / Cursor harnesses, each in its own persistent session, and reads them all back. Composer
|
|
428
|
+
- **Multi-agent orchestration** — an orchestrator hands sub-tasks to Claude Code / Codex / Grok / Cursor harnesses, each in its own persistent session, and reads them all back. Composer is one of the models Cursor selects (`composer-2.5-fast`).
|
|
428
429
|
- **CI** — a job step can spin up an agent, drive it, and tear it down.
|
|
429
430
|
- **cron** — a scheduled run can launch an agent and collect its output.
|
|
430
431
|
|
|
@@ -434,7 +435,7 @@ The terminal is real and shared, so a human *can* jump in ([A human can watch](#
|
|
|
434
435
|
|
|
435
436
|
```mermaid
|
|
436
437
|
flowchart LR
|
|
437
|
-
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP ·
|
|
438
|
+
AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_configure · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 16 tools"]
|
|
438
439
|
S -->|"pty_read<br/>token-reduced"| AI
|
|
439
440
|
S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>survive restarts"]
|
|
440
441
|
P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
|
|
@@ -555,8 +556,8 @@ continue to use `claude_approval`.
|
|
|
555
556
|
| `pty_observe` | Pane/harness liveness, native process identity, state, and activity | `session_id`, `cursor?` |
|
|
556
557
|
| `agent_launch` | Canonical agent launch; harness and model are independent | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
|
|
557
558
|
| `agent_approval` | Inspect a Codex approval and submit a one-time approval or denial | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
|
|
558
|
-
| `claude_agent` / `codex_agent` / `grok_agent`
|
|
559
|
-
| `agent_configure` | Change model/effort in a running Claude, Codex, Grok,
|
|
559
|
+
| `claude_agent` / `codex_agent` / `grok_agent` | Deprecated compatibility aliases (`composer_agent` was removed in 0.41.0; run Composer with `agent_launch` on `cursor-cli`) | legacy launcher arguments |
|
|
560
|
+
| `agent_configure` | Change model/effort in a running Claude, Codex, Grok, or Cursor session without restarting it | `session_id`, `model?`, `reasoning_effort?` |
|
|
560
561
|
| `claude_turn` | Issue (dispatch-only) or recover one correlated Claude operation | `action`, `session_id`, `operation_id`, `text?` |
|
|
561
562
|
| `claude_approval` | Inspect or answer the current correlated Claude approval prompt | `action`, `session_id`, `operation_id?`, `approval_choice?`, `observed_prompt_digest?` |
|
|
562
563
|
| `diagnostics` | Read-only factory readiness as machine-readable JSON | (none) |
|
|
@@ -575,13 +576,13 @@ Consumer flow is `aiterm-runtime-errors snapshot`, then `aiterm-runtime-errors a
|
|
|
575
576
|
|
|
576
577
|
`agent_launch` starts a selected harness's interactive coding-agent TUI inside a fresh persistent PTY and returns its `session_id`. The harness owns the agent loop, authentication, hooks, session, and transcript; `model` is independent. The TUI is a full-screen app, so read it with `pty_read({ screen: true })` for the rendered view.
|
|
577
578
|
|
|
578
|
-
`agent_configure({ session_id, model?, reasoning_effort? })` changes a running Claude, Codex, Grok,
|
|
579
|
+
`agent_configure({ session_id, model?, reasoning_effort? })` changes a running Claude, Codex, Grok, or Cursor TUI through the harness's standard controls, preserving the PTY and conversation context.
|
|
579
580
|
|
|
580
581
|
| `harness` | Launches | Model behavior |
|
|
581
582
|
| --- | --- | --- |
|
|
582
583
|
| `claude-code` | Claude Code CLI | Claude catalog model; native effort controls |
|
|
583
584
|
| `codex-cli` | Codex CLI | OpenAI catalog model; native effort controls |
|
|
584
|
-
| `grok-cli` | Grok Build CLI | Grok catalog model
|
|
585
|
+
| `grok-cli` | Grok Build CLI | Grok catalog model |
|
|
585
586
|
| `cursor-cli` | Cursor Agent CLI | Cursor catalog model, including GPT/Claude/Grok; effort uses model parameter override |
|
|
586
587
|
|
|
587
588
|
The selected harness CLI must be installed and authenticated. Use each product owner's official installer and updater; Aiterm does not distribute alternate CLI tarballs. For Cursor Agent CLI, use `curl https://cursor.com/install -fsS | bash` on macOS/Linux/WSL or `irm 'https://cursor.com/install?win32=true' | iex` on native Windows, authenticate once with `agent login`, and update with `agent update`; Aiterm invokes the unambiguous `cursor-agent` binary. Missing binaries, invalid model/effort values, unavailable Grok catalog models, and nonexistent `cwd` fail before a session exists.
|
|
@@ -593,13 +594,13 @@ unchanged. Optional `throughline_supplement_file` is passed unchanged to Through
|
|
|
593
594
|
`throughline_source_session` and Throughline 0.10.8 or later; Aiterm does not read or classify the supplement. Throughline is resolved through `THROUGHLINE_BIN` and then `PATH`; a missing or invalid
|
|
594
595
|
export fails before the PTY exists instead of silently launching clean.
|
|
595
596
|
|
|
596
|
-
When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lines), callers recover it in full with `pty_read({ agent_transcript: true })`. It returns the most recently completed turn's final assistant message in plain text with no re-prompting. The existing human-readable content keeps its diagnostic suffix; machine callers read the answer alone from `structuredContent.text` in `aiterm.pty-read-result.v1`. Claude reads the bounded owner-only result captured by the launch-correlated Stop hook and verifies its digest/byte count; it never reads Claude's private transcript. Durable machine callers should use `claude_turn`: `issue` sends once, `recover` never sends, `pending` is distinct from unsafe or malformed state, and only `completed` carries the exact verified `raw_output`. Codex uses the normal rollout transcript's `task_complete.turn_id`; Grok
|
|
597
|
+
When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lines), callers recover it in full with `pty_read({ agent_transcript: true })`. It returns the most recently completed turn's final assistant message in plain text with no re-prompting. The existing human-readable content keeps its diagnostic suffix; machine callers read the answer alone from `structuredContent.text` in `aiterm.pty-read-result.v1`. Claude reads the bounded owner-only result captured by the launch-correlated Stop hook and verifies its digest/byte count; it never reads Claude's private transcript. Durable machine callers should use `claude_turn`: `issue` sends once, `recover` never sends, `pending` is distinct from unsafe or malformed state, and only `completed` carries the exact verified `raw_output`. Codex uses the normal rollout transcript's `task_complete.turn_id`; Grok returns the last non-empty assistant message after the last real user row, excluding tool-use preambles; Cursor uses the normal agent transcript bound to the launch ID and current turn. Missing or ambiguous attribution remains an explicit error.
|
|
597
598
|
|
|
598
599
|
### Completion detection (5 layers)
|
|
599
600
|
|
|
600
601
|
For PowerShell over SSH, `mark:true` recognizes the current standard `PS ...>` prompt and emits PowerShell syntax even when Aiterm runs on macOS or Linux. A prompt left in earlier output is not used to select the syntax.
|
|
601
602
|
|
|
602
|
-
`pty_read({ wait: true })` decides "is the command done?" via five layers: process exit / a `mark:true` sentinel / an `until` match / output quiescence with shell return / timeout. `mark` emits the shell's exit status on POSIX shells and `0` (success) or `1` (failure) on PowerShell; fish/csh/tcsh are rejected before send because they do not share either status syntax. When `mark` or `until` is active, that requested evidence takes precedence and a momentarily quiet shell cannot complete the read as quiescent. Agent sessions add a sixth exact layer: Codex observes normal rollout `task_complete`; Grok
|
|
603
|
+
`pty_read({ wait: true })` decides "is the command done?" via five layers: process exit / a `mark:true` sentinel / an `until` match / output quiescence with shell return / timeout. `mark` emits the shell's exit status on POSIX shells and `0` (success) or `1` (failure) on PowerShell; fish/csh/tcsh are rejected before send because they do not share either status syntax. When `mark` or `until` is active, that requested evidence takes precedence and a momentarily quiet shell cannot complete the read as quiescent. Agent sessions add a sixth exact layer: Codex observes normal rollout `task_complete`; Grok observes normal session `turn_ended`; Claude observes its additive launch-correlated Stop event; Cursor observes `turn_ended(status:"success")` in the launch-bound normal agent transcript. `aiterm-wait --cursor` performs that harness-specific observation without the parent blocking or polling. Pre-send readiness failures are MCP errors, and late completion remains recoverable without resending.
|
|
603
604
|
|
|
604
605
|
### Completion push for parent agents (`aiterm-wait`)
|
|
605
606
|
|
package/dist/agent-resolver.js
CHANGED
|
@@ -29,7 +29,7 @@ export function isWindowsNativeExecutable(candidate) {
|
|
|
29
29
|
// Windows の bin 受入: native 実行ファイル(.exe/.cmd/.bat)に加え、pane shell
|
|
30
30
|
// (Git Bash)が shebang で実行できる script も実在すれば受け入れる。旧 WSL 側
|
|
31
31
|
// バイナリ検査への黙ったフォールバックは廃止(別 HOME・別 auth の subagent を
|
|
32
|
-
// 作るため)。native 実行ファイルの強制が要る harness(grok
|
|
32
|
+
// 作るため)。native 実行ファイルの強制が要る harness(grok の実効
|
|
33
33
|
// sandbox 等)は openAgent 側の専用ゲートが明示エラーで担う。
|
|
34
34
|
export function isUsableAgentExecutableFile(candidate) {
|
|
35
35
|
if (!isWin)
|
package/dist/agent-shared.js
CHANGED
|
@@ -177,13 +177,11 @@ export function writeAgentMetadata(meta) {
|
|
|
177
177
|
export function agentLabel(kind) {
|
|
178
178
|
return kind === "claude"
|
|
179
179
|
? "Claude Code"
|
|
180
|
-
: kind === "
|
|
181
|
-
? "Grok Build(
|
|
182
|
-
: kind === "
|
|
183
|
-
? "
|
|
184
|
-
:
|
|
185
|
-
? "Cursor Agent CLI"
|
|
186
|
-
: "Codex";
|
|
180
|
+
: kind === "grok"
|
|
181
|
+
? "Grok Build(Grok)"
|
|
182
|
+
: kind === "cursor"
|
|
183
|
+
? "Cursor Agent CLI"
|
|
184
|
+
: "Codex";
|
|
187
185
|
}
|
|
188
186
|
export function agentHarness(kind) {
|
|
189
187
|
return kind === "claude"
|
|
@@ -223,7 +221,7 @@ export function writeScopeLaunchNote(kind, writeScope) {
|
|
|
223
221
|
? ""
|
|
224
222
|
: kind === "cursor" && writeScope === "read-only"
|
|
225
223
|
? `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。Cursor Agent CLIへ --mode ask を付与し、書込みを実効禁止。`
|
|
226
|
-
: (kind === "codex" || kind === "grok"
|
|
224
|
+
: (kind === "codex" || kind === "grok") && writeScope === "read-only"
|
|
227
225
|
? `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。${agentLabel(kind)} CLIへ --sandbox read-only を付与し、書込みを実効禁止。` +
|
|
228
226
|
(kind === "codex" ? "" : "MCPツール許可は --always-approve で自動承認(sandbox内のため能力拡大なし)。")
|
|
229
227
|
: `\n能力宣言: write_scope=${JSON.stringify(writeScope)}。パス単位のsandbox allowlistに対応するCLI引数がないため宣言の記録のみ(構造的unsupported)。`;
|
package/dist/core.js
CHANGED
|
@@ -18,7 +18,7 @@ import { readRuntimeProcesses, processSubtree, parentProcess, processIdentity, b
|
|
|
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 {
|
|
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
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";
|
|
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";
|
|
@@ -121,7 +121,6 @@ const AGENT_COMMAND_PATTERNS = {
|
|
|
121
121
|
codex: /(^|[\s/])codex([\s]|$)/,
|
|
122
122
|
claude: /(^|[\s/])claude([\s]|$)/,
|
|
123
123
|
grok: /(^|[\s/])grok(-[^\s/]+)?([\s]|$)/,
|
|
124
|
-
composer: /(^|[\s/])(composer|grok(-[^\s/]+)?)([\s]|$)/,
|
|
125
124
|
cursor: /(^|[\s/])(cursor-agent|agent)([\s]|$)/,
|
|
126
125
|
};
|
|
127
126
|
/**
|
|
@@ -1134,7 +1133,7 @@ export function observeSession(name, cursor) {
|
|
|
1134
1133
|
const screen = captured.stdout;
|
|
1135
1134
|
result.token_hint = meta ? paneTokenHint(screen) : null;
|
|
1136
1135
|
if (meta && agent) {
|
|
1137
|
-
const observation = meta.kind === "grok"
|
|
1136
|
+
const observation = meta.kind === "grok" ? grokPaneObservation(screen)
|
|
1138
1137
|
: meta.kind === "codex" ? codexPaneObservation(screen)
|
|
1139
1138
|
: meta.kind === "claude" ? claudePaneObservation(screen) : cursorPaneObservation(screen);
|
|
1140
1139
|
result.state = observation.state;
|
|
@@ -1933,7 +1932,7 @@ function loadAgentMetadata(name) {
|
|
|
1933
1932
|
throw new AitermError(`agent metadata を読めません: ${e.message}`, 2);
|
|
1934
1933
|
}
|
|
1935
1934
|
const m = raw;
|
|
1936
|
-
if ((m.kind !== "claude" && m.kind !== "codex" && m.kind !== "grok" && m.kind !== "
|
|
1935
|
+
if ((m.kind !== "claude" && m.kind !== "codex" && m.kind !== "grok" && m.kind !== "cursor") ||
|
|
1937
1936
|
m.aiterm_session !== name ||
|
|
1938
1937
|
typeof m.launch_id !== "string" ||
|
|
1939
1938
|
!LAUNCH_ID_RE.test(m.launch_id)) {
|
|
@@ -2191,7 +2190,7 @@ function bindCompletedInitialPrompt(meta) {
|
|
|
2191
2190
|
setInitialPromptState(meta, "done");
|
|
2192
2191
|
return;
|
|
2193
2192
|
}
|
|
2194
|
-
if (
|
|
2193
|
+
if (meta.kind === "grok" && meta.completion_route === "grok_transcript") {
|
|
2195
2194
|
if (!latestGrokCompletion(meta, readTranscriptLines)) {
|
|
2196
2195
|
throw new AitermError(`agent session '${meta.aiterm_session}' は起動時 prompt の完了待ちです。${agentWaitGuide(meta.aiterm_session)}`, 2);
|
|
2197
2196
|
}
|
|
@@ -2231,7 +2230,7 @@ function latestAgentDoneEvent(meta, expectedOperationId = null) {
|
|
|
2231
2230
|
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
2232
2231
|
return latestCursorCompletion(meta, readTranscriptLines);
|
|
2233
2232
|
}
|
|
2234
|
-
if (
|
|
2233
|
+
if (meta.kind === "grok" && meta.completion_route === "grok_transcript") {
|
|
2235
2234
|
return latestGrokCompletion(meta, readTranscriptLines);
|
|
2236
2235
|
}
|
|
2237
2236
|
const size = safeStatSize(meta.event_file);
|
|
@@ -2323,7 +2322,7 @@ function recoverAgentHarnessSession(meta) {
|
|
|
2323
2322
|
writeAgentMetadata(meta);
|
|
2324
2323
|
}
|
|
2325
2324
|
function agentCompletionCursor(meta) {
|
|
2326
|
-
if (
|
|
2325
|
+
if (meta.kind === "grok" && meta.completion_route === "grok_transcript") {
|
|
2327
2326
|
const transcript = grokEventsTranscript(meta);
|
|
2328
2327
|
return transcript ? safeStatSize(transcript) : 0;
|
|
2329
2328
|
}
|
|
@@ -2409,7 +2408,7 @@ export async function readAgentTranscriptResult(name, o = {}) {
|
|
|
2409
2408
|
throw new AitermError(`agent session '${name}' はまだターンが完了していません。agent_done 完了後に再取得してください。${agentWaitGuide(name)}`, 2);
|
|
2410
2409
|
}
|
|
2411
2410
|
const done = latestAgentDoneEvent(meta, operationId);
|
|
2412
|
-
if (o.completion && meta.kind !== "codex" && meta.kind !== "grok"
|
|
2411
|
+
if (o.completion && meta.kind !== "codex" && meta.kind !== "grok"
|
|
2413
2412
|
&& (!done || done.turn_id !== o.completion.turn_id || done.operation_id !== o.completion.operation_id)) {
|
|
2414
2413
|
throw new AitermError("回収対象の完了情報が置換されました。別の回答は配送しません", 2);
|
|
2415
2414
|
}
|
|
@@ -2607,7 +2606,7 @@ export function agentWaitGuide(session) {
|
|
|
2607
2606
|
const cmd = `aiterm-wait --session ${session ?? "<session_id>"} --cursor 0`;
|
|
2608
2607
|
return `完了通知は ${agentWaitLaunchForm(cmd)} で受ける(親はここで待たない・polling 不要)。receipt の outcome=done を確認してから再取得する。`;
|
|
2609
2608
|
}
|
|
2610
|
-
// harness別の利用上限観測。Grok
|
|
2609
|
+
// harness別の利用上限観測。Grokは現在の質問カード、Cursorは現在の画面、他harnessは既存logを使う。
|
|
2611
2610
|
// 出典(2026-08-22): grok は live 実バナーで検証、codex/claude はインストール済み実バイナリの
|
|
2612
2611
|
// 埋込文字列から抽出(codex: "You've hit your usage limit for" / claude: "Usage limit reached ·
|
|
2613
2612
|
// continuing automatically when it resets"。Claude Code はリセット時に自動継続する設計なので、
|
|
@@ -2619,7 +2618,7 @@ const AGENT_RATE_LIMIT_PATTERNS = {
|
|
|
2619
2618
|
const AGENT_RATE_LIMIT_SCAN_BYTES = 16 * 1024;
|
|
2620
2619
|
// pane log の末尾から上限バナーを探す。読めない・無い・対象 harness でないは全て null(誤検知より取りこぼし側へ倒す)。
|
|
2621
2620
|
export function detectAgentRateLimit(kind, aitermSession) {
|
|
2622
|
-
if (kind === "grok"
|
|
2621
|
+
if (kind === "grok") {
|
|
2623
2622
|
return grokRateLimitDialog(captureScreen(aitermSession, 0))?.message ?? null;
|
|
2624
2623
|
}
|
|
2625
2624
|
if (kind === "cursor")
|
|
@@ -2671,7 +2670,7 @@ export async function observeAgentDone(name, o = {}) {
|
|
|
2671
2670
|
if (meta.kind === "cursor" && meta.completion_route === "cursor_transcript") {
|
|
2672
2671
|
return observeCursorDone(meta, timeout, o.cursor, detectAgentRateLimit, (session) => captureScreen(session, 0), o.signal);
|
|
2673
2672
|
}
|
|
2674
|
-
if (
|
|
2673
|
+
if (meta.kind === "grok" && meta.completion_route === "grok_transcript") {
|
|
2675
2674
|
return observeGrokDone(meta, timeout, o.cursor, detectAgentRateLimit, o.signal);
|
|
2676
2675
|
}
|
|
2677
2676
|
const metadataFile = agentMetadataPath(meta.aiterm_session, meta.launch_id);
|
|
@@ -2769,13 +2768,13 @@ function isAgentTuiReady(kind, screen) {
|
|
|
2769
2768
|
// Codex/Claude は実行中に「(esc to interrupt)」、Cursor は「Working」+
|
|
2770
2769
|
// 「ctrl+c to stop」を表示する(いずれも実機採取)。startup 側の処理(MCP initialize 等)が
|
|
2771
2770
|
// 走ったまま古い composer がscrollbackに残る画面は入力受付とみなさない。
|
|
2772
|
-
// Grok
|
|
2771
|
+
// Grok は実機で `Waiting for response` / `Responding…` / `[stop]` を表示する。
|
|
2773
2772
|
function isAgentTuiBusy(kind, screen) {
|
|
2774
2773
|
if (kind === "cursor")
|
|
2775
2774
|
return /ctrl\+c to stop/i.test(screen);
|
|
2776
2775
|
if (kind === "codex" || kind === "claude")
|
|
2777
2776
|
return /esc to interrupt/i.test(screen);
|
|
2778
|
-
if (kind === "grok"
|
|
2777
|
+
if (kind === "grok") {
|
|
2779
2778
|
return grokTuiBusy(screen);
|
|
2780
2779
|
}
|
|
2781
2780
|
return false;
|
|
@@ -2783,7 +2782,7 @@ function isAgentTuiBusy(kind, screen) {
|
|
|
2783
2782
|
// ready gate 用: 入力欄マーカーがあっても busy 表示中は ready と数えない。
|
|
2784
2783
|
// frontend 推定(inferAgentFrontend)は「agent TUI が前面か」を見るだけなので isAgentTuiReady のまま。
|
|
2785
2784
|
function isAgentTuiIdleReady(kind, screen) {
|
|
2786
|
-
const observation = kind === "grok"
|
|
2785
|
+
const observation = kind === "grok" ? grokPaneObservation(screen)
|
|
2787
2786
|
: kind === "codex" ? codexPaneObservation(screen)
|
|
2788
2787
|
: kind === "claude" ? claudePaneObservation(screen) : cursorPaneObservation(screen);
|
|
2789
2788
|
return observation.state === "idle";
|
|
@@ -2791,7 +2790,7 @@ function isAgentTuiIdleReady(kind, screen) {
|
|
|
2791
2790
|
// 起動側が明示応答すべき既知UI。ここで自動承認せず、ready timeoutを待たずに
|
|
2792
2791
|
// `initial_prompt=not_sent`を返してsessionを生かしたままcallerへ制御を戻す。
|
|
2793
2792
|
function isAgentTuiActionRequired(kind, screen) {
|
|
2794
|
-
if (kind === "grok"
|
|
2793
|
+
if (kind === "grok")
|
|
2795
2794
|
return grokLaunchBlockingDialog(screen) !== null;
|
|
2796
2795
|
if (kind === "codex") {
|
|
2797
2796
|
return codexPaneObservation(screen).state === "blocked";
|
|
@@ -2830,7 +2829,7 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
2830
2829
|
for (;;) {
|
|
2831
2830
|
lastScreen = sample();
|
|
2832
2831
|
samples++;
|
|
2833
|
-
if (kind === "grok"
|
|
2832
|
+
if (kind === "grok")
|
|
2834
2833
|
assertGrokSandboxNotRejected(lastScreen);
|
|
2835
2834
|
if (isAgentTuiIdleReady(kind, lastScreen)) {
|
|
2836
2835
|
readyStreak++;
|
|
@@ -2873,7 +2872,7 @@ function agentSubmitResidueOnScreen(kind, screen, tail) {
|
|
|
2873
2872
|
const lines = screen.split("\n");
|
|
2874
2873
|
// 入力欄マーカーは ready 判定と同じ記号を行頭基準で探す。submit 済みの transcript echo は
|
|
2875
2874
|
// マーカー行より上に出るため、最後のマーカー行以降だけを composer 領域として見る。
|
|
2876
|
-
// grok
|
|
2875
|
+
// grok は Windows native 描画(`>`・実測 1.0.4)も ready 判定と同様に受ける。
|
|
2877
2876
|
const markerRe = kind === "codex"
|
|
2878
2877
|
? CODEX_COMPOSER_MARKER_RE
|
|
2879
2878
|
: kind === "claude"
|
|
@@ -3118,7 +3117,7 @@ async function prepareAgentInput(name, meta, options) {
|
|
|
3118
3117
|
while (!ready.ready) {
|
|
3119
3118
|
const action = meta.kind === "codex" ? codexStartupAction(ready.lastScreen, options.trust_project === true)
|
|
3120
3119
|
: meta.kind === "claude" ? claudeStartupAction(ready.lastScreen, options.trust_project === true)
|
|
3121
|
-
: meta.kind === "grok"
|
|
3120
|
+
: meta.kind === "grok" ? grokStartupAction(ready.lastScreen, options.trust_project === true) : null;
|
|
3122
3121
|
if (!action || handled.has(action.kind))
|
|
3123
3122
|
break;
|
|
3124
3123
|
handled.add(action.kind);
|
|
@@ -3145,7 +3144,7 @@ async function prepareAgentInput(name, meta, options) {
|
|
|
3145
3144
|
ready = await waitAgentTuiReady(name, meta, options.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS);
|
|
3146
3145
|
}
|
|
3147
3146
|
if (!ready.ready) {
|
|
3148
|
-
const state = meta.kind === "grok"
|
|
3147
|
+
const state = meta.kind === "grok" ? grokPaneObservation(ready.lastScreen)
|
|
3149
3148
|
: meta.kind === "codex" ? codexPaneObservation(ready.lastScreen)
|
|
3150
3149
|
: meta.kind === "claude" ? claudePaneObservation(ready.lastScreen) : cursorPaneObservation(ready.lastScreen);
|
|
3151
3150
|
return { status: "blocked", reason: state.reason };
|
|
@@ -3217,7 +3216,7 @@ export async function sendInitialAgentPrompt(name, text, o = {}) {
|
|
|
3217
3216
|
let screen = "";
|
|
3218
3217
|
do {
|
|
3219
3218
|
screen = captureScreen(name, AGENT_TUI_READY_LINES);
|
|
3220
|
-
const state = meta.kind === "grok"
|
|
3219
|
+
const state = meta.kind === "grok" ? grokPaneObservation(screen)
|
|
3221
3220
|
: meta.kind === "codex" ? codexPaneObservation(screen)
|
|
3222
3221
|
: meta.kind === "claude" ? claudePaneObservation(screen) : cursorPaneObservation(screen);
|
|
3223
3222
|
if (state.state === "busy") {
|
|
@@ -3415,7 +3414,7 @@ export async function configureAgent(name, opts) {
|
|
|
3415
3414
|
reasoning_effort: effort,
|
|
3416
3415
|
};
|
|
3417
3416
|
}
|
|
3418
|
-
if (meta.kind === "grok"
|
|
3417
|
+
if (meta.kind === "grok") {
|
|
3419
3418
|
if (model) {
|
|
3420
3419
|
const bin = resolveAgentBin(meta.kind);
|
|
3421
3420
|
if (!bin)
|
|
@@ -3551,8 +3550,8 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
3551
3550
|
throw new AitermError("operation_id はClaude agent sessionだけで使用できます", 2);
|
|
3552
3551
|
}
|
|
3553
3552
|
bindCompletedInitialPrompt(meta);
|
|
3554
|
-
// Codex/Grok
|
|
3555
|
-
// Grok
|
|
3553
|
+
// Codex/Grokはbind済みのfollow-upでも毎回idleを確認してからtranscript境界を切る。
|
|
3554
|
+
// Grokはsession IDが起動前から既知でも、共有MCPの初期化完了前には送信しない。
|
|
3556
3555
|
// 同じcursorへ複数turnを帰属させる余地や、初期化中TUIへの早送信を作らない。
|
|
3557
3556
|
// Claudeはsession IDを起動時に採番するため「bind済み」では初回を区別できず、起動直後の
|
|
3558
3557
|
// dispatchがready gateを素通りしていた。composer描画前に貼付とEnterが届くと起動時の一塊の
|
|
@@ -3563,7 +3562,7 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
3563
3562
|
&& readClaudeOperationMarker(meta) === null;
|
|
3564
3563
|
const paneInputRecovery = o.pane_input_recovery ?? await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
3565
3564
|
let codexRateLimitModelSwitch = false;
|
|
3566
|
-
const limitDialog = meta.kind === "grok"
|
|
3565
|
+
const limitDialog = meta.kind === "grok"
|
|
3567
3566
|
? grokRateLimitDialog(captureScreen(name, 0)) : null;
|
|
3568
3567
|
if (limitDialog) {
|
|
3569
3568
|
const live = observeSession(name);
|
|
@@ -3736,7 +3735,7 @@ async function steerRunningTurn(name, meta, text, o) {
|
|
|
3736
3735
|
sendKey(name, "Enter", { preserveAgentOperation });
|
|
3737
3736
|
// GrokとCursorは実行中の送信を待ち行列へ入れる。そのままだと現在turnの完了後に別turnとして動き、
|
|
3738
3737
|
// 完了通知が差し込み前の回答で届いてしまう。待ち行列へ入ったことを確かめ、標準の「今すぐ送る」で現在turnへ移す。
|
|
3739
|
-
const queued = meta.kind === "grok"
|
|
3738
|
+
const queued = meta.kind === "grok" ? grokSteerQueued
|
|
3740
3739
|
: meta.kind === "cursor" ? cursorSteerQueued : null;
|
|
3741
3740
|
if (queued) {
|
|
3742
3741
|
const label = meta.kind === "cursor" ? "Cursor" : "Grok";
|
|
@@ -3820,7 +3819,7 @@ function inspectClaudeOperation(meta, operationId, action) {
|
|
|
3820
3819
|
const rawOutput = readClaudeResultText(meta, done, operationId, transcriptUnavailable);
|
|
3821
3820
|
return { ...base, status: "completed", raw_output: rawOutput, reason: null };
|
|
3822
3821
|
}
|
|
3823
|
-
// ── 対話型エージェント起動(Claude / Codex / Grok Build
|
|
3822
|
+
// ── 対話型エージェント起動(Claude / Codex / Grok Build / Cursor)──────
|
|
3824
3823
|
// aiterm の永続端末に、指定モデルの対話エージェント TUI を起動する。以後は pty_read で画面を
|
|
3825
3824
|
// 読み、pty_send で操作する=aiterm の対話パラダイムそのもの。モデルはツールごとに固定し、
|
|
3826
3825
|
// reasoning effort は引数で渡す。CLI 未導入環境は明示エラー(動くフリをしない)。
|
|
@@ -4063,21 +4062,20 @@ export function openAgent(kind, opts = {}) {
|
|
|
4063
4062
|
// 未検証リスク: npm グローバル導入の codex.cmd/.bat シムの対話 TUI 描画は実 Windows でしか確認
|
|
4064
4063
|
// できない(CI 非対象。docs/03_audit-sweep-2026-07.md 参照)。native .exe の TUI 描画は
|
|
4065
4064
|
// grok.exe で実測済み(2026-08-15)。
|
|
4066
|
-
// Windows の grok
|
|
4065
|
+
// Windows の grok は Windows native の grok.exe だけを起動する(オーナー裁定 2026-08-15:
|
|
4067
4066
|
// WindowsネイティブはWindowsネイティブで完結させ、WSL2へ持ち込まない)。WSL 側 grok を起動すると
|
|
4068
4067
|
// harness 実体が WSL process になり、auth・session 記録(events/chat_history)が WSL home 側へ分裂して
|
|
4069
4068
|
// transcript/completion を回収できない(実被弾: 2026-08-15 olc-plan-review-grok2)。
|
|
4070
|
-
if (isWin &&
|
|
4069
|
+
if (isWin && kind === "grok" && !isWindowsNativeExecutable(bin)) {
|
|
4071
4070
|
ownTelemetryFailure("AITERM.VENDOR_LAUNCHER_FAILED", new AitermError(`Windows の ${label} launcher は Windows native の grok.exe だけを起動できます(現在の解決先: ${bin})。` +
|
|
4072
4071
|
"Windows 版 Grok CLI を導入するか、GROK_BIN に grok.exe の絶対パスを指定してください。", 2), 2);
|
|
4073
4072
|
}
|
|
4074
4073
|
const binForCmd = agentBinForPaneShell(bin);
|
|
4075
4074
|
const cwdForCmd = cwd ? paneCwdArgument(cwd) : cwd;
|
|
4076
|
-
const grokAuthPath = agentDone &&
|
|
4077
|
-
if (kind === "grok"
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
assertGrokModelAvailable(bin, cwd ?? process.cwd(), requestedModel);
|
|
4075
|
+
const grokAuthPath = agentDone && kind === "grok" ? resolveAndValidateGrokAuth(realGrokHome()) : null;
|
|
4076
|
+
if (kind === "grok") {
|
|
4077
|
+
if (model)
|
|
4078
|
+
assertGrokModelAvailable(bin, cwd ?? process.cwd(), model);
|
|
4081
4079
|
}
|
|
4082
4080
|
if (kind === "cursor" && model) {
|
|
4083
4081
|
assertCursorModelAvailable(bin, cwd ?? process.cwd(), model, effort);
|
|
@@ -11,7 +11,7 @@ export const cursorParentSchema = z.object({
|
|
|
11
11
|
}).strict();
|
|
12
12
|
const deliveryIdSchema = z.string().uuid();
|
|
13
13
|
const dispatchTools = new Set([
|
|
14
|
-
"agent_launch", "claude_agent", "codex_agent", "grok_agent", "
|
|
14
|
+
"agent_launch", "claude_agent", "codex_agent", "grok_agent", "pty_send", "claude_turn",
|
|
15
15
|
]);
|
|
16
16
|
export class CursorDeliveryError extends AitermError {
|
|
17
17
|
delivery_code;
|
package/dist/grok-stop-hook.js
CHANGED
|
@@ -64,8 +64,8 @@ async function main() {
|
|
|
64
64
|
const kind = process.env.AITERM_AGENT_KIND;
|
|
65
65
|
const session = process.env.AITERM_SESSION_ID || process.env.AITERM_AGENT_SESSION_ID || "";
|
|
66
66
|
const launchId = process.env.AITERM_AGENT_LAUNCH_ID || "";
|
|
67
|
-
if (kind !== "grok"
|
|
68
|
-
fail(`AITERM_AGENT_KIND が grok
|
|
67
|
+
if (kind !== "grok")
|
|
68
|
+
fail(`AITERM_AGENT_KIND が grok ではありません: ${kind ?? ""}`);
|
|
69
69
|
if (!SESSION_RE.test(session))
|
|
70
70
|
fail(`session id が不正です: ${session}`);
|
|
71
71
|
if (!LAUNCH_ID_RE.test(launchId))
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Grok
|
|
2
|
-
//
|
|
1
|
+
// Grok 固有の制御。Composer は Cursor の model の一つであり、Grok CLI では扱わない
|
|
2
|
+
// (2026-09-27 grok 1.0.41 実測でcatalogに無い)。
|
|
3
3
|
// core 所有のサービス(transcript 行読取・rate limit 検知)は引数で注入し、
|
|
4
4
|
// 依存方向を core → harnesses → agent-shared の一方向に保つ。
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -16,7 +16,6 @@ const GROK_MODELS_TIMEOUT_MS = 15_000;
|
|
|
16
16
|
// 製品既定は検証済みGrok catalog世代へ固定する。callerの明示modelはlive catalogで別途照合する。
|
|
17
17
|
export const GROK_MODEL_DEFAULTS = {
|
|
18
18
|
grok: "grok-4.6",
|
|
19
|
-
composer: "grok-composer-2.5-fast",
|
|
20
19
|
};
|
|
21
20
|
export function realGrokHome() {
|
|
22
21
|
return path.resolve(process.env.GROK_HOME || path.join(process.env.HOME ?? os.homedir(), ".grok"));
|
|
@@ -66,11 +65,11 @@ export function assertGrokModelAvailable(bin, cwd, model) {
|
|
|
66
65
|
if (!models.includes(model)) {
|
|
67
66
|
throw new AitermError(`Grok model catalog に ${JSON.stringify(model)} がありません。利用可能: ${models.join(", ")}。` +
|
|
68
67
|
"別modelへfallbackせず起動を中止しました" +
|
|
69
|
-
(/composer/i.test(model) ? "。ComposerはCursor
|
|
68
|
+
(/composer/i.test(model) ? "。ComposerはCursorのmodelです: harness=cursor-cli, model=composer-2.5-fast" : ""), 2);
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
export function grokSessionDirectory(meta) {
|
|
73
|
-
if (
|
|
72
|
+
if (meta.kind !== "grok" || !meta.grok_home || !meta.vendor_session_id)
|
|
74
73
|
return null;
|
|
75
74
|
// Grok CLIは起動cwdをOSの絶対パスへ正規化して保存する。
|
|
76
75
|
const cwd = path.resolve(meta.cwd ?? process.cwd());
|
|
@@ -81,7 +80,7 @@ export function grokEventsTranscript(meta) {
|
|
|
81
80
|
return dir ? path.join(dir, "events.jsonl") : null;
|
|
82
81
|
}
|
|
83
82
|
export function grokCompletionEvent(meta, record) {
|
|
84
|
-
if (
|
|
83
|
+
if (meta.kind !== "grok" ||
|
|
85
84
|
record?.type !== "turn_ended" ||
|
|
86
85
|
(record?.outcome !== "completed" && record?.outcome !== "cancelled" && record?.outcome !== "error"))
|
|
87
86
|
return null;
|
|
@@ -239,24 +238,23 @@ export async function observeGrokDone(meta, timeout, requestedCursor, detectRate
|
|
|
239
238
|
}
|
|
240
239
|
export function buildGrokAgentCmd(kind, bin, model, effort, prompt, meta) {
|
|
241
240
|
const parts = [shq(bin)];
|
|
242
|
-
// grok / composer は同じ grok CLI をモデル違いで起動する。
|
|
243
241
|
parts.push("--no-auto-update");
|
|
244
242
|
// 無人起動の対象cwdはCLIの公式folder trust指定で登録し、確認画面にpromptを消費させない。
|
|
245
|
-
if (meta?.kind === "grok"
|
|
243
|
+
if (meta?.kind === "grok")
|
|
246
244
|
parts.push("--no-alt-screen", "--trust");
|
|
247
245
|
parts.push("--model", shq(model ?? GROK_MODEL_DEFAULTS[kind]));
|
|
248
246
|
if (effort)
|
|
249
247
|
parts.push("--reasoning-effort", shq(effort));
|
|
250
|
-
if (
|
|
248
|
+
if (meta?.kind === "grok" && meta.write_scope === "read-only") {
|
|
251
249
|
// read-only は sandbox が実効書込み禁止を作るため、MCP ツール許可ダイアログの自動承認を
|
|
252
250
|
// 付けても能力は増えない。無人 subagent が初回 MCP 使用の許可待ちで停止する実障害への対処。
|
|
253
251
|
// read-only 以外の launch には付けない=権限拡大しない。
|
|
254
252
|
parts.push("--sandbox", "read-only", "--always-approve");
|
|
255
253
|
}
|
|
256
|
-
if (
|
|
254
|
+
if (meta?.kind === "grok" && meta.hook_route === "shared_grok_home") {
|
|
257
255
|
parts.push("--session-id", shq(meta.vendor_session_id ?? ""), "--rules", shq(subagentInstruction(meta)));
|
|
258
256
|
}
|
|
259
|
-
if (
|
|
257
|
+
if (meta?.kind === "grok" && prompt)
|
|
260
258
|
parts.push("--verbatim");
|
|
261
259
|
if (prompt)
|
|
262
260
|
parts.push(shq(prompt)); // 初手プロンプト(任意)
|
|
@@ -267,7 +265,7 @@ export function grokLaunchNote(kind, model, effort, meta) {
|
|
|
267
265
|
return (`起動設定: model=${model ?? GROK_MODEL_DEFAULTS[kind]}(${model ? "引数" : "ツール既定"})。` +
|
|
268
266
|
`effort=${effort ?? "CLI/model既定"}。` + writeScopeNote);
|
|
269
267
|
}
|
|
270
|
-
// grok
|
|
268
|
+
// grok: 検証済み auth 正本をそのままの path 形で渡す。Windows では native 強制により
|
|
271
269
|
// harness は Windows process なので、Windows ドライブパスが正しい形(WSL 形への変換はしない)。
|
|
272
270
|
export function grokEnvTokens(meta) {
|
|
273
271
|
return [
|
|
@@ -303,9 +301,8 @@ export function grokTuiReady(screen) {
|
|
|
303
301
|
if (grokLaunchBlockingDialog(screen))
|
|
304
302
|
return false;
|
|
305
303
|
// Grok Build 0.2.117 は起動完了後に製品名を消し、model footerだけを残す。
|
|
306
|
-
// Composerも同じfrontendでmodel名だけが異なるため、両方をharness UIの根拠にする。
|
|
307
304
|
// Windows native grok.exe(1.0.4 実測)は入力欄markerを `❯` でなく `>` で描画するため両方を受ける。
|
|
308
|
-
const grokFrontend = screen.includes("Grok Build") || /\
|
|
305
|
+
const grokFrontend = screen.includes("Grok Build") || /\bGrok\s+[\w.()-]+/.test(screen);
|
|
309
306
|
return grokFrontend && /(^|\n|\s)[❯>]/.test(screen);
|
|
310
307
|
}
|
|
311
308
|
export function grokTuiBusy(screen) {
|
|
@@ -333,7 +330,7 @@ export function grokRateLimitDialog(screen) {
|
|
|
333
330
|
return { message: "You hit your weekly limit.", dismissKey: "X" };
|
|
334
331
|
}
|
|
335
332
|
function grokFramedComposer(screen) {
|
|
336
|
-
return /^[ \t]*│[ \t]*[❯>][^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*╰[^\n]*\
|
|
333
|
+
return /^[ \t]*│[ \t]*[❯>][^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*╰[^\n]*\bGrok\s+[\w.()-]+[^\n]*╯[ \t]*(?:\n|$)/mu.test(screen);
|
|
337
334
|
}
|
|
338
335
|
export function grokPaneObservation(screen) {
|
|
339
336
|
// 通信失敗後もWaitingが残る実画面を、稼働中として返さない。
|
package/dist/index.js
CHANGED
|
@@ -122,7 +122,7 @@ async function factoryDiagnostics() {
|
|
|
122
122
|
grok: {
|
|
123
123
|
status: grok,
|
|
124
124
|
optional: true,
|
|
125
|
-
required_for: ["grok_agent"
|
|
125
|
+
required_for: ["grok_agent"],
|
|
126
126
|
},
|
|
127
127
|
cursor: {
|
|
128
128
|
status: cursor,
|
|
@@ -292,7 +292,7 @@ registerRemoteAwareTool("pty_send", {
|
|
|
292
292
|
wait_process: waitProcessOutputSchema,
|
|
293
293
|
parent_delivery: parentDeliveryOutputSchema.optional(),
|
|
294
294
|
launch_id: z.string().nullable(),
|
|
295
|
-
vendor: z.enum(["claude", "codex", "grok", "
|
|
295
|
+
vendor: z.enum(["claude", "codex", "grok", "cursor"]).nullable(),
|
|
296
296
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]).nullable(),
|
|
297
297
|
// dispatch後のsubmit座礁観測(additive)。true=composerに送信textの残存を確認(submit未成立の疑い)/
|
|
298
298
|
// false=残存を観測せず(成立の保証ではない)/ null=通常送信・判定不能。
|
|
@@ -426,7 +426,7 @@ registerRemoteAwareTool("pty_read", {
|
|
|
426
426
|
mode: z.enum(["terminal", "agent_transcript"]),
|
|
427
427
|
session_id: z.string(),
|
|
428
428
|
text: z.string(),
|
|
429
|
-
vendor: z.enum(["claude", "codex", "grok", "
|
|
429
|
+
vendor: z.enum(["claude", "codex", "grok", "cursor"]).nullable(),
|
|
430
430
|
turn_id: z.string().nullable(),
|
|
431
431
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]).nullable(),
|
|
432
432
|
raw_chars: z.number().int().nonnegative().nullable(),
|
|
@@ -716,7 +716,7 @@ registerRemoteAwareTool("claude_approval", {
|
|
|
716
716
|
}
|
|
717
717
|
});
|
|
718
718
|
registerRemoteAwareTool("agent_configure", {
|
|
719
|
-
description: "起動済みのClaude/Codex/Grok/
|
|
719
|
+
description: "起動済みのClaude/Codex/Grok/Cursor agent sessionを再起動せず、会話contextを保ったままmodel/reasoning effortを変更する。" +
|
|
720
720
|
"各harnessのCLI標準model操作を使う。Cursorのreasoning_effort変更はmodelと同時指定する。",
|
|
721
721
|
inputSchema: {
|
|
722
722
|
session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
@@ -726,7 +726,7 @@ registerRemoteAwareTool("agent_configure", {
|
|
|
726
726
|
outputSchema: {
|
|
727
727
|
schema: z.literal("aiterm.agent-configure-result.v1"),
|
|
728
728
|
session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
729
|
-
provider: z.enum(["claude", "codex", "grok", "
|
|
729
|
+
provider: z.enum(["claude", "codex", "grok", "cursor"]),
|
|
730
730
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
|
|
731
731
|
model: z.string().nullable(),
|
|
732
732
|
reasoning_effort: z.string().nullable(),
|
|
@@ -752,10 +752,7 @@ const agentModelDesc = (kind) => kind === "claude"
|
|
|
752
752
|
"(端末側のピンがそのまま効く。実効値は起動応答に明示される)"
|
|
753
753
|
: kind === "cursor"
|
|
754
754
|
? "Cursor Agent CLIで選ぶbase model(例: gpt-5.6-luna)。GPT/Claude/Grok等を選べ、effortは別指定。省略時はCursor既定"
|
|
755
|
-
:
|
|
756
|
-
(kind === "composer"
|
|
757
|
-
? "既定/explicit modelを起動前にlive catalogへ照合し、不在ならfallbackせずエラー"
|
|
758
|
-
: "explicit modelを起動前にlive catalogへ照合し、不在ならfallbackせずエラー");
|
|
755
|
+
: "起動モデル。省略時は grok-4.6。explicit modelを起動前にlive catalogへ照合し、不在ならfallbackせずエラー";
|
|
759
756
|
const agentEffortDesc = (kind) => kind === "claude"
|
|
760
757
|
? "Claude Code reasoning effort。low/medium/high/xhigh/max。省略時はCLI既定"
|
|
761
758
|
: kind === "codex"
|
|
@@ -939,7 +936,7 @@ function registerAgentTool(toolName, kind, desc) {
|
|
|
939
936
|
server.registerTool("agent_launch", {
|
|
940
937
|
description: "エージェントを単一の標準入口から永続sessionへ起動する。harnessはagent loop・認証・hook・transcriptを所有する実行基盤、" +
|
|
941
938
|
"modelはそのharnessが選ぶ推論モデルであり別軸。Cursor harnessからGPT/Claude/Grok等を選んでも完了相関はCursor方式のまま。" +
|
|
942
|
-
"Composer
|
|
939
|
+
"ComposerはCursorのmodelの一つで、harnessでもGrokのmodelでもない。harness=cursor-cli と model=composer-2.5-fast(またはcomposer-2.5)で指定する。" +
|
|
943
940
|
"remoteを付けると、SSHで入った別端末のAitermで同じ起動を行い、完了は同じ形で親へ届く。" +
|
|
944
941
|
agentEnvironmentDesc + agentCompletionDesc,
|
|
945
942
|
inputSchema: {
|
|
@@ -965,7 +962,7 @@ server.registerTool("agent_launch", {
|
|
|
965
962
|
remote_host: z.string().optional().describe("別端末で起動した時の接続先"),
|
|
966
963
|
remote_version: z.string().nullable().optional().describe("別端末のAiterm版"),
|
|
967
964
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
|
|
968
|
-
provider: z.enum(["claude", "codex", "grok", "
|
|
965
|
+
provider: z.enum(["claude", "codex", "grok", "cursor"]).describe("旧互換field。新規連携はharnessを使う"),
|
|
969
966
|
session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
970
967
|
managed_completion: z.boolean(),
|
|
971
968
|
event_cursor: z.number().int().nullable(),
|
|
@@ -1006,12 +1003,6 @@ registerAgentTool("grok_agent", "grok", "【旧互換alias。新規連携は age
|
|
|
1006
1003
|
"turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
|
|
1007
1004
|
agentCompletionDesc +
|
|
1008
1005
|
"model/reasoning_effortを引数で指定可。read-only sandboxとagent_configureに対応。");
|
|
1009
|
-
registerAgentTool("composer_agent", "composer", "【旧互換alias。Composerは現行Grok CLI catalogに無いため、新規連携は agent_launch(harness=cursor-cli, model=composer-2.5-fast)】Grok BuildのComposerモデルを永続端末に起動する。" +
|
|
1010
|
-
agentEnvironmentDesc +
|
|
1011
|
-
"turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
|
|
1012
|
-
agentCompletionDesc +
|
|
1013
|
-
"model/reasoning_effortを引数で指定可。live catalogにComposer modelがなければGrokへfallbackせず明示エラー。" +
|
|
1014
|
-
"read-only sandboxとagent_configureに対応。");
|
|
1015
1006
|
async function main() {
|
|
1016
1007
|
// 親ホストを initialize の clientInfo.name から確定させ、receipt の完了待ちコマンドを
|
|
1017
1008
|
// そのホストの実際の起動形で名指しする(実測: claude-code は initialize → notifications/initialized
|
package/dist/parent-delivery.js
CHANGED
|
@@ -21,7 +21,7 @@ const recordSchema = z.object({
|
|
|
21
21
|
boundary: z.object({
|
|
22
22
|
session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
23
23
|
launch_id: z.string().regex(/^[0-9a-f]{32}$/),
|
|
24
|
-
vendor: z.enum(["claude", "codex", "grok", "
|
|
24
|
+
vendor: z.enum(["claude", "codex", "grok", "cursor"]),
|
|
25
25
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
|
|
26
26
|
event_cursor: z.number().int().nonnegative(), operation_id: z.string().nullable(),
|
|
27
27
|
// 別端末の子。remote用の保存場所にだけ書き、旧版のreaderへ渡さない。
|
|
@@ -58,7 +58,7 @@ export function mergeJsonMcp(file, registration) {
|
|
|
58
58
|
}
|
|
59
59
|
export function claudeParentHookEntries(registration) {
|
|
60
60
|
const command = { type: "command", command: registration.command, args: [join(dirname(registration.args[0]), "claude-parent-hook.js")] };
|
|
61
|
-
const matcher = "^mcp__aiterm__(agent_launch|claude_agent|codex_agent|grok_agent|
|
|
61
|
+
const matcher = "^mcp__aiterm__(agent_launch|claude_agent|codex_agent|grok_agent|pty_send|claude_turn)$";
|
|
62
62
|
return {
|
|
63
63
|
PreToolUse: [{ matcher, hooks: [{ ...command, timeout: 15 }] }],
|
|
64
64
|
PostToolUse: [{ matcher, hooks: [{ ...command, asyncRewake: true, timeout: 86400 }] }],
|
package/docs/DESIGN.md
CHANGED
|
@@ -61,7 +61,7 @@ Claude Code親は公式の非同期hookで本文を受け取り、待機中も
|
|
|
61
61
|
それ以外の親には`wait_process`がplatform nativeな別process起動情報を返す。
|
|
62
62
|
waiterは純readerで、親のforeground turnを塞がない。
|
|
63
63
|
回答はharness所有transcriptから同じturnへ相関して回収し、欠落・曖昧・timeout時にpromptを再送しない。
|
|
64
|
-
Grok
|
|
64
|
+
Grokの記録先はCLIと同じOS絶対パスへcwdを正規化して導出し、完了通知と回答で同じ関数を使う。
|
|
65
65
|
配送用のGrok回答は`turn_ended.ts`から同じturnの`turn_started.turn_number`を取得し、
|
|
66
66
|
`chat_history.jsonl`の`user.prompt_index`と相関する。次turnが既に始まっていても対象回答だけを回収する。
|
|
67
67
|
agent sessionへの送信口は`pty_send`だけとする。子の状態は呼び出し側に選ばせず、Aitermが送る時点の画面で振り分ける。
|
|
@@ -235,9 +235,9 @@ shell、接続先、各harnessの公式CLIが所有する。
|
|
|
235
235
|
stale send lockは並行processとのABAを避けるため自動削除せず、公開APIでは対象sessionを`pty_close`して
|
|
236
236
|
同じIDで再作成する。全session一括停止は公開しない。
|
|
237
237
|
|
|
238
|
-
Grok
|
|
238
|
+
Grokのread-only sandbox起動拒否は、`src/harnesses/grok.ts`の
|
|
239
239
|
`assertGrokSandboxNotRejected`がCLIのエラー表示から検出する。`src/core.ts`の共通入力受付待機は
|
|
240
|
-
Grok
|
|
240
|
+
Grokの場合だけこの判定を呼び、`GROK_SANDBOX_STARTUP_FAILED`で原因と未送信を返す。
|
|
241
241
|
初回prompt付き起動と通常dispatchに適用され、他harnessの入力受付判定には適用しない。
|
|
242
242
|
`trust_project`指定なしのpromptなし起動応答はPTYへの起動要求を示し、入力受付の確認は後続の送信時に行う。
|
|
243
243
|
|
|
@@ -245,12 +245,12 @@ hookパスのシンボリックリンク等を拒否する判断はGrok CLIが
|
|
|
245
245
|
hookのコピー、設定の置換、sandboxの解除は行わない。原因を設定の管理元で修正した後、対象sessionを
|
|
246
246
|
閉じて起動し直す。検出の回帰試験は`test/grok-startup.test.mjs`に置く。
|
|
247
247
|
|
|
248
|
-
Grok
|
|
248
|
+
Grokのmanaged起動は公式`--trust`を渡し、指定cwdの信頼状態はGrok CLIが管理する。
|
|
249
249
|
`grokLaunchBlockingDialog`は信頼確認を入力受付から除外し、scrollbackのshell promptを取り違えない。
|
|
250
250
|
`grokTuiBusy`は応答中の表示だけを実行中の根拠にし、完了後も残る`[hooks: 成功/失敗]`を含めない。
|
|
251
251
|
これらのCLI固有判定は`src/harnesses/grok.ts`が所有し、共通処理は判定を呼び出す。
|
|
252
252
|
|
|
253
|
-
Grok
|
|
253
|
+
Grokの終了済みerrorターンにweekly-limit質問カードが残る場合、通常dispatchだけが
|
|
254
254
|
現在のviewportを読み、harness生存と最新turnのエラー完了を確かめて`X`を一回送る。
|
|
255
255
|
既存の入力受付待機を通した後に完了cursorを取得し、今回の本文だけを送る。
|
|
256
256
|
実施した解除は既存receiptの`pane_input_recovery`に`grok_rate_limit_dialog_dismissed`として載る。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
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": [
|