aiterm-mcp 0.25.3 → 0.26.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/README.md CHANGED
@@ -165,7 +165,7 @@ collection is off by default and performs no network I/O. It ships via
165
165
  tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
166
166
  Release re-registers the Official MCP Registry entry.
167
167
 
168
- **Status:** actively maintained · the newcomer here, betting on a different shape (see [vs. the alternatives](#vs-the-alternatives)) · runs on Linux · WSL2 · macOS · native Windows for the core PTY tools (correlated agent completion is POSIX/WSL/macOS only for now) · MIT · see the [CHANGELOG](CHANGELOG.md).
168
+ **Status:** actively maintained · the newcomer here, betting on a different shape (see [vs. the alternatives](#vs-the-alternatives)) · runs on Linux · WSL2 · macOS · native Windows for the core PTY tools and Grok/Composer launches (Claude/Codex correlated completion is POSIX/WSL/macOS only for now) · MIT · see the [CHANGELOG](CHANGELOG.md).
169
169
 
170
170
  ## Why now
171
171
 
@@ -221,7 +221,7 @@ One call per model, so the tool name itself tells you which model you get:
221
221
  | --- | --- | --- |
222
222
  | `claude_agent` | Claude Code CLI (Anthropic) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (`low`/`medium`/`high`/`xhigh`/`max`), `env_vars?`, `cwd?`, `session_name?`, `launch_operation_id?` |
223
223
  | `codex_agent` | Codex CLI (OpenAI; terminal config/CLI default unless overridden) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (`low`/`medium`/`high`/`xhigh`/`max`/`ultra`; ultra enables proactive automatic delegation), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
224
- | `grok_agent` | Grok Build, model `grok-4.5` by default (`model?` overrides) (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
224
+ | `grok_agent` | Grok Build, model `grok-4.6` by default (`model?` overrides) (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
225
225
  | `composer_agent` | Grok Build, model `grok-composer-2.5-fast` by default (`model?` overrides); every model is live-catalog checked (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
226
226
 
227
227
  `env_vars` is an allowlist of environment-variable **names**, not a name/value map. At launch,
@@ -242,7 +242,7 @@ places its returned context before a fixed separator and the mission. This route
242
242
  `throughline >= 0.9.0`; it reads the source memory without changing that database's session
243
243
  ownership. No Throughline dependency is needed when the field is omitted.
244
244
 
245
- All four launchers forward `model` and `reasoning_effort` through public CLI flags. Explicit Grok/Composer models and Composer's default model are checked against the current `grok models` catalog before a PTY exists; missing models are errors, with no cache, retry, or fallback to another model. Pass an absolute path for `cwd` — `~` is not expanded. Durable callers can make a promptless Claude launch exactly replayable by passing an explicit `session_name` and `launch_operation_id`. Claude adds a launch-local settings file only for the correlated Stop hook and loads it together with normal `user,project,local` setting sources; it does not replace normal hooks, MCPs, plugins, permissions, or trust state. The hook event contains no answer body, and the bounded owner-only result is returned by `pty_read({ agent_transcript:true })` without reading Claude's private transcript. While a correlated Claude turn is active, raw sends and non-interrupt keys are rejected. Exact `/login` and `/logout` dispatches are rejected so shared authentication is repaired once in a normal terminal. Codex reads its normal rollout store; Grok/Composer read their normal session event/history files. Before dispatch, Codex waits for an idle TUI, while Grok/Composer additionally require the vendor's structured `mcp_init_completed` event so a visible input box cannot accept a prompt too early. Correlated completion requires POSIX filesystem semantics (Linux, WSL2, macOS).
245
+ All four launchers forward `model` and `reasoning_effort` through public CLI flags. Explicit Grok/Composer models and Composer's default model are checked against the current `grok models` catalog before a PTY exists; missing models are errors, with no cache, retry, or fallback to another model. Pass an absolute path for `cwd` — `~` is not expanded. Durable callers can make a promptless Claude launch exactly replayable by passing an explicit `session_name` and `launch_operation_id`. Claude adds a launch-local settings file only for the correlated Stop hook and loads it together with normal `user,project,local` setting sources; it does not replace normal hooks, MCPs, plugins, permissions, or trust state. The hook event contains no answer body, and the bounded owner-only result is returned by `pty_read({ agent_transcript:true })` without reading Claude's private transcript. While a correlated Claude turn is active, raw sends and non-interrupt keys are rejected. Exact `/login` and `/logout` dispatches are rejected so shared authentication is repaired once in a normal terminal. Codex reads its normal rollout store; Grok/Composer read their normal session event/history files. Before dispatch, Codex waits for an idle TUI, while Grok/Composer additionally require the vendor's structured `mcp_init_completed` event so a visible input box cannot accept a prompt too early. Claude/Codex correlated completion requires POSIX filesystem semantics (Linux, WSL2, macOS); Grok/Composer correlated completion also works on native Windows, where the launcher starts the Windows-native `grok.exe` and reads its Windows-side session records.
246
246
 
247
247
  There is no hidden protocol between agents: a launched Claude, Codex, Grok, or Composer is another user-visible persistent terminal session. The MCP client drives that TUI with ordinary PTY operations, and a human can attach to watch or take over.
248
248
 
@@ -446,7 +446,7 @@ Each launcher starts a specific vendor's interactive coding-agent TUI inside a f
446
446
  | --- | --- | --- |
447
447
  | `claude_agent` | Claude Code CLI (Anthropic) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (`low`/`medium`/`high`/`xhigh`/`max`), `env_vars?`, `cwd?`, `session_name?`, `launch_operation_id?` |
448
448
  | `codex_agent` | Codex CLI (OpenAI; terminal config/CLI default unless overridden) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (`low`/`medium`/`high`/`xhigh`/`max`/`ultra`; ultra enables proactive automatic delegation), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
449
- | `grok_agent` | Grok Build, model `grok-4.5` by default (`model?` overrides) (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
449
+ | `grok_agent` | Grok Build, model `grok-4.6` by default (`model?` overrides) (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
450
450
  | `composer_agent` | Grok Build, model `grok-composer-2.5-fast` by default (`model?` overrides); every model is live-catalog checked (xAI) | `prompt?`, `throughline_source_session?`, `model?`, `reasoning_effort?` (vendor-supported value), `env_vars?`, `cwd?`, `session_name?`, `write_scope?` |
451
451
 
452
452
  The vendor CLI must be installed and authenticated (`claude` for `claude_agent`; `codex` for `codex_agent`; `grok` for both Grok tools). Binary resolution uses `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`, then each documented default location, then `PATH`. Missing binaries, invalid model/effort values, unavailable Grok/Composer catalog models, and nonexistent `cwd` fail before a session is created. Claude additionally requires a structured healthy authentication status before any PTY exists, and correlated Claude sessions reject `/login` and `/logout`; repair authentication once in a normal terminal. All four launchers share the normal project/user environment and the same non-blocking dispatch contract. Claude, Codex, Grok, and Composer depth-1 live smokes and a Claude depth-2 nested-delegation smoke are green; fixture coverage remains a separate claim. Native Windows can launch agents but correlated completion is not supported yet.
@@ -497,7 +497,7 @@ Sessions live on a shared tmux socket. The `tmux -S … attach -t <id>` line pri
497
497
  - **Node.js >= 18**
498
498
  - **tmux** (runtime prerequisite; check with `tmux -V`. Install with `apt install tmux` / `brew install tmux`)
499
499
  - **macOS / Linux / WSL2** run tmux directly. On macOS install it with `brew install tmux` (stock macOS ships none). If your MCP client is launched from the **GUI** rather than a terminal, Homebrew's bin (`/opt/homebrew/bin` on Apple Silicon, `/usr/local/bin` on Intel) may be off its `PATH`; aiterm auto-searches those locations, or set **`AITERM_TMUX=/path/to/tmux`** to point at it explicitly.
500
- - **Native Windows** has no tmux, so aiterm transparently runs tmux **inside WSL**. It needs [WSL](https://learn.microsoft.com/windows/wsl/) installed and initialized, with **tmux installed inside your WSL distro** (`sudo apt install tmux`); verify with `wsl tmux -V`. Sessions, the socket, and human `attach` all live on the WSL side — the AI just drives them from the Windows-side command. (You reach Windows tools the same way you reach SSH: `pty_send "powershell.exe …"` nests into PowerShell.)
500
+ - **Native Windows** has no tmux, so aiterm transparently runs tmux **inside WSL**. It needs [WSL](https://learn.microsoft.com/windows/wsl/) installed and initialized, with **tmux installed inside your WSL distro** (`sudo apt install tmux`); verify with `wsl tmux -V`. Sessions, the socket, and human `attach` all live on the WSL side — the AI just drives them from the Windows-side command. (You reach Windows tools the same way you reach SSH: `pty_send "powershell.exe …"` nests into PowerShell.) WSL stays a pure terminal transport: `grok_agent`/`composer_agent` launch the **Windows-native** Grok CLI (`%USERPROFILE%\.grok\bin\grok.exe`, or `GROK_BIN` pointing at a `.exe`) as a Windows process, and a WSL-side grok is rejected before a session is created so vendor auth and session records never split across the WSL boundary. aiterm keeps one long-lived WSL interop anchor process alive so Windows executables can start inside the WSL tmux pane.
501
501
  - For the **agent launchers**: the corresponding vendor CLI, installed and authenticated — `claude` for `claude_agent`, `codex` for `codex_agent`, `grok` for `grok_agent` / `composer_agent`. Portable fork additionally needs `throughline >= 0.9.0`; ordinary clean launch does not. (Not needed if you only use the PTY tools.)
502
502
  - Optional: the [`rtk`](https://github.com/rtk-ai/rtk) binary (used by `pty_send`'s `rtk: true` delegation; works fine without it)
503
503
 
package/dist/core.js CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 設計: docs/01_design-plan.md / docs/02_mcp-plan.md。出力削減は rag/ の RTK を移植。
9
9
  */
10
- import { spawnSync } from "node:child_process";
10
+ import { spawn, spawnSync } from "node:child_process";
11
11
  import * as fs from "node:fs";
12
12
  import * as path from "node:path";
13
13
  import * as os from "node:os";
@@ -185,6 +185,82 @@ function ensureWinBridge(observe = true) {
185
185
  ptyDependencyError("WSL 経由で tmux を起動できませんでした。WSL のディストリ未導入、または distro 内に tmux が無い可能性があります。`wsl tmux -V` が通るか確認してください(tmux 導入例: sudo apt install tmux)。", observe);
186
186
  winBridgeOk = true;
187
187
  }
188
+ // Windows の tmux bridge では各 wsl.exe 呼び出しが短命で、tmux server と pane が継承する
189
+ // WSL_INTEROP は起動元 WSL session の終了とともに死ぬ。死んだ socket のまま pane 内から
190
+ // Windows .exe を起動すると binfmt interop が UtilAcceptVsock accept4=110(ETIMEDOUT) で失敗する。
191
+ // 生きた session leader の socket を WSL_INTEROP へ指せば同じ pane で成功する(どちらも実測
192
+ // 2026-08-15・WSL 2.6.1)。そのため aiterm が長寿命 anchor(sleep する wsl.exe process)を
193
+ // 1本所有し、native .exe 起動の env へその socket を供給する。anchor は起動時に自身の
194
+ // WSL_INTEROP と pid を state file へ書き、以後は生存確認だけで再利用する。
195
+ const WIN_INTEROP_ANCHOR_SPAWN_TIMEOUT_MS = 10_000;
196
+ const WIN_INTEROP_ANCHOR_POLL_MS = 100;
197
+ function winInteropAnchorStatePath() {
198
+ return path.join(ensureSecureStateRoot(), "interop-anchor.json");
199
+ }
200
+ function readWinInteropAnchorState() {
201
+ let raw;
202
+ try {
203
+ raw = fs.readFileSync(winInteropAnchorStatePath(), "utf8");
204
+ }
205
+ catch {
206
+ return null;
207
+ }
208
+ try {
209
+ const value = JSON.parse(raw);
210
+ if (typeof value.socket === "string" &&
211
+ value.socket.startsWith("/run/WSL/") &&
212
+ Number.isSafeInteger(value.pid) &&
213
+ value.pid > 0) {
214
+ return { socket: value.socket, pid: value.pid };
215
+ }
216
+ }
217
+ catch {
218
+ /* 不完全・破損 state は下の再生成経路で作り直す */
219
+ }
220
+ return null;
221
+ }
222
+ function isWinInteropAnchorAlive(state) {
223
+ const r = spawnSync("wsl.exe", ["-e", "sh", "-c", `test -S ${shq(state.socket)} && kill -0 ${state.pid}`], {
224
+ encoding: "utf8",
225
+ timeout: 10000,
226
+ windowsHide: true,
227
+ });
228
+ return !r.error && r.status === 0;
229
+ }
230
+ // openAgent は同期経路のため、anchor の起動待ちだけ Atomics.wait で有界に同期 sleep する。
231
+ function sleepSyncMs(ms) {
232
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
233
+ }
234
+ function ensureWinInteropAnchor() {
235
+ const existing = readWinInteropAnchorState();
236
+ if (existing && isWinInteropAnchorAlive(existing))
237
+ return existing.socket;
238
+ const statePath = winInteropAnchorStatePath();
239
+ fs.rmSync(statePath, { force: true });
240
+ // 並行launchが同時にここへ来ると anchor が余分に1本立つが、state は後着が上書きし
241
+ // どちらの socket も有効なので実害はない(余剰 process は wsl shutdown まで sleep のみ)。
242
+ const child = spawn("wsl.exe", [
243
+ "-e",
244
+ "sh",
245
+ "-c",
246
+ `printf '{"socket":"%s","pid":%s}' "$WSL_INTEROP" "$$" > ${shq(toWslPath(statePath))} && exec sleep infinity`,
247
+ ], { detached: true, stdio: "ignore", windowsHide: true });
248
+ child.unref();
249
+ const deadline = Date.now() + WIN_INTEROP_ANCHOR_SPAWN_TIMEOUT_MS;
250
+ for (;;) {
251
+ const state = readWinInteropAnchorState();
252
+ if (state) {
253
+ if (!isWinInteropAnchorAlive(state)) {
254
+ throw new AitermError("WSL interop anchor が起動直後に停止しました。`wsl` が動作するか確認してください。", 2);
255
+ }
256
+ return state.socket;
257
+ }
258
+ if (Date.now() >= deadline) {
259
+ throw new AitermError("WSL interop anchor を起動できません。`wsl` が動作するか確認してください。", 2);
260
+ }
261
+ sleepSyncMs(WIN_INTEROP_ANCHOR_POLL_MS);
262
+ }
263
+ }
188
264
  // tmux が見つからないときの説明。macOS は tmux を同梱せず、Homebrew の bin は GUI 起動時の PATH に
189
265
  // 入らないため、原因と対処(導入・自動探索・AITERM_TMUX 上書き)を正直に提示する。
190
266
  function tmuxMissingMessage() {
@@ -450,20 +526,22 @@ function agentClaudeDispatchReceiptPath(name, launchId, operationId) {
450
526
  return path.join(agentsDir(), `${name}.${launchId}.${validated.slice("sha256:".length)}.claude-dispatch`);
451
527
  }
452
528
  function existingAgentsDir() {
453
- if (typeof process.getuid !== "function")
454
- return null;
455
- const root = path.join(runtimeStateBase(), `aiterm-mcp-${process.getuid()}`);
529
+ // Windows は getuid を持たないが、currentUid() が 0 を返し fs.Stats.uid も常に 0 のため
530
+ // owner 比較は自然に通過する(currentUid の既知制約受容と同じ)。以前はここで null を返して
531
+ // いたため、Windows では close/killAll の agent state 掃除が常に no-op になり、同名 session の
532
+ // 再起動が「agent metadata が複数あります」で失敗していた(実被弾 2026-08-15)。
533
+ const root = path.join(runtimeStateBase(), `aiterm-mcp-${currentUid()}`);
456
534
  const dir = path.join(root, "agents");
457
535
  try {
458
536
  const rst = fs.lstatSync(root);
459
- if (!rst.isDirectory() || rst.isSymbolicLink() || rst.uid !== process.getuid())
537
+ if (!rst.isDirectory() || rst.isSymbolicLink() || rst.uid !== currentUid())
460
538
  return null;
461
- if ((rst.mode & 0o077) !== 0)
539
+ if (!isWin && (rst.mode & 0o077) !== 0)
462
540
  fs.chmodSync(root, 0o700);
463
541
  const st = fs.lstatSync(dir);
464
- if (!st.isDirectory() || st.isSymbolicLink() || st.uid !== process.getuid())
542
+ if (!st.isDirectory() || st.isSymbolicLink() || st.uid !== currentUid())
465
543
  return null;
466
- if ((st.mode & 0o077) !== 0)
544
+ if (!isWin && (st.mode & 0o077) !== 0)
467
545
  fs.chmodSync(dir, 0o700);
468
546
  return dir;
469
547
  }
@@ -3381,8 +3459,9 @@ function isAgentTuiReady(kind, screen) {
3381
3459
  }
3382
3460
  // Grok Build 0.2.117 は起動完了後に製品名を消し、model footerだけを残す。
3383
3461
  // Composerも同じfrontendでmodel名だけが異なるため、両方をvendor UIの根拠にする。
3462
+ // Windows native grok.exe(1.0.4 実測)は入力欄markerを `❯` でなく `>` で描画するため両方を受ける。
3384
3463
  const grokFrontend = screen.includes("Grok Build") || /\b(?:Grok|Composer)\s+[\w.()-]+/.test(screen);
3385
- return grokFrontend && /(^|\n|\s)❯/.test(screen);
3464
+ return grokFrontend && /(^|\n|\s)[❯>]/.test(screen);
3386
3465
  }
3387
3466
  // Codex/Claude は実行中に「(esc to interrupt)」を表示する(実機採取)。startup 側の処理
3388
3467
  // (MCP initialize 等)が走ったまま composer だけ描画されている画面は入力受付とみなさない。
@@ -3446,7 +3525,8 @@ function agentSubmitResidueOnScreen(kind, screen, tail) {
3446
3525
  const lines = screen.split("\n");
3447
3526
  // 入力欄マーカーは ready 判定と同じ記号を行頭基準で探す。submit 済みの transcript echo は
3448
3527
  // マーカー行より上に出るため、最後のマーカー行以降だけを composer 領域として見る。
3449
- const markerRe = kind === "codex" ? /^\s*[›>]/ : kind === "claude" ? /^\s*❯/ : /(^|\s)❯/;
3528
+ // grok/composer は Windows native 描画(`>`・実測 1.0.4)も ready 判定と同様に受ける。
3529
+ const markerRe = kind === "codex" ? /^\s*[›>]/ : kind === "claude" ? /^\s*❯/ : /(^|\s)[❯>]/;
3450
3530
  let markerIdx = -1;
3451
3531
  for (let i = lines.length - 1; i >= 0; i--) {
3452
3532
  if (markerRe.test(lines[i])) {
@@ -3942,7 +4022,7 @@ function resolveAgentBin(kind) {
3942
4022
  ? ["CLAUDE_BIN", [".local", "bin", "claude"], "claude"]
3943
4023
  : kind === "codex"
3944
4024
  ? ["CODEX_BIN", [".local", "bin", "codex"], "codex"]
3945
- : ["GROK_BIN", [".grok", "bin", "grok"], "grok"];
4025
+ : ["GROK_BIN", [".grok", "bin", isWin ? "grok.exe" : "grok"], "grok"];
3946
4026
  const fromEnv = process.env[envVar];
3947
4027
  if (fromEnv) {
3948
4028
  // 明示指定 env は実在を検証する。存在しないパスを黙って返すと、session を作って
@@ -4163,8 +4243,9 @@ function shq(s) {
4163
4243
  }
4164
4244
  // grok CLI はモデル未指定だと端末側 default に従うため、ツール契約として既定 slug を固定する。
4165
4245
  // codex は既定 slug を持たず端末 config/CLI 既定に委ねる(起動応答で実効値を報告する)。
4246
+ // grok 既定は dotagents 規範(docs/02_models.md: xAI 旗艦 = grok-4.6)に従う。
4166
4247
  const GROK_MODEL_DEFAULTS = {
4167
- grok: "grok-4.5",
4248
+ grok: "grok-4.6",
4168
4249
  composer: "grok-composer-2.5-fast",
4169
4250
  };
4170
4251
  const CLAUDE_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
@@ -4202,7 +4283,10 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
4202
4283
  if (effort)
4203
4284
  parts.push("--reasoning-effort", shq(effort));
4204
4285
  if ((meta?.kind === "grok" || meta?.kind === "composer") && meta.write_scope === "read-only") {
4205
- parts.push("--sandbox", "read-only");
4286
+ // read-only は sandbox が実効書込み禁止を作るため、MCP ツール許可ダイアログの自動承認を
4287
+ // 付けても能力は増えない。無人 subagent が初回 MCP 使用の許可待ちで停止する実障害への対処。
4288
+ // read-only 以外の launch には付けない=権限拡大しない。
4289
+ parts.push("--sandbox", "read-only", "--always-approve");
4206
4290
  }
4207
4291
  if ((meta?.kind === "grok" || meta?.kind === "composer") && meta.hook_route === "shared_grok_home") {
4208
4292
  parts.push("--session-id", shq(meta.vendor_session_id ?? ""), "--rules", shq(subagentInstruction(meta)));
@@ -4214,41 +4298,51 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
4214
4298
  parts.push(shq(prompt)); // 初手プロンプト(任意)
4215
4299
  return parts.join(" ");
4216
4300
  }
4217
- function agentEnvPrefix(meta, sid, envVars = []) {
4301
+ // Windows native .exe を WSL pane から interop 起動する時だけ、生きた anchor socket と、
4302
+ // 注入 env を Win32 process まで運ぶ WSLENV(/w) を先頭へ加える(WSL 側の env は interop 先の
4303
+ // Windows process へ既定では渡らない・実測 2026-08-15)。POSIX と非 native bin では不変。
4304
+ export function winInteropEnvTokens(tokens, interopSocket) {
4305
+ if (!interopSocket)
4306
+ return tokens;
4307
+ const names = tokens.map((token) => token.slice(0, token.indexOf("=")));
4308
+ return [
4309
+ `WSL_INTEROP=${shq(interopSocket)}`,
4310
+ ...(names.length ? [`WSLENV=${names.map((name) => `${name}/w`).join(":")}`] : []),
4311
+ ...tokens,
4312
+ ];
4313
+ }
4314
+ function agentEnvPrefix(meta, sid, envVars = [], interopSocket = null) {
4218
4315
  const inherited = envVars.flatMap((name) => {
4219
4316
  const value = process.env[name];
4220
4317
  return value === undefined ? [] : [`${name}=${shq(value)}`];
4221
4318
  });
4222
- if (!meta)
4223
- return inherited.length ? inherited.join(" ") + " " : "";
4224
- const common = [
4225
- ...inherited,
4226
- `AITERM_AGENT_KIND=${shq(meta.kind)}`,
4227
- `AITERM_SESSION_ID=${shq(sid)}`,
4228
- `AITERM_AGENT_SESSION_ID=${shq(sid)}`,
4229
- `AITERM_AGENT_LAUNCH_ID=${shq(meta.launch_id)}`,
4230
- `AITERM_AGENT_ROLE=${shq(meta.agent_role ?? "subagent")}`,
4231
- `AITERM_AGENT_PARENT_SESSION_ID=${shq(meta.parent_session_id ?? "host-root")}`,
4232
- `AITERM_AGENT_DEPTH=${shq(String(meta.delegation_depth ?? 1))}`,
4233
- `AITERM_AGENT_LINEAGE=${shq(meta.lineage ?? `host-root>${meta.kind}:${sid}`)}`,
4234
- `AITERM_AGENT_DELEGATION_ALLOWED=${shq(meta.delegation_allowed === true ? "true" : "false")}`,
4235
- ];
4236
- if (meta.kind === "claude") {
4237
- return common.join(" ") + " ";
4238
- }
4239
- if (meta.kind === "codex") {
4240
- return common.join(" ") + " ";
4241
- }
4242
- // Windows では起動コマンドが WSL 内 bash で走るため、検証済み auth 正本の Windows ドライブパスを
4243
- // bin/cwd と同じく /mnt/c/... 形へ変換して渡す。変換しないと WSL 側 grok が正本を開けず接続で停止する。
4244
- const grokAuthForCmd = meta.grok_auth_path && isWin && isWindowsDrivePath(meta.grok_auth_path)
4245
- ? toWslPath(meta.grok_auth_path)
4246
- : meta.grok_auth_path;
4247
- return [
4248
- ...(grokAuthForCmd ? [`GROK_AUTH_PATH=${shq(grokAuthForCmd)}`] : []),
4249
- "GROK_DISABLE_AUTOUPDATER=1",
4250
- ...common,
4251
- ].join(" ") + " ";
4319
+ const tokens = (() => {
4320
+ if (!meta)
4321
+ return inherited;
4322
+ const common = [
4323
+ ...inherited,
4324
+ `AITERM_AGENT_KIND=${shq(meta.kind)}`,
4325
+ `AITERM_SESSION_ID=${shq(sid)}`,
4326
+ `AITERM_AGENT_SESSION_ID=${shq(sid)}`,
4327
+ `AITERM_AGENT_LAUNCH_ID=${shq(meta.launch_id)}`,
4328
+ `AITERM_AGENT_ROLE=${shq(meta.agent_role ?? "subagent")}`,
4329
+ `AITERM_AGENT_PARENT_SESSION_ID=${shq(meta.parent_session_id ?? "host-root")}`,
4330
+ `AITERM_AGENT_DEPTH=${shq(String(meta.delegation_depth ?? 1))}`,
4331
+ `AITERM_AGENT_LINEAGE=${shq(meta.lineage ?? `host-root>${meta.kind}:${sid}`)}`,
4332
+ `AITERM_AGENT_DELEGATION_ALLOWED=${shq(meta.delegation_allowed === true ? "true" : "false")}`,
4333
+ ];
4334
+ if (meta.kind === "claude" || meta.kind === "codex")
4335
+ return common;
4336
+ // grok/composer: 検証済み auth 正本をそのままの path 形で渡す。Windows では native 強制により
4337
+ // vendor は Windows process なので、Windows ドライブパスが正しい形(WSL 形への変換はしない)。
4338
+ return [
4339
+ ...(meta.grok_auth_path ? [`GROK_AUTH_PATH=${shq(meta.grok_auth_path)}`] : []),
4340
+ "GROK_DISABLE_AUTOUPDATER=1",
4341
+ ...common,
4342
+ ];
4343
+ })();
4344
+ const finalTokens = winInteropEnvTokens(tokens, interopSocket);
4345
+ return finalTokens.length ? finalTokens.join(" ") + " " : "";
4252
4346
  }
4253
4347
  function agentLabel(kind) {
4254
4348
  return kind === "claude"
@@ -4266,7 +4360,8 @@ function buildAgentLaunchNote(kind, model, effort, meta) {
4266
4360
  const writeScopeNote = meta?.write_scope === undefined
4267
4361
  ? ""
4268
4362
  : (kind === "codex" || kind === "grok" || kind === "composer") && meta.write_scope === "read-only"
4269
- ? `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。${agentLabel(kind)} CLIへ --sandbox read-only を付与し、書込みを実効禁止。`
4363
+ ? `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。${agentLabel(kind)} CLIへ --sandbox read-only を付与し、書込みを実効禁止。` +
4364
+ (kind === "codex" ? "" : "MCPツール許可は --always-approve で自動承認(sandbox内のため能力拡大なし)。")
4270
4365
  : `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。パス単位のsandbox allowlistに対応するCLI引数がないため宣言の記録のみ(構造的unsupported)。`;
4271
4366
  if (kind === "claude") {
4272
4367
  return `起動設定: model=${model ?? "CLI既定"} effort=${effort ?? "CLI既定"}。${writeScopeNote}`;
@@ -4416,10 +4511,22 @@ export function openAgent(kind, opts = {}) {
4416
4511
  // Windows は起動コマンドが WSL 内 bash で走る(tmux ブリッジ)。bin/cwd を /mnt/c/... 形へ変換して
4417
4512
  // 渡す(ログの toWslPath と対称・A1)。前提: Windows 側に CLI を導入(resolveAgentBin が Windows
4418
4513
  // パスで解決)。toWslPath は session を作る前に呼ぶ=変換失敗(非ドライブパス)で残骸 session を残さない。
4419
- // 未検証リスク: npm グローバル導入の codex.cmd/.bat シムや WSL interop 上の対話 TUI 描画は実 Windows
4420
- // でしか確認できない(CI 非対象。docs/03_audit-sweep-2026-07.md 参照)。
4514
+ // 未検証リスク: npm グローバル導入の codex.cmd/.bat シムの対話 TUI 描画は実 Windows でしか確認
4515
+ // できない(CI 非対象。docs/03_audit-sweep-2026-07.md 参照)。native .exe の interop 起動と
4516
+ // TUI 描画は grok.exe で実測済み(2026-08-15)。
4517
+ // Windows の grok/composer は Windows native の grok.exe だけを起動する(オーナー裁定 2026-08-15:
4518
+ // WindowsネイティブはWindowsネイティブで完結させ、WSL2へ持ち込まない)。WSL 側 grok を起動すると
4519
+ // vendor 実体が WSL process になり、auth・session 記録(events/chat_history)が WSL home 側へ分裂して
4520
+ // transcript/completion を回収できない(実被弾: 2026-08-15 olc-plan-review-grok2)。
4521
+ if (isWin && (kind === "grok" || kind === "composer") && !isWindowsNativeExecutable(bin)) {
4522
+ ownTelemetryFailure("AITERM.VENDOR_LAUNCHER_FAILED", new AitermError(`Windows の ${label} launcher は Windows native の grok.exe だけを起動できます(現在の解決先: ${bin})。` +
4523
+ "Windows 版 Grok CLI を導入するか、GROK_BIN に grok.exe の絶対パスを指定してください。", 2), 2);
4524
+ }
4421
4525
  const binForCmd = agentBinForWslShell(bin);
4422
4526
  const cwdForCmd = cwd && isWin ? toWslPath(cwd) : cwd;
4527
+ // native .exe を pane 内で interop 起動するには生きた WSL_INTEROP が必須(anchor 解説参照)。
4528
+ // session 作成前に確保し、失敗時は残骸 session を残さない。
4529
+ const interopSocket = isWin && isWindowsNativeExecutable(bin) ? ensureWinInteropAnchor() : null;
4423
4530
  const grokAuthPath = agentDone && (kind === "grok" || kind === "composer") ? resolveAndValidateGrokAuth(realGrokHome()) : null;
4424
4531
  if (kind === "grok" || kind === "composer") {
4425
4532
  const requestedModel = model ?? (kind === "composer" ? GROK_MODEL_DEFAULTS.composer : null);
@@ -4452,7 +4559,7 @@ export function openAgent(kind, opts = {}) {
4452
4559
  agentMetadataNegativeCache.delete(sid);
4453
4560
  launchNote = buildAgentLaunchNote(kind, model, effort, meta);
4454
4561
  const cmd = buildAgentCmd(kind, binForCmd, model, effort, opts.prompt ?? null, meta);
4455
- const envPrefix = agentEnvPrefix(meta, sid, envVars);
4562
+ const envPrefix = agentEnvPrefix(meta, sid, envVars, interopSocket);
4456
4563
  const full = cwdForCmd ? `cd ${shq(cwdForCmd)} && ${envPrefix}${cmd}` : `${envPrefix}${cmd}`;
4457
4564
  // force:true で送る。起動骨格は `bin '...'` の固定形で、prompt/cwd/effort は shq でクオート済みの
4458
4565
  // 引数=シェルは決して破壊コマンドとして実行しない。破壊ゲート(生シェルコマンド想定)を prompt に
package/dist/index.js CHANGED
@@ -430,7 +430,7 @@ const agentModelDesc = (kind) => kind === "claude"
430
430
  : kind === "codex"
431
431
  ? "起動モデル(例: gpt-5.6-sol / gpt-5.6-terra / gpt-5.6-luna)。省略時は端末 config/CLI 既定を継承" +
432
432
  "(端末側のピンがそのまま効く。実効値は起動応答に明示される)"
433
- : `起動モデル。省略時は ${kind === "grok" ? "grok-4.5" : "grok-composer-2.5-fast"}。` +
433
+ : `起動モデル。省略時は ${kind === "grok" ? "grok-4.6" : "grok-composer-2.5-fast"}。` +
434
434
  (kind === "composer"
435
435
  ? "既定/explicit modelを起動前にlive catalogへ照合し、不在ならfallbackせずエラー"
436
436
  : "explicit modelを起動前にlive catalogへ照合し、不在ならfallbackせずエラー");
@@ -560,7 +560,7 @@ registerAgentTool("codex_agent", "codex", "【Codex (OpenAI)】の対話エー
560
560
  agentCompletionDesc +
561
561
  "model / reasoning_effort を引数で指定可" +
562
562
  "(省略時は端末 config/CLI 既定を継承。実効値は起動応答に明示)。");
563
- registerAgentTool("grok_agent", "grok", "【Grok Build の Grok モデル (既定 grok-4.5)】の対話エージェント TUI を永続端末に起動する。" +
563
+ registerAgentTool("grok_agent", "grok", "【Grok Build の Grok モデル (既定 grok-4.6)】の対話エージェント TUI を永続端末に起動する。" +
564
564
  agentEnvironmentDesc +
565
565
  "turn は pty_send で送る(自動で非ブロック dispatch になる)。" +
566
566
  agentCompletionDesc +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.25.3",
3
+ "version": "0.26.0",
4
4
  "mcpName": "io.github.kitepon/aiterm-mcp",
5
5
  "description": "Persistent tmux terminal MCP for launching and driving Claude, Codex, Grok, or Composer from any MCP client, cross-vendor or same-vendor. Also runs durable PTY sessions for SSH, containers, and REPLs.",
6
6
  "keywords": [