aiterm-mcp 0.25.2 → 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 +5 -5
- package/dist/core.js +162 -45
- package/dist/index.js +2 -2
- package/package.json +1 -1
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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 !==
|
|
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 !==
|
|
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
|
}
|
|
@@ -1625,7 +1703,11 @@ function resolveAndValidateGrokAuth(srcHome) {
|
|
|
1625
1703
|
try {
|
|
1626
1704
|
fd = fs.openSync(authPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
1627
1705
|
const st = fs.fstatSync(fd);
|
|
1628
|
-
|
|
1706
|
+
// Windows の fs.Stats.mode は POSIX permission bit を持たず、常に 666/777 相当を報告する
|
|
1707
|
+
// (NTFS ACL は別体系)。currentUid と同じ既知制約の明示的受容として、Windows では
|
|
1708
|
+
// group/other bit 検証を行わない。isFile・nlink・owner・size・O_NOFOLLOW・realpath 検証は共通に維持する。
|
|
1709
|
+
const worldAccessible = !isWin && (st.mode & 0o077) !== 0;
|
|
1710
|
+
if (!st.isFile() || st.nlink !== 1 || st.uid !== currentUid() || worldAccessible || st.size > GROK_AUTH_MAX_BYTES) {
|
|
1629
1711
|
throw new AitermError("Grok 認証正本の安全検証に失敗しました", 2);
|
|
1630
1712
|
}
|
|
1631
1713
|
const value = JSON.parse(fs.readFileSync(fd, "utf8"));
|
|
@@ -1643,7 +1725,8 @@ function resolveAndValidateGrokAuth(srcHome) {
|
|
|
1643
1725
|
// /tmp のような root 所有 + sticky の共有 directory は、本人所有の private な
|
|
1644
1726
|
// 直下 directory を他 UID が rename/unlink できないため許可する。sticky 無しの
|
|
1645
1727
|
// group/other writable 祖先は path swap が可能なので従来どおり拒否する。
|
|
1646
|
-
|
|
1728
|
+
// Windows は directory も mode bit を持たない(常に 777 相当)ため、同じ受容で除外する。
|
|
1729
|
+
const writableByOthers = !isWin && (dirSt.mode & 0o022) !== 0;
|
|
1647
1730
|
const protectedSharedRoot = dirSt.uid === 0 && (dirSt.mode & 0o1000) !== 0;
|
|
1648
1731
|
if (!dirSt.isDirectory()
|
|
1649
1732
|
|| dirSt.isSymbolicLink()
|
|
@@ -3376,8 +3459,9 @@ function isAgentTuiReady(kind, screen) {
|
|
|
3376
3459
|
}
|
|
3377
3460
|
// Grok Build 0.2.117 は起動完了後に製品名を消し、model footerだけを残す。
|
|
3378
3461
|
// Composerも同じfrontendでmodel名だけが異なるため、両方をvendor UIの根拠にする。
|
|
3462
|
+
// Windows native grok.exe(1.0.4 実測)は入力欄markerを `❯` でなく `>` で描画するため両方を受ける。
|
|
3379
3463
|
const grokFrontend = screen.includes("Grok Build") || /\b(?:Grok|Composer)\s+[\w.()-]+/.test(screen);
|
|
3380
|
-
return grokFrontend && /(^|\n|\s)
|
|
3464
|
+
return grokFrontend && /(^|\n|\s)[❯>]/.test(screen);
|
|
3381
3465
|
}
|
|
3382
3466
|
// Codex/Claude は実行中に「(esc to interrupt)」を表示する(実機採取)。startup 側の処理
|
|
3383
3467
|
// (MCP initialize 等)が走ったまま composer だけ描画されている画面は入力受付とみなさない。
|
|
@@ -3441,7 +3525,8 @@ function agentSubmitResidueOnScreen(kind, screen, tail) {
|
|
|
3441
3525
|
const lines = screen.split("\n");
|
|
3442
3526
|
// 入力欄マーカーは ready 判定と同じ記号を行頭基準で探す。submit 済みの transcript echo は
|
|
3443
3527
|
// マーカー行より上に出るため、最後のマーカー行以降だけを composer 領域として見る。
|
|
3444
|
-
|
|
3528
|
+
// grok/composer は Windows native 描画(`>`・実測 1.0.4)も ready 判定と同様に受ける。
|
|
3529
|
+
const markerRe = kind === "codex" ? /^\s*[›>]/ : kind === "claude" ? /^\s*❯/ : /(^|\s)[❯>]/;
|
|
3445
3530
|
let markerIdx = -1;
|
|
3446
3531
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3447
3532
|
if (markerRe.test(lines[i])) {
|
|
@@ -3937,7 +4022,7 @@ function resolveAgentBin(kind) {
|
|
|
3937
4022
|
? ["CLAUDE_BIN", [".local", "bin", "claude"], "claude"]
|
|
3938
4023
|
: kind === "codex"
|
|
3939
4024
|
? ["CODEX_BIN", [".local", "bin", "codex"], "codex"]
|
|
3940
|
-
: ["GROK_BIN", [".grok", "bin", "grok"], "grok"];
|
|
4025
|
+
: ["GROK_BIN", [".grok", "bin", isWin ? "grok.exe" : "grok"], "grok"];
|
|
3941
4026
|
const fromEnv = process.env[envVar];
|
|
3942
4027
|
if (fromEnv) {
|
|
3943
4028
|
// 明示指定 env は実在を検証する。存在しないパスを黙って返すと、session を作って
|
|
@@ -4158,8 +4243,9 @@ function shq(s) {
|
|
|
4158
4243
|
}
|
|
4159
4244
|
// grok CLI はモデル未指定だと端末側 default に従うため、ツール契約として既定 slug を固定する。
|
|
4160
4245
|
// codex は既定 slug を持たず端末 config/CLI 既定に委ねる(起動応答で実効値を報告する)。
|
|
4246
|
+
// grok 既定は dotagents 規範(docs/02_models.md: xAI 旗艦 = grok-4.6)に従う。
|
|
4161
4247
|
const GROK_MODEL_DEFAULTS = {
|
|
4162
|
-
grok: "grok-4.
|
|
4248
|
+
grok: "grok-4.6",
|
|
4163
4249
|
composer: "grok-composer-2.5-fast",
|
|
4164
4250
|
};
|
|
4165
4251
|
const CLAUDE_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
@@ -4197,7 +4283,10 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
4197
4283
|
if (effort)
|
|
4198
4284
|
parts.push("--reasoning-effort", shq(effort));
|
|
4199
4285
|
if ((meta?.kind === "grok" || meta?.kind === "composer") && meta.write_scope === "read-only") {
|
|
4200
|
-
|
|
4286
|
+
// read-only は sandbox が実効書込み禁止を作るため、MCP ツール許可ダイアログの自動承認を
|
|
4287
|
+
// 付けても能力は増えない。無人 subagent が初回 MCP 使用の許可待ちで停止する実障害への対処。
|
|
4288
|
+
// read-only 以外の launch には付けない=権限拡大しない。
|
|
4289
|
+
parts.push("--sandbox", "read-only", "--always-approve");
|
|
4201
4290
|
}
|
|
4202
4291
|
if ((meta?.kind === "grok" || meta?.kind === "composer") && meta.hook_route === "shared_grok_home") {
|
|
4203
4292
|
parts.push("--session-id", shq(meta.vendor_session_id ?? ""), "--rules", shq(subagentInstruction(meta)));
|
|
@@ -4209,36 +4298,51 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
|
|
|
4209
4298
|
parts.push(shq(prompt)); // 初手プロンプト(任意)
|
|
4210
4299
|
return parts.join(" ");
|
|
4211
4300
|
}
|
|
4212
|
-
|
|
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) {
|
|
4213
4315
|
const inherited = envVars.flatMap((name) => {
|
|
4214
4316
|
const value = process.env[name];
|
|
4215
4317
|
return value === undefined ? [] : [`${name}=${shq(value)}`];
|
|
4216
4318
|
});
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
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(" ") + " " : "";
|
|
4242
4346
|
}
|
|
4243
4347
|
function agentLabel(kind) {
|
|
4244
4348
|
return kind === "claude"
|
|
@@ -4256,7 +4360,8 @@ function buildAgentLaunchNote(kind, model, effort, meta) {
|
|
|
4256
4360
|
const writeScopeNote = meta?.write_scope === undefined
|
|
4257
4361
|
? ""
|
|
4258
4362
|
: (kind === "codex" || kind === "grok" || kind === "composer") && meta.write_scope === "read-only"
|
|
4259
|
-
? `\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内のため能力拡大なし)。")
|
|
4260
4365
|
: `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。パス単位のsandbox allowlistに対応するCLI引数がないため宣言の記録のみ(構造的unsupported)。`;
|
|
4261
4366
|
if (kind === "claude") {
|
|
4262
4367
|
return `起動設定: model=${model ?? "CLI既定"} effort=${effort ?? "CLI既定"}。${writeScopeNote}`;
|
|
@@ -4406,10 +4511,22 @@ export function openAgent(kind, opts = {}) {
|
|
|
4406
4511
|
// Windows は起動コマンドが WSL 内 bash で走る(tmux ブリッジ)。bin/cwd を /mnt/c/... 形へ変換して
|
|
4407
4512
|
// 渡す(ログの toWslPath と対称・A1)。前提: Windows 側に CLI を導入(resolveAgentBin が Windows
|
|
4408
4513
|
// パスで解決)。toWslPath は session を作る前に呼ぶ=変換失敗(非ドライブパス)で残骸 session を残さない。
|
|
4409
|
-
// 未検証リスク: npm グローバル導入の codex.cmd/.bat
|
|
4410
|
-
//
|
|
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
|
+
}
|
|
4411
4525
|
const binForCmd = agentBinForWslShell(bin);
|
|
4412
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;
|
|
4413
4530
|
const grokAuthPath = agentDone && (kind === "grok" || kind === "composer") ? resolveAndValidateGrokAuth(realGrokHome()) : null;
|
|
4414
4531
|
if (kind === "grok" || kind === "composer") {
|
|
4415
4532
|
const requestedModel = model ?? (kind === "composer" ? GROK_MODEL_DEFAULTS.composer : null);
|
|
@@ -4442,7 +4559,7 @@ export function openAgent(kind, opts = {}) {
|
|
|
4442
4559
|
agentMetadataNegativeCache.delete(sid);
|
|
4443
4560
|
launchNote = buildAgentLaunchNote(kind, model, effort, meta);
|
|
4444
4561
|
const cmd = buildAgentCmd(kind, binForCmd, model, effort, opts.prompt ?? null, meta);
|
|
4445
|
-
const envPrefix = agentEnvPrefix(meta, sid, envVars);
|
|
4562
|
+
const envPrefix = agentEnvPrefix(meta, sid, envVars, interopSocket);
|
|
4446
4563
|
const full = cwdForCmd ? `cd ${shq(cwdForCmd)} && ${envPrefix}${cmd}` : `${envPrefix}${cmd}`;
|
|
4447
4564
|
// force:true で送る。起動骨格は `bin '...'` の固定形で、prompt/cwd/effort は shq でクオート済みの
|
|
4448
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.
|
|
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.
|
|
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.
|
|
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": [
|