aiterm-mcp 0.20.2 → 0.21.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.ja.md CHANGED
@@ -23,7 +23,7 @@
23
23
  >
24
24
  > *MCP = Model Context Protocol — Claude Code のようなツールが AI に機能を差し込むためのオープン標準。*
25
25
 
26
- kitepon.devを運営する[クオ(@QLyun35332)](https://x.com/QLyun35332)が
26
+ [kitepon.dev](https://kitepon.dev/)を運営する[クオ(@QLyun35332)](https://x.com/QLyun35332)が
27
27
  開発・メンテナンスしています。
28
28
 
29
29
  ## MCPクライアントへ導入
@@ -87,14 +87,17 @@ claude mcp add --scope user --transport stdio aiterm -- npx -y aiterm-mcp
87
87
  }
88
88
  ```
89
89
 
90
- **工場での役割:** aiterm-mcpはdotagents開発工場が管理する自作コア10製品の一つです。
91
- 永続PTYと外部agent実行レーンを所有し、dotagentsが製品横断の導入・統合契約を所有します。
90
+ **所有境界:** 本repositoryは永続PTYと外部agent実行レーンを所有します。製品横断の導入と
91
+ host統合は、kitepon.devの製品開発を支える内部基盤
92
+ [dotagents](https://github.com/kitepon-rgb/dotagents)が担当します。
92
93
 
93
94
  **言葉でなく実測で:** このリポジトリ自身の 203 テストで、`pty_read` はコンテキストに載るトークンを生ログの **約 7.1 分の 1** に減らす。しかも pass/fail の判定は畳んでも残る。→ [組み込みシェルツールとの使い分け](#組み込みシェルツールとの使い分け)
94
95
 
95
96
  13 ツール: 6 つの **PTY ツール**(`pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list`)で 1 本の永続端末を開き・操作し・読む。加えて 4 つの **エージェント起動ツール**(`claude_agent` / `codex_agent` / `grok_agent` / `composer_agent`)が別のコーディングエージェントの TUI を新しい端末の中に起動し、`claude_turn`がdurable caller向けの構造化issue/recoveryを、`claude_approval`がmanaged Claudeの相関済み承認UI中継を、`diagnostics`が安全なfactory readinessを返す。バックエンドは **tmux** なので、MCP サーバや AI クライアントが再起動してもセッションは生き残る。
96
97
 
97
- **v0.20.0 を 2026-07-26 に公開。** 待たずに一度だけ観測する
98
+ **v0.20.3では、壊れた認証から複数のmanaged Claude/Fable sessionが同時にloginへ流れる問題を修理。**
99
+ 新規Claude起動はPTY作成前にvendor所有の共有認証を検証し、正常な認証は複数sessionから並行・反復利用できる。
100
+ v0.20では、待たずに一度だけ観測する
98
101
  `aiterm-wait --timeout 0` の未完了を、実際に待って終わらなかった`timeout`と区別し、
99
102
  `running`(exit 5)で返すようにしました。v0.19系では相関済みmanaged Claude approval中継を追加し、
100
103
  複数行shell配送を維持し、native Windowsのfactory diagnosticsを拡張しました。
@@ -132,6 +135,8 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
132
135
 
133
136
  同じ primitive が別エージェントの TUI を宿す。4 つの起動ツールが、Claude/Codex/Grok/Composer の対話 TUI を新しい永続端末の中に起動し、`session_id` を返す。既存の人間向けtextに加えて`aiterm.agent-launch-result.v1` structured receiptも返すため、durable callerは表示文字列を解析せずsession handleを取得できる。以後は同じ `pty_read` / `pty_send` で継続操作する。**起動は常に managed**(aiterm 所有の Stop hook 付き)で、agent session への `pty_send` は非ブロックの **dispatch** になり `event_cursor` 入り receipt を即返す。完了通知は `aiterm-wait --session <id> --cursor <event_cursor>` をホストのバックグラウンドタスクとして実行し、exit 時に receipt の `outcome` で判定する(exit 0=done / 3=timeout=未完了・既定600秒 / 4=closed。親はブロックもポーリングもしない)。起動時 `prompt` を渡した launch は structured receipt にコピペ可能な `wait_command` と `event_cursor`、そして `submit_residue` 観測を含む(true=prompt が composer に未 submit で残存している疑い=案内に従い画面確認から復旧 / false=残存観測せず・成立の保証ではない / null=対象外)。dispatch receipt にも同じ観測が付く。durable machine callerは`claude_turn({ action:"issue"|"recover", session_id, operation_id, ... })`を使い、人間向けerror文字列を解析せず`accepted`/`pending`/`completed`/`unknown`を判定できる。recoveryは再送せず、検証済み完了だけがexact `raw_output`を持つ。通常の`pty_send`/`pty_read`は対話callerと人間向けに維持する。`C-c`後もmarkerを保持し、Stopが来なければsessionをcloseする。`claude_agent` と `codex_agent` の初回 `prompt` は ready gate 経由で送信して待たずに返る(Grok/Composer は argv 渡し)。手動でキー操作したい場合は `pty_open` で素の端末を開き vendor CLI を自分で起動する。
134
137
 
138
+ `codex_agent`・`grok_agent`・`composer_agent`は任意の`write_scope`(`"read-only"`または書込み許可パスの説明)も受ける。指定値はlaunch receipt・session metadata・`pty_list`へ保存する。Codexの`write_scope:"read-only"`だけは実効能力壁であり、aitermがCLIの`--sandbox read-only`を付ける。Grok/Composerには対応する対話起動sandboxがなく、Codexにもパス説明をallowlistへ変換するフラグがないため、それらは強制済みと偽らず`write_scope_enforcement:"declaration_only_unsupported"`を返す。`write_scope`を省略した起動は従来どおりである。
139
+
135
140
  ```text
136
141
  codex_agent({ session_name: "codex1", cwd: "/repo",
137
142
  prompt: "port test/legacy.py to vitest" })
@@ -151,7 +156,7 @@ $ aiterm-wait --session codex1 --cursor <event_cursor> # exit 0=done / 3=timeo
151
156
  | `grok_agent` | Grok Build(xAI、既定`grok-4.5`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?` |
152
157
  | `composer_agent` | Grok Build(xAI、既定`grok-composer-2.5-fast`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?` |
153
158
 
154
- 各ベンダーの CLI が導入・認証済みであること(`claude_agent` は `claude`、`codex_agent` は `codex`、Grok 系は `grok`)。バイナリは `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、各既定path、`PATH` の順で解決する。CLI不在・不正なmodel/effort・実在しない`cwd`はsession作成前に失敗し、残骸を残さない。Claudeは通常settingsを継承しないlaunch専用settingsとStop hookを使い、本文なしeventとowner-only bounded resultを分離する。`pty_read({ agent_transcript:true })`はdigestとbyte数を検証したresultだけを返し、Claude private transcriptを読まない。後着resultは同じsessionからprompt再送なしで回収できる。managed Claudeのactive turn中はC-c以外の`pty_key`と素送信を拒否する。Claudeが`Do you want to proceed?`を表示したら、`claude_approval(action:"inspect", ...)`で画面digestを取得し、表示内容を判断してから、そのdigestと`approve_once`または`deny`を`respond`へ渡す。同じoperation・同じ画面が維持されている時だけ入力し、任意文字列や恒久許可選択肢は中継しない。中断は`C-c`、解除は`pty_close`。
159
+ 各ベンダーの CLI が導入・認証済みであること(`claude_agent` は `claude`、`codex_agent` は `codex`、Grok 系は `grok`)。バイナリは `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、各既定path、`PATH` の順で解決する。CLI不在・不正なmodel/effort・実在しない`cwd`はsession作成前に失敗し、残骸を残さない。Claudeはさらに、PTY作成前に同じCLIの`auth status --json`が`loggedIn:true`を返すことを要求する。未認証・malformed・失敗exit・timeoutは残骸ゼロで失敗し、正常なvendor所有の共有認証は複数sessionから利用する。managed Claudeへのexact `/login`・`/logout`は通常dispatchとforce送信の双方で副作用前に拒否するため、認証は通常端末で一度だけ修理する。Claudeは通常settingsを継承しないlaunch専用settingsとStop hookを使い、本文なしeventとowner-only bounded resultを分離する。`pty_read({ agent_transcript:true })`はdigestとbyte数を検証したresultだけを返し、Claude private transcriptを読まない。後着resultは同じsessionからprompt再送なしで回収できる。managed Claudeのactive turn中はC-c以外の`pty_key`と素送信を拒否する。Claudeが`Do you want to proceed?`を表示したら、`claude_approval(action:"inspect", ...)`で画面digestを取得し、表示内容を判断してから、そのdigestと`approve_once`または`deny`を`respond`へ渡す。同じoperation・同じ画面が維持されている時だけ入力し、任意文字列や恒久許可選択肢は中継しない。中断は`C-c`、解除は`pty_close`。
155
160
 
156
161
  エージェント間の隠れたプロトコルは無い。起動したClaude/Codex/Grok/Composerは利用者がattachできるもう1本の永続sessionであり、MCPクライアントが通常のPTY操作で駆動する。
157
162
 
@@ -353,7 +358,7 @@ consumer は `aiterm-runtime-errors snapshot` を読み、durable ingestion 後
353
358
  | `grok_agent` | Grok Build(xAI、既定`grok-4.5`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?` |
354
359
  | `composer_agent` | Grok Build(xAI、既定`grok-composer-2.5-fast`、`model?`で上書き) | `prompt?`, `model?`, `reasoning_effort?`は非対応(指定時は明示エラー), `cwd?`, `session_name?` |
355
360
 
356
- 対応するCLI(`claude` / `codex` / `grok`)の導入・認証が必要。解決順は`CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、既定path、`PATH`。前提違反はsession作成前に明示失敗する。4 launcherすべてが同じ非ブロックdispatch契約を使い、Claude/Codexの初回promptはready gate経由で送信される。Claudeはisolated managed settingsとhook-captured resultを使い、private transcriptへ依存しない。Claude/Codex/Grok/Composerのlive smokeはすべてgreenであり、fixtureによる検証とは区別して記録する。
361
+ 対応するCLI(`claude` / `codex` / `grok`)の導入・認証が必要。解決順は`CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`、既定path、`PATH`。前提違反はsession作成前に明示失敗する。ClaudeはPTY作成前に構造化認証statusも検証し、managed session内の`/login`・`/logout`を拒否する。4 launcherすべてが同じ非ブロックdispatch契約を使い、Claude/Codexの初回promptはready gate経由で送信される。Claudeはisolated managed settingsとhook-captured resultを使い、private transcriptへ依存しない。Claude/Codex/Grok/Composerのlive smokeはすべてgreenであり、fixtureによる検証とは区別して記録する。
357
362
 
358
363
  エージェントの回答が画面 tailより長ければ、対話callerは`pty_read({ agent_transcript:true })`で再promptなしに全文回収する。Claudeはmanaged Stop hookがowner-only resultへ保存した本文をdigest/byte数で検証して返し、private transcriptを読まない。durable machine callerは`claude_turn`を使う。`issue`は一度だけ送信し、`recover`は決して再送せず、`pending`を破損やidentity不一致と区別する。検証済みの`completed`だけがexact `raw_output`を持ち、`unknown`は未dispatchと帰属不能を区別する。不一致・破損は成功statusへ丸めずtool errorのままにする。IDなしの対話turnも匿名markerで直列化するため、現在Stop待ちの間に古い回答を返さない。CodexはStop hookの`turn_id`で構造化transcriptへjoinし、Grok/Composerは最後の実user行より後ろのassistant行を採る。不在・非agent・抽出不能は明示エラー。
359
364
 
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
  >
24
24
  > *MCP = Model Context Protocol — the open standard that lets tools like Claude Code plug capabilities into an AI.*
25
25
 
26
- Built and maintained by [Quo](https://x.com/QLyun35332) at kitepon.dev.
26
+ Built and maintained by [Quo](https://x.com/QLyun35332) at [kitepon.dev](https://kitepon.dev/en).
27
27
 
28
28
  ## Install in your MCP client
29
29
 
@@ -86,17 +86,20 @@ Save this as `.cursor/mcp.json` for the project, or `~/.cursor/mcp.json` globall
86
86
  }
87
87
  ```
88
88
 
89
- **Factory role:** aiterm-mcp is one of the ten self-owned core products managed by
90
- the dotagents development factory. It owns the persistent PTY and external-agent
91
- execution lane; dotagents owns the cross-product installation and integration
92
- contract.
89
+ **Ownership boundary:** this repository owns the persistent PTY and external-agent
90
+ execution lane. Cross-product installation and host integration are handled by
91
+ [dotagents](https://github.com/kitepon-rgb/dotagents), the internal development
92
+ toolchain behind kitepon.dev's products.
93
93
 
94
94
  **Measured, not claimed:** on this repo's own 203-test suite, 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)
95
95
 
96
96
  Thirteen tools: six **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` — to open, drive, and read one persistent terminal, four **agent launchers** — `claude_agent` / `codex_agent` / `grok_agent` / `composer_agent` — that each start another coding agent's TUI inside a fresh one, `claude_turn` for durable structured issue/recovery, `claude_approval` for correlated managed-Claude approval prompts, and `diagnostics` for safe factory readiness. The backend is **tmux**, so sessions survive even if the MCP server or the AI client restarts.
97
97
 
98
- **v0.20.0 was published on 2026-07-26.** It distinguishes a non-blocking
99
- `aiterm-wait --timeout 0` observation (`running`, exit 5) from a real timed-out
98
+ **v0.20.3 prevents concurrent managed Claude/Fable sessions from turning one broken
99
+ login into many competing login flows.** Every new Claude launch verifies the
100
+ vendor-owned shared credential store before creating a PTY, while healthy credentials
101
+ remain reusable across concurrent and repeated sessions. The v0.20 line also distinguishes
102
+ a non-blocking `aiterm-wait --timeout 0` observation (`running`, exit 5) from a real timed-out
100
103
  wait. The v0.19 line added the correlated managed-Claude approval relay,
101
104
  preserved multiline shell delivery, and extended factory diagnostics on native
102
105
  Windows. As of v0.16/0.17 a parent agent never blocks on aiterm:
@@ -142,6 +145,8 @@ pty_read(id, { wait: true }) → read the token-reduced output, completion
142
145
 
143
146
  The same primitive hosts another agent's TUI. Four launchers each start one vendor's interactive coding-agent TUI inside a fresh persistent terminal and return a `session_id`. Their existing human-readable text is accompanied by an `aiterm.agent-launch-result.v1` structured receipt, so durable callers never parse display text for the session handle; when the launch carries an initial `prompt`, the receipt also includes the `event_cursor`, a ready-made `wait_command` for the completion waiter, and a `submit_residue` observation (`true` = the prompt is likely still sitting unsubmitted in the composer — the hint explains recovery; `false` = no residue observed, not a proof of submission; `null` = not applicable). From there you drive it with the same `pty_read` / `pty_send` you'd use on any shell: read its output token-reduced, send it the next step. (The TUIs are full-screen apps, so `pty_read({ screen: true })` gives you the rendered view.) Every launch is **managed**: aiterm installs its own Stop hook, so turn completion is a first-class event. Sending to an agent session is a non-blocking **dispatch** — the call returns immediately with an `event_cursor`, and completion arrives via [`aiterm-wait`](#completion-push-for-parent-agents-aiterm-wait). Durable machine callers use `claude_turn({ action: "issue" | "recover", session_id, operation_id, ... })`: it returns fixed `accepted` / `pending` / `completed` / `unknown` states without parsing human-facing errors, never resends during recovery, and includes exact `raw_output` only for a verified completion. The same operation ID is carried through the dispatch receipt, active marker, Stop event, and result. The ordinary `pty_send` / `pty_read` surface remains available for interactive callers and humans. `C-c` keeps the marker for a delayed Stop; if no Stop arrives, close the session. An initial `prompt` on `claude_agent`/`codex_agent` is submitted through the same ready gate and the launcher returns without waiting; on Grok/Composer it is passed on the CLI's argv. This needs the vendor's own CLI installed and authenticated — see [Requirements](#requirements).
144
147
 
148
+ `codex_agent`, `grok_agent`, and `composer_agent` also accept an optional `write_scope`: either `"read-only"` or a human-readable description of writable paths. A supplied value is retained in the launch receipt, session metadata, and `pty_list`. For Codex, `write_scope: "read-only"` is an effective boundary: aiterm adds the CLI's `--sandbox read-only` flag. Grok/Composer have no corresponding interactive-launch sandbox, and Codex has no path-description allowlist flag; those cases return `write_scope_enforcement: "declaration_only_unsupported"` rather than claiming enforcement. Omitting `write_scope` preserves prior behavior.
149
+
145
150
  For a managed Claude turn stopped at `Do you want to proceed?`, use `claude_approval(action: "inspect", ...)` to capture the active operation and SHA-256 screen digest, review the displayed command, then call `respond` with that exact digest and either `approve_once` or `deny`. The relay rechecks the operation and screen under the send lock, never exposes arbitrary input or permanent approval, keeps the active marker intact, and records a prompt-free owner-only receipt. `pty_send(force: true)` does not bypass this boundary.
146
151
 
147
152
  ```text
@@ -164,7 +169,7 @@ One call per model, so the tool name itself tells you which model you get:
164
169
  | `grok_agent` | Grok Build, model `grok-4.5` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error; Grok CLI `--effort` is headless-only), `cwd?`, `session_name?` |
165
170
  | `composer_agent` | Grok Build, model `grok-composer-2.5-fast` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error), `cwd?`, `session_name?` |
166
171
 
167
- The vendor CLI must be installed and authenticated (`claude` for `claude_agent`; `codex` for `codex_agent`; `grok` for both Grok tools). aiterm resolves the binary via `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`, then `~/.local/bin/claude` / `~/.local/bin/codex` / `~/.grok/bin/grok`, then `PATH`. Prerequisites are checked **before** a session exists: empty `model` values and unsupported effort values are rejected up front; a missing CLI binary or a nonexistent `cwd` fails for all four. A rejected launch leaves **zero leftover session** behind. Claude and Codex launchers forward `model` and `reasoning_effort` through their vendor CLI's public flags; Grok/Composer reject `reasoning_effort` because it is headless-only. 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 a `launch_operation_id` formatted as `sha256:<64 lowercase hex>`. Repeating the identical launch returns the same structured session receipt without starting the CLI twice; a different correlation ID or launch argument for that session fails explicitly. Claude uses launch-local managed settings containing only aiterm's Stop hook: normal user/project/local hooks are not inherited, 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. A late result remains recoverable from the same session without re-sending the prompt. While a managed Claude turn is active, raw sends and non-interrupt keys are rejected. If Claude displays `Do you want to proceed?`, call `claude_approval(action:"inspect", ...)`, decide from the visible prompt, then call `respond` with the returned digest and either `approve_once` or `deny`. The response is accepted only while the same operation and screen digest remain current; arbitrary text and persistent-allow choices are never relayed. Use `pty_key("C-c")` to interrupt and `pty_close` to abandon the session. For unconstrained manual key-by-key driving, open a plain `pty_open` session and start the vendor CLI yourself. Codex uses a managed `CODEX_HOME`; Grok/Composer isolate their managed homes and pass validated OAuth state through `GROK_AUTH_PATH`. Before the first unbound dispatch, aiterm waits for the vendor TUI's input prompt and fails before sending if it is not ready. Managed completion requires POSIX filesystem semantics (Linux, WSL2, macOS).
172
+ The vendor CLI must be installed and authenticated (`claude` for `claude_agent`; `codex` for `codex_agent`; `grok` for both Grok tools). aiterm resolves the binary via `CLAUDE_BIN` / `CODEX_BIN` / `GROK_BIN`, then `~/.local/bin/claude` / `~/.local/bin/codex` / `~/.grok/bin/grok`, then `PATH`. Prerequisites are checked **before** a session exists: empty `model` values and unsupported effort values are rejected up front; a missing CLI binary or a nonexistent `cwd` fails for all four. Before creating a Claude session, aiterm also requires a successful structured `claude auth status --json` result with `loggedIn: true`; unavailable, malformed, or failed authentication leaves **zero leftover session**. Claude sessions share the vendor-owned credential store rather than copying credentials per launch, so multiple sessions can reuse one healthy login. Managed Claude rejects exact `/login` and `/logout` dispatches, including forced sends: repair authentication once in a normal terminal, then relaunch any stale unauthenticated sessions. Claude and Codex launchers forward `model` and `reasoning_effort` through their vendor CLI's public flags; Grok/Composer reject `reasoning_effort` because it is headless-only. 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 a `launch_operation_id` formatted as `sha256:<64 lowercase hex>`. Repeating the identical launch returns the same structured session receipt without starting the CLI twice; a different correlation ID or launch argument for that session fails explicitly. Claude uses launch-local managed settings containing only aiterm's Stop hook: normal user/project/local hooks are not inherited, 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. A late result remains recoverable from the same session without re-sending the prompt. While a managed Claude turn is active, raw sends and non-interrupt keys are rejected. If Claude displays `Do you want to proceed?`, call `claude_approval(action:"inspect", ...)`, decide from the visible prompt, then call `respond` with the returned digest and either `approve_once` or `deny`. The response is accepted only while the same operation and screen digest remain current; arbitrary text and persistent-allow choices are never relayed. Use `pty_key("C-c")` to interrupt and `pty_close` to abandon the session. For unconstrained manual key-by-key driving, open a plain `pty_open` session and start the vendor CLI yourself. Codex uses a managed `CODEX_HOME`; Grok/Composer isolate their managed homes and pass validated OAuth state through `GROK_AUTH_PATH`. Before the first unbound dispatch, aiterm waits for the vendor TUI's input prompt and fails before sending if it is not ready. Managed completion requires POSIX filesystem semantics (Linux, WSL2, macOS).
168
173
 
169
174
  The managed Codex home links authentication, privately snapshots `config.toml` and `agents/*.toml` custom-role definitions, and keeps sessions/caches isolated. A symlinked role definition is resolved into a regular-file snapshot rather than shared with the source home.
170
175
 
@@ -370,7 +375,7 @@ Each launcher starts a specific vendor's interactive coding-agent TUI inside a f
370
375
  | `grok_agent` | Grok Build, model `grok-4.5` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error; Grok CLI `--effort` is headless-only), `cwd?`, `session_name?` |
371
376
  | `composer_agent` | Grok Build, model `grok-composer-2.5-fast` by default (`model?` overrides) (xAI) | `prompt?`, `model?`, `reasoning_effort?` unsupported (an explicit value is an error), `cwd?`, `session_name?` |
372
377
 
373
- 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, and nonexistent `cwd` fail before a session is created. All four share the same non-blocking dispatch contract for follow-up turns; `claude_agent`/`codex_agent` submit an initial `prompt` through the ready gate. Claude uses isolated managed settings and a hook-captured bounded result rather than private transcript access. Claude, Codex, Grok, and Composer live smokes are green; fixture coverage remains a separate claim. Native Windows can launch agents but managed completion is not supported yet.
378
+ 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, and nonexistent `cwd` fail before a session is created. Claude additionally requires a structured healthy authentication status before any PTY exists, and managed Claude rejects `/login` and `/logout`; repair authentication once in a normal terminal. All four share the same non-blocking dispatch contract for follow-up turns; `claude_agent`/`codex_agent` submit an initial `prompt` through the ready gate. Claude uses isolated managed settings and a hook-captured bounded result rather than private transcript access. Claude, Codex, Grok, and Composer live smokes are green; fixture coverage remains a separate claim. Native Windows can launch agents but managed completion is not supported yet.
374
379
 
375
380
  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. Claude reads the bounded owner-only result captured by the managed 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`. `unknown` distinguishes `operation_not_found` from a receipt whose result can no longer be attributed. Mismatch and corruption remain tool errors rather than being folded into a successful status. ID-less interactive Claude turns are still serialized by an anonymous marker, so an older answer is not returned while the current Stop is pending. Codex joins its structured transcript on the Stop hook `turn_id`; Grok/Composer take the assistant rows after the last real user row. A missing result/transcript, a non-agent session, or an unextractable message is an explicit error, never a silent empty.
376
381
 
File without changes
package/dist/core.js CHANGED
@@ -945,12 +945,23 @@ function prepareSendText(text, o) {
945
945
  assertSendTextSize(text);
946
946
  return text;
947
947
  }
948
+ function assertManagedClaudeCredentialCommandNotSent(name, text) {
949
+ const meta = tryLoadAgentMetadata(name);
950
+ if (meta?.kind !== "claude")
951
+ return;
952
+ const normalized = text.replace(PASTE_MARKERS_RE, "").replace(ANSI_RE, "").replace(CTRL_RE, "").trim();
953
+ if (!/^\/(?:login|logout)$/i.test(normalized))
954
+ return;
955
+ throw new AitermError("managed Claude sessionでは共有認証を変更する /login と /logout を送信できません。" +
956
+ "認証操作は通常端末で一度だけ行い、必要ならこのsessionをcloseして起動し直してください。", 2);
957
+ }
948
958
  export function send(name, text, o = {}) {
949
959
  assertSessionName(name);
950
960
  const enter = o.enter ?? true;
951
961
  if (!sessionExists(name))
952
962
  throw new AitermError(`session '${name}' が無い(open してください)`, 2);
953
963
  assertInitialPromptNotPendingForSend(name, !!o.force);
964
+ assertManagedClaudeCredentialCommandNotSent(name, text);
954
965
  text = prepareSendText(text, o);
955
966
  if (o.mark) {
956
967
  // mark の sentinel は POSIX シェル構文。前面が fish/csh/tcsh 等の非 POSIX 対話シェルだと "$?" が
@@ -1219,6 +1230,7 @@ export function listSessions() {
1219
1230
  const agent = [
1220
1231
  `agent=${meta.kind}`,
1221
1232
  "agent_done=true",
1233
+ meta.write_scope === undefined ? null : `write_scope=${JSON.stringify(meta.write_scope)}`,
1222
1234
  meta.vendor_session_id ? `vendor_session_id=${meta.vendor_session_id}` : null,
1223
1235
  ]
1224
1236
  .filter(Boolean)
@@ -2250,7 +2262,7 @@ function createClaudeAgentMetadata(name, cwd, initialPrompt, launchOperationId,
2250
2262
  writeAgentMetadata(meta);
2251
2263
  return meta;
2252
2264
  }
2253
- function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}) {
2265
+ function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}, writeScope) {
2254
2266
  const launchId = randomBytes(16).toString("hex");
2255
2267
  const eventFile = agentEventPath(name, launchId);
2256
2268
  createEmpty0600NoFollow(eventFile);
@@ -2262,6 +2274,7 @@ function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}) {
2262
2274
  event_file: eventFile,
2263
2275
  created_at: new Date().toISOString(),
2264
2276
  cwd,
2277
+ ...(writeScope === undefined ? {} : { write_scope: writeScope }),
2265
2278
  vendor_session_id: null,
2266
2279
  initial_prompt: initialPrompt,
2267
2280
  hook_route: "managed_codex_home",
@@ -2271,7 +2284,7 @@ function createCodexAgentMetadata(name, cwd, initialPrompt, overrides = {}) {
2271
2284
  writeAgentMetadata(meta);
2272
2285
  return meta;
2273
2286
  }
2274
- function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath) {
2287
+ function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath, writeScope) {
2275
2288
  const launchId = randomBytes(16).toString("hex");
2276
2289
  const eventFile = agentEventPath(name, launchId);
2277
2290
  createEmpty0600NoFollow(eventFile);
@@ -2283,6 +2296,7 @@ function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath) {
2283
2296
  event_file: eventFile,
2284
2297
  created_at: new Date().toISOString(),
2285
2298
  cwd,
2299
+ ...(writeScope === undefined ? {} : { write_scope: writeScope }),
2286
2300
  vendor_session_id: null,
2287
2301
  initial_prompt: initialPrompt,
2288
2302
  hook_route: "managed_grok_home",
@@ -2344,6 +2358,7 @@ function loadAgentMetadata(name) {
2344
2358
  event_file: expectedEvent,
2345
2359
  created_at: typeof m.created_at === "string" ? m.created_at : "",
2346
2360
  cwd: typeof m.cwd === "string" ? m.cwd : null,
2361
+ ...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
2347
2362
  vendor_session_id: typeof m.vendor_session_id === "string" ? m.vendor_session_id : null,
2348
2363
  initial_prompt: normalizeInitialPromptState(m.initial_prompt),
2349
2364
  launch_operation_id: launchOperationId,
@@ -2366,6 +2381,7 @@ function loadAgentMetadata(name) {
2366
2381
  event_file: expectedEvent,
2367
2382
  created_at: typeof m.created_at === "string" ? m.created_at : "",
2368
2383
  cwd: typeof m.cwd === "string" ? m.cwd : null,
2384
+ ...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
2369
2385
  vendor_session_id: typeof m.vendor_session_id === "string" ? m.vendor_session_id : null,
2370
2386
  initial_prompt: normalizeInitialPromptState(m.initial_prompt),
2371
2387
  hook_route: "managed_codex_home",
@@ -2389,6 +2405,7 @@ function loadAgentMetadata(name) {
2389
2405
  event_file: expectedEvent,
2390
2406
  created_at: typeof m.created_at === "string" ? m.created_at : "",
2391
2407
  cwd: typeof m.cwd === "string" ? m.cwd : null,
2408
+ ...(typeof m.write_scope === "string" ? { write_scope: m.write_scope } : {}),
2392
2409
  vendor_session_id: typeof m.vendor_session_id === "string" ? m.vendor_session_id : null,
2393
2410
  initial_prompt: normalizeInitialPromptState(m.initial_prompt),
2394
2411
  hook_route: "managed_grok_home",
@@ -2687,6 +2704,33 @@ function readClaudeResultText(meta, done, operationId) {
2687
2704
  }
2688
2705
  return result.text;
2689
2706
  }
2707
+ const CLAUDE_COMPLETION_MARKER_SETTLE_TIMEOUT_MS = 1_000;
2708
+ function claudeCompletionWasPublishedAfterMarker(meta, marker) {
2709
+ let markerStat;
2710
+ let resultStat;
2711
+ try {
2712
+ markerStat = fs.lstatSync(agentClaudeOperationPath(meta.aiterm_session, meta.launch_id));
2713
+ resultStat = fs.lstatSync(meta.result_file ?? "");
2714
+ }
2715
+ catch {
2716
+ return false;
2717
+ }
2718
+ if (resultStat.mtimeMs < markerStat.mtimeMs)
2719
+ return false;
2720
+ const done = latestAgentDoneEvent(meta, marker.operationId);
2721
+ return done !== null && done.operation_id === marker.operationId;
2722
+ }
2723
+ async function settlePublishedClaudeCompletionMarker(meta, marker) {
2724
+ if (!claudeCompletionWasPublishedAfterMarker(meta, marker))
2725
+ return marker;
2726
+ const deadline = performance.now() + CLAUDE_COMPLETION_MARKER_SETTLE_TIMEOUT_MS;
2727
+ let active = marker;
2728
+ while (active && performance.now() < deadline) {
2729
+ await sleep(AGENT_DONE_POLL_MS);
2730
+ active = readClaudeOperationMarker(meta);
2731
+ }
2732
+ return active;
2733
+ }
2690
2734
  /** agent vendor の構造化 transcript から直近完了ターンの最終回答を読む。 */
2691
2735
  export async function readAgentTranscript(name, o = {}) {
2692
2736
  const meta = loadAgentMetadata(name);
@@ -2695,7 +2739,9 @@ export async function readAgentTranscript(name, o = {}) {
2695
2739
  throw new AitermError("operation_id付き回収はClaude agent sessionだけで使用できます", 2);
2696
2740
  }
2697
2741
  if (meta.kind === "claude") {
2698
- const active = readClaudeOperationMarker(meta);
2742
+ let active = readClaudeOperationMarker(meta);
2743
+ if (active)
2744
+ active = await settlePublishedClaudeCompletionMarker(meta, active);
2699
2745
  if (active) {
2700
2746
  const label = active.operationId ? `operation ${active.operationId}` : "operation_idなしのClaude turn";
2701
2747
  throw new AitermError(`${label} はまだ完了していません。Stop完了後に同じsessionから再取得してください。${agentWaitGuide(name)}`, 2);
@@ -3189,6 +3235,7 @@ export function isAgentSession(name) {
3189
3235
  export async function dispatchAgentTurn(name, text, o = {}) {
3190
3236
  assertSessionName(name);
3191
3237
  const meta = loadAgentMetadata(name);
3238
+ assertManagedClaudeCredentialCommandNotSent(name, text);
3192
3239
  const operationId = o.operation_id == null ? null : validateOperationId(o.operation_id);
3193
3240
  if (operationId && meta.kind !== "claude") {
3194
3241
  throw new AitermError("operation_id はClaude agent sessionだけで使用できます", 2);
@@ -3326,6 +3373,40 @@ function resolveAgentBin(kind) {
3326
3373
  }
3327
3374
  return null;
3328
3375
  }
3376
+ const CLAUDE_AUTH_STATUS_TIMEOUT_MS = 5_000;
3377
+ function assertClaudeAuthenticationReady(bin) {
3378
+ const result = spawnSync(bin, ["auth", "status", "--json"], {
3379
+ encoding: "utf8",
3380
+ timeout: CLAUDE_AUTH_STATUS_TIMEOUT_MS,
3381
+ maxBuffer: 64 * 1024,
3382
+ });
3383
+ let status = null;
3384
+ try {
3385
+ status = JSON.parse((result.stdout ?? "").trim());
3386
+ }
3387
+ catch {
3388
+ status = null;
3389
+ }
3390
+ if (result.error == null &&
3391
+ result.status === 0 &&
3392
+ status !== null &&
3393
+ typeof status === "object" &&
3394
+ !Array.isArray(status) &&
3395
+ status.loggedIn === true) {
3396
+ return;
3397
+ }
3398
+ if (status !== null &&
3399
+ typeof status === "object" &&
3400
+ !Array.isArray(status) &&
3401
+ status.loggedIn === false) {
3402
+ throw new AitermError("Claude Codeの認証を利用できません。sessionは作成していません。" +
3403
+ "通常端末で `claude doctor` を実行し、Keychain/credential storeを直してから一度だけ `claude auth login` を実行してください。" +
3404
+ "managed Claude session内で /login を繰り返さないでください。", 2);
3405
+ }
3406
+ const timedOut = result.error && result.error.code === "ETIMEDOUT";
3407
+ throw new AitermError(`Claude Codeの認証状態を起動前に確認できません${timedOut ? "(5秒でtimeout)" : ""}。sessionは作成していません。` +
3408
+ "`claude auth status --json` と `claude doctor` が成功することを通常端末で確認してください。", 2);
3409
+ }
3329
3410
  function isUsableExecutableFile(candidate) {
3330
3411
  try {
3331
3412
  if (!fs.statSync(candidate).isFile())
@@ -3373,6 +3454,10 @@ function buildAgentCmd(kind, bin, model, effort, prompt, meta = null) {
3373
3454
  else if (kind === "codex") {
3374
3455
  if (meta?.kind === "codex")
3375
3456
  parts.push("--dangerously-bypass-hook-trust");
3457
+ // `codex --help` で確認した実在フラグ。read-only 宣言だけはCLI sandboxへ落とし、
3458
+ // launcher自身が実効能力壁を作る。パス説明はCodex CLIに同等のallowlist引数がないため宣言のまま残す。
3459
+ if (meta?.kind === "codex" && meta.write_scope === "read-only")
3460
+ parts.push("--sandbox", "read-only");
3376
3461
  // model/effort は CLI 引数で明示(config 継承より優先)。agent_done 時は managed home 側
3377
3462
  // config.toml も同値で上書き済み(applyCodexConfigOverrides)。
3378
3463
  if (model)
@@ -3441,12 +3526,17 @@ function agentLabel(kind) {
3441
3526
  // model_reasoning_effort)が対話子へ波及する構造のため、引数・端末config継承・CLI既定の
3442
3527
  // どれで起動したかを起動時点で可視化し、実効 effort=ultra は proactive 自動委譲 ON を警告する。
3443
3528
  function buildAgentLaunchNote(kind, model, effort, meta) {
3529
+ const writeScopeNote = meta?.write_scope === undefined
3530
+ ? ""
3531
+ : kind === "codex" && meta.write_scope === "read-only"
3532
+ ? `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。Codex CLIへ --sandbox read-only を付与し、書込みを実効禁止。`
3533
+ : `\n能力宣言: write_scope=${JSON.stringify(meta.write_scope)}。${kind === "grok" || kind === "composer" ? "このCLIには起動sandbox機構がないため" : "パス単位のsandbox allowlistに対応するCLI引数がないため"}宣言の記録のみ(構造的unsupported)。`;
3444
3534
  if (kind === "claude") {
3445
- return `起動設定: model=${model ?? "CLI既定"} effort=${effort ?? "CLI既定"}。`;
3535
+ return `起動設定: model=${model ?? "CLI既定"} effort=${effort ?? "CLI既定"}。${writeScopeNote}`;
3446
3536
  }
3447
3537
  if (kind !== "codex") {
3448
3538
  return (`起動設定: model=${model ?? GROK_MODEL_DEFAULTS[kind]}(${model ? "引数" : "ツール既定"})。` +
3449
- "reasoning effort は対話 TUI 非対応=未指定で起動。");
3539
+ "reasoning effort は対話 TUI 非対応=未指定で起動。" + writeScopeNote);
3450
3540
  }
3451
3541
  const configPath = meta?.kind === "codex" && meta.codex_home
3452
3542
  ? path.join(meta.codex_home, "config.toml")
@@ -3465,7 +3555,7 @@ function buildAgentLaunchNote(kind, model, effort, meta) {
3465
3555
  ? "⚠ effort=ultra は max 推論+proactive 自動委譲 ON(子エージェント自動生成・使用量急増に注意)。"
3466
3556
  : "");
3467
3557
  const summary = meta?.kind === "codex" && meta.codex_home ? managedCodexConfigSummary(configPath, true) : "";
3468
- return summary ? `${launch}\n${summary}\n` : launch;
3558
+ return (summary ? `${launch}\n${summary}\n` : launch) + writeScopeNote;
3469
3559
  }
3470
3560
  function claudeLaunchRequestDigest({ sessionName, model, effort, cwd, agentDone, }) {
3471
3561
  const canonical = JSON.stringify({
@@ -3507,6 +3597,7 @@ export function openAgent(kind, opts = {}) {
3507
3597
  throw new AitermError("model が空文字です(省略するか有効なモデル名を指定してください)", 2);
3508
3598
  }
3509
3599
  const effort = opts.reasoning_effort ?? null;
3600
+ const writeScope = opts.write_scope;
3510
3601
  if (effort && kind === "claude" && !CLAUDE_EFFORTS.has(effort)) {
3511
3602
  throw new AitermError("Claude Code の reasoning_effort は low/medium/high/xhigh/max のいずれかです", 2);
3512
3603
  }
@@ -3587,6 +3678,8 @@ export function openAgent(kind, opts = {}) {
3587
3678
  requireMatchingClaudeLaunch(opts.session_name, launchOperationId, launchRequestDigest);
3588
3679
  return existingAgentLaunchResult("claude", opts.session_name, model, effort);
3589
3680
  }
3681
+ if (kind === "claude")
3682
+ assertClaudeAuthenticationReady(bin);
3590
3683
  // Windows は起動コマンドが WSL 内 bash で走る(tmux ブリッジ)。bin/cwd を /mnt/c/... 形へ変換して
3591
3684
  // 渡す(ログの toWslPath と対称・A1)。前提: Windows 側に CLI を導入(resolveAgentBin が Windows
3592
3685
  // パスで解決)。toWslPath は session を作る前に呼ぶ=変換失敗(非ドライブパス)で残骸 session を残さない。
@@ -3613,8 +3706,8 @@ export function openAgent(kind, opts = {}) {
3613
3706
  ? kind === "claude"
3614
3707
  ? createClaudeAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", launchOperationId, launchRequestDigest)
3615
3708
  : kind === "codex"
3616
- ? createCodexAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", { model, effort })
3617
- : createGrokAgentMetadata(kind, sid, cwd, opts.prompt ? "pending" : "none", grokAuthPath)
3709
+ ? createCodexAgentMetadata(sid, cwd, opts.prompt ? "pending" : "none", { model, effort }, writeScope)
3710
+ : createGrokAgentMetadata(kind, sid, cwd, opts.prompt ? "pending" : "none", grokAuthPath, writeScope)
3618
3711
  : null;
3619
3712
  if (meta)
3620
3713
  agentMetadataNegativeCache.delete(sid);
@@ -3677,6 +3770,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
3677
3770
  prompt,
3678
3771
  agent_done: true,
3679
3772
  launch_operation_id: opts.launch_operation_id ?? null,
3773
+ write_scope: opts.write_scope,
3680
3774
  });
3681
3775
  // argv prompt(grok/composer)は composer を経由しないため submit 座礁観測の対象外。
3682
3776
  return [sid, hint, prompt ? 0 : null, null];
@@ -3689,6 +3783,7 @@ export async function openAgentWithInitialPrompt(kind, opts = {}) {
3689
3783
  prompt: null,
3690
3784
  agent_done: true,
3691
3785
  launch_operation_id: opts.launch_operation_id ?? null,
3786
+ write_scope: opts.write_scope,
3692
3787
  });
3693
3788
  try {
3694
3789
  const initial = await sendInitialAgentPrompt(sid, prompt, {
package/dist/index.js CHANGED
@@ -420,6 +420,19 @@ const agentCompletionDesc = `起動して投げたら投げっぱなしでよい
420
420
  `結果回収は pty_read(agent_transcript:true)。`;
421
421
  function registerAgentTool(toolName, kind, desc) {
422
422
  const correlatedLaunchSchema = {};
423
+ const supportsWriteScope = kind === "codex" || kind === "grok" || kind === "composer";
424
+ const writeScopeInputSchema = supportsWriteScope
425
+ ? {
426
+ write_scope: z.string().min(1).optional().describe("能力宣言。read-only、または書込みを許可するパスの説明文字列。Codexのread-onlyだけはCLI sandboxで実効禁止する"),
427
+ }
428
+ : {};
429
+ const writeScopeOutputSchema = supportsWriteScope
430
+ ? {
431
+ // write_scope省略時は既存launch receiptを完全に保つため両fieldを出さない。
432
+ write_scope: z.string().optional(),
433
+ write_scope_enforcement: z.enum(["enforced_read_only", "declaration_only_unsupported"]).optional(),
434
+ }
435
+ : {};
423
436
  if (kind === "claude") {
424
437
  correlatedLaunchSchema.launch_operation_id = z
425
438
  .string()
@@ -437,6 +450,7 @@ function registerAgentTool(toolName, kind, desc) {
437
450
  reasoning_effort: z.string().nullish().describe(agentEffortDesc(kind)),
438
451
  cwd: z.string().nullish().describe("作業ディレクトリ(対象リポのルート等・任意)"),
439
452
  session_name: z.string().nullish().describe("セッション名(省略で自動採番)"),
453
+ ...writeScopeInputSchema,
440
454
  ...correlatedLaunchSchema,
441
455
  },
442
456
  outputSchema: {
@@ -451,8 +465,9 @@ function registerAgentTool(toolName, kind, desc) {
451
465
  // 初回prompt dispatch後のsubmit座礁観測(additive)。true=composerに残存を確認(submit未成立の疑い)/
452
466
  // false=残存を観測せず(submit成立の保証ではない)/ null=promptなし・argv prompt・判定不能。
453
467
  submit_residue: z.boolean().nullable(),
468
+ ...writeScopeOutputSchema,
454
469
  },
455
- }, async ({ prompt, model, reasoning_effort, cwd, session_name, launch_operation_id }) => {
470
+ }, async ({ prompt, model, reasoning_effort, cwd, session_name, launch_operation_id, write_scope }) => {
456
471
  try {
457
472
  const [sid, hint, eventCursor, submitResidue] = await core.openAgentWithInitialPrompt(kind, {
458
473
  prompt: prompt ?? undefined,
@@ -461,6 +476,7 @@ function registerAgentTool(toolName, kind, desc) {
461
476
  cwd: cwd ?? undefined,
462
477
  session_name: session_name ?? undefined,
463
478
  launch_operation_id: launch_operation_id ?? undefined,
479
+ ...(supportsWriteScope ? { write_scope } : {}),
464
480
  });
465
481
  const structured = {
466
482
  schema: "aiterm.agent-launch-result.v1",
@@ -470,6 +486,14 @@ function registerAgentTool(toolName, kind, desc) {
470
486
  event_cursor: eventCursor,
471
487
  wait_command: eventCursor === null ? null : `aiterm-wait --session ${sid} --cursor ${eventCursor}`,
472
488
  submit_residue: submitResidue,
489
+ ...(supportsWriteScope && write_scope !== undefined
490
+ ? {}
491
+ : supportsWriteScope ? {
492
+ write_scope,
493
+ write_scope_enforcement: kind === "codex" && write_scope === "read-only"
494
+ ? "enforced_read_only"
495
+ : "declaration_only_unsupported",
496
+ } : {}),
473
497
  };
474
498
  return {
475
499
  content: [{ type: "text", text: `session_id: ${sid}\n${hint}` }],
@@ -483,6 +507,7 @@ function registerAgentTool(toolName, kind, desc) {
483
507
  }
484
508
  registerAgentTool("claude_agent", "claude", "【Claude Code (Anthropic)】の対話エージェントTUIを永続端末に起動する。`claude -p`ではなく、" +
485
509
  "同じ利用者可視sessionへpty_sendで継続入力する。常にmanaged(isolated settingsのStop hook)で起動する。" +
510
+ "起動前に共有認証を構造化確認し、未認証ならsessionを作らない。managed session内の/login・/logoutは拒否する。" +
486
511
  agentCompletionDesc +
487
512
  "Claude の durable turn は claude_turn でも回収できる。");
488
513
  registerAgentTool("codex_agent", "codex", "【Codex (OpenAI)】の対話エージェント TUI を永続端末に起動する。実装・レビュー・調査を対話で回す。" +
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "mcpName": "io.github.kitepon-rgb/aiterm-mcp",
5
5
  "description": "Persistent tmux terminal MCP that lets Claude Code drive Codex CLI's interactive TUI, including slash commands and $imagegen. Also runs durable PTY sessions for SSH, containers, REPLs, and coding agents.",
6
6
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "license": "MIT",
28
28
  "author": {
29
29
  "name": "Quo / クオ at kitepon.dev",
30
- "url": "https://x.com/QLyun35332"
30
+ "url": "https://kitepon.dev/"
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
@@ -54,9 +54,10 @@
54
54
  "scripts": {
55
55
  "build": "tsc",
56
56
  "mcpb:build": "npm run build && node scripts/build-mcpb.mjs && npm ci --omit=dev --ignore-scripts --no-audit --no-fund --prefix dist/mcpb-stage/server && npx --yes @anthropic-ai/mcpb@2.1.2 validate dist/mcpb-stage/manifest.json && npx --yes @anthropic-ai/mcpb@2.1.2 pack dist/mcpb-stage dist/aiterm-mcp.mcpb",
57
- "prepublishOnly": "npm run build",
57
+ "verify:release-commit": "node scripts/verify-release-commit.mjs",
58
+ "prepublishOnly": "npm run verify:release-commit && npm run build",
58
59
  "start": "node dist/index.js",
59
- "test": "npm run build && node --test test/*.test.mjs"
60
+ "test": "npm run build && node --test scripts/verify-release-commit.test.mjs test/*.test.mjs"
60
61
  },
61
62
  "dependencies": {
62
63
  "@modelcontextprotocol/sdk": "^1.29.0",