aiterm-mcp 0.45.2 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.46.1] - 2026-10-02
11
+
12
+ ### 修正
13
+
14
+ - 認証PTYが閉じられた、またはbackend再起動で失われた場合、`agent_auth`の`status`は`failed`、`cancel`は取消済みの`blocked`を返し、`session_id:null`で再開始できる。通常PTY・harness不一致・記録の破損や読取り失敗は引き続きエラーとする。
15
+
16
+ ## [0.46.0] - 2026-10-02
17
+
18
+ ### 追加
19
+
20
+ - 公開tool `agent_auth`。Claude/Codex/Grok/Cursorの公式認証を共通のstart・status・cancelで進め、公式URL・device code・入力待ちを`aiterm.agent-auth-result.v1`へ返す。CLI差はharness adapterへ閉じ、資格情報は公式CLIだけが保存する。Claudeの公式初回案内も同じ認証PTYで進め、Grokは公式loginの終了結果を認証確認の正本にし、device codeを公式URLの`user_code`から抽出する。remoteも標準対応し、公開面は18 toolsとなる。
21
+
10
22
  ## [0.45.2] - 2026-10-01
11
23
 
12
24
  ### 修正
@@ -1952,7 +1964,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
1952
1964
  `ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
1953
1965
  provenance.
1954
1966
 
1955
- [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.45.2...HEAD
1967
+ [Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.46.1...HEAD
1968
+ [0.46.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.46.0...v0.46.1
1969
+ [0.46.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.45.2...v0.46.0
1956
1970
  [0.45.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.45.1...v0.45.2
1957
1971
  [0.45.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.45.0...v0.45.1
1958
1972
  [0.45.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.44.1...v0.45.0
package/README.ja.md CHANGED
@@ -143,7 +143,7 @@ diagnostics、recovery、update、releaseを所有します。このREADMEと[
143
143
 
144
144
  **言葉でなく実測で:** 記録済み203テストのベンチマークでは、`pty_read` はコンテキストに載るトークンを生ログの **約 7.1 分の 1** に減らす。しかも pass/fail の判定は畳んでも残る。→ [組み込みシェルツールとの使い分け](#組み込みシェルツールとの使い分け)
145
145
 
146
- 17ツール: 7つのPTYツール、正規のagent起動入口`agent_launch`、移行用の旧3alias、`agent_models`、`agent_configure`、`agent_approval`、`claude_turn`、`claude_approval`、`diagnostics`。backendはPOSIXのtmux/Windows nativeのpsmuxなので、MCPサーバやAIクライアントが再起動してもsessionは生き残る。
146
+ 18ツール: 7つのPTYツール、正規のagent起動入口`agent_launch`、移行用の旧3alias、`agent_models`、`agent_configure`、`agent_auth`、`agent_approval`、`claude_turn`、`claude_approval`、`diagnostics`。backendはPOSIXのtmux/Windows nativeのpsmuxなので、MCPサーバやAIクライアントが再起動してもsessionは生き残る。
147
147
 
148
148
  **v0.28.0では実行基盤harnessとmodelを分離した。** harnessはagent loop・認証・hook・session・transcriptを所有し、modelはその上で選ぶ。Cursor Agent CLIでGPT/Claude/Grokを選んでも完了契約はCursor方式のまま。ComposerはCursorのmodelの一つで、harnessでもGrokのmodelでもない。`harness:"cursor-cli", model:"composer-2.5-fast"`(または`composer-2.5`)で表す。旧起動ツールは同じ実装へ流れる互換alias。
149
149
 
@@ -203,7 +203,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
203
203
  場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
204
204
  Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
205
205
 
206
- **状態:** 開発継続中 · 現行公開版 **v0.45.2** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
206
+ **状態:** 開発継続中 · 現行公開版 **v0.46.1** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
207
207
 
208
208
  ### 更新と巻き戻し
209
209
 
@@ -411,7 +411,7 @@ MCP クライアントが aiterm を stdio 越しにプログラムから駆動
411
411
 
412
412
  ```mermaid
413
413
  flowchart LR
414
- AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_models · agent_configure · agent_approval · claude_turn · claude_approval<br/>旧launcher alias · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 17 tools"]
414
+ AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_models · agent_configure · agent_auth · agent_approval · claude_turn · claude_approval<br/>旧launcher alias · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 18 tools"]
415
415
  S -->|"pty_read<br/>token-reduced"| AI
416
416
  S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>再起動を跨ぐ"]
417
417
  P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
@@ -527,6 +527,7 @@ Claudeの相関済み承認は既存の`claude_approval`を使う。
527
527
  | `pty_list` | textと構造化したsession一覧、明示した非秘密環境変数の照会 | `env_keys?` |
528
528
  | `pty_observe` | pane/harnessの生存、native process identity、状態と活動 | `session_id`, `cursor?` |
529
529
  | `agent_launch` | harnessとmodelを別軸で選ぶ正規agent起動入口 | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
530
+ | `agent_auth` | 公式CLIの認証を開始・確認・取消し、公式URL・device code・入力待ちを返す | `harness`, `action`, `session_id?`, `cwd?`, `env_vars?` |
530
531
  | `agent_models` | harnessが今選べるmodelとreasoning effortを、そのharness自身の一覧からpromptを送らずに取得 | `harness`, `cwd?`, `include_hidden?` |
531
532
  | `agent_approval` | Codexの現在の承認を検査し、単発許可・拒否を送る | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
532
533
  | `claude_agent` / `codex_agent` / `grok_agent` | deprecated互換alias(`composer_agent`は0.41.0で削除。Composerは`agent_launch`の`cursor-cli`で使う) | 旧launcher引数 |
@@ -549,6 +550,14 @@ consumer は `aiterm-runtime-errors snapshot` を読み、durable ingestion 後
549
550
 
550
551
  `agent_configure({ session_id, model?, reasoning_effort? })`はharness標準操作で起動中のClaude/Codex/Grok/Cursorを変更し、PTYと会話contextを維持する。
551
552
 
553
+ `agent_auth({ harness, action:"start"|"status"|"cancel", session_id?, cwd?, env_vars? })`は、各harnessの公式CLIで認証を進める。Claudeは`claude auth login`、CodexとGrokは`login --device-auth`、Cursorは`NO_OPEN_BROWSER=1 cursor-agent login`を使い、資格情報は各CLIだけが保存する。Aitermは資格情報を読取り・copy・編集せず、独自OAuthも実装しない。`remote`は他toolと同じ標準対応。
554
+
555
+ 結果は`aiterm.agent-auth-result.v1`。`status`は`waiting`/`authenticated`/`blocked`/`failed`、`session_id`・`url`・`user_code`・`input_required`・`message`を返す。`start`のsession IDを保存して`status`へ渡す。公式HTTPS URLと明示device codeだけを返し、CLIの生出力・token・OAuth callback codeを結果へ載せない。`input_required:true`なら同じsessionの`pty_read(screen:true)`で公式画面を表示し、`pty_send`/`pty_key`で人の入力を中継する。
556
+
557
+ Grokには公式認証status commandが無いため、session付きの確認は公式loginのexit 0を正本にし、session無しの確認は`blocked`を返す。他harnessは公式statusも照合する。Claudeは認証後の公式初回案内を同じsessionで進め、選択待ちは`blocked`/`input_required:true`で返す。`authenticated`は認証結果であり、`agent_launch`の起動準備完了は別途確認する。`cancel`は指定した認証sessionだけを閉じ、資格情報を削除しない。
558
+
559
+ PTYが消失した場合は、相関記録の有無にかかわらず`status`が`failed`、`cancel`が既に終了・取消済みを示す`blocked`を返し、どちらも`session_id:null`となる。保存したsession IDを解除して`start`で再開始できる。生存中の通常PTYやharness不一致、記録の破損・読取り失敗はエラーを返す。
560
+
552
561
  `agent_models({ harness, cwd?, include_hidden? })`は、導入済みのharnessが今選べるmodel IDとreasoning effortを返す。画面の候補を、実際にagentを動かす端末の一覧から作れる。各harness自身の一覧を読むだけで、promptもturnも送らない。
553
562
 
554
563
  | `harness` | 取得元 | 補足 |
package/README.md CHANGED
@@ -145,7 +145,7 @@ Aiterm and is not a runtime dependency.
145
145
 
146
146
  **Measured, not claimed:** in the recorded 203-test benchmark, a `pty_read` puts **~7.1× fewer tokens** in your context than the raw log — and the pass/fail verdict survives the fold. → [When to reach for it vs. the built-in shell](#when-to-reach-for-it-vs-the-built-in-shell)
147
147
 
148
- Seventeen tools: seven **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` / `pty_observe` — to open, drive, read, and observe one persistent terminal; one canonical **agent launcher**, `agent_launch`, which selects `claude-code`, `codex-cli`, `grok-cli`, or `cursor-cli` as the execution harness; three deprecated launcher aliases kept for migration; `agent_models`; `agent_configure`; `agent_approval`; `claude_turn`; `claude_approval`; and `diagnostics`. The backend is **tmux on POSIX and psmux on native Windows**, so sessions survive even if the MCP server or the AI client restarts.
148
+ 18 tools: seven **PTY tools** — `pty_open` / `pty_send` / `pty_read` / `pty_key` / `pty_close` / `pty_list` / `pty_observe` — to open, drive, read, and observe one persistent terminal; one canonical **agent launcher**, `agent_launch`, which selects `claude-code`, `codex-cli`, `grok-cli`, or `cursor-cli` as the execution harness; three deprecated launcher aliases kept for migration; `agent_models`; `agent_configure`; `agent_auth`; `agent_approval`; `claude_turn`; `claude_approval`; and `diagnostics`. The backend is **tmux on POSIX and psmux on native Windows**, so sessions survive even if the MCP server or the AI client restarts.
149
149
 
150
150
  **v0.28.0 separates the execution harness from the model.** The harness owns the agent loop, authentication, hooks, session, and transcript; `model` is what that harness runs. Cursor Agent CLI can therefore select GPT, Claude, or Grok without changing the completion contract from Cursor hooks to another harness's. Composer is one of Cursor's models, not a harness and not a Grok model: use `harness: "cursor-cli", model: "composer-2.5-fast"` (or `composer-2.5`). The old launcher tools are thin compatibility aliases over the same implementation.
151
151
 
@@ -217,7 +217,7 @@ collection is off by default and performs no network I/O. It ships via
217
217
  tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
218
218
  Release re-registers the Official MCP Registry entry.
219
219
 
220
- **Status:** actively maintained · current public release **v0.45.2** · runs on Linux · WSL2 · macOS · native Windows (tmux on POSIX, the tmux-CLI-compatible [psmux](https://github.com/psmux/psmux) on native Windows — no WSL required) · MIT · see the [CHANGELOG](CHANGELOG.md).
220
+ **Status:** actively maintained · current public release **v0.46.1** · runs on Linux · WSL2 · macOS · native Windows (tmux on POSIX, the tmux-CLI-compatible [psmux](https://github.com/psmux/psmux) on native Windows — no WSL required) · MIT · see the [CHANGELOG](CHANGELOG.md).
221
221
 
222
222
  ### Update and rollback
223
223
 
@@ -399,7 +399,7 @@ The only edits to the captures above are the two `⋮` lines (a long head/tail r
399
399
  `aiterm-setup --json`が`ready`になったら、利用するMCP clientを再起動して接続を確認する。Claude Codeの場合:
400
400
 
401
401
  ```bash
402
- /mcp # aiterm should show as connected, exposing 17 tools
402
+ /mcp # aiterm should show as connected, exposing 18 tools
403
403
  ```
404
404
 
405
405
  Your first session — four calls, one persistent terminal:
@@ -440,7 +440,7 @@ The terminal is real and shared, so a human *can* jump in ([A human can watch](#
440
440
 
441
441
  ```mermaid
442
442
  flowchart LR
443
- AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_models · agent_configure · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 17 tools"]
443
+ AI["AI / MCP client<br/>(the orchestrator)"] -->|"pty_send · pty_observe · agent_launch · agent_models · agent_configure · agent_auth · agent_approval · claude_turn · claude_approval<br/>legacy launcher aliases · diagnostics"| S["aiterm-mcp<br/>stdio MCP · 18 tools"]
444
444
  S -->|"pty_read<br/>token-reduced"| AI
445
445
  S -->|"tmux / psmux<br/>send · capture"| P["persistent PTYs<br/>survive restarts"]
446
446
  P -->|"ssh · docker · repl"| R["nested<br/>remote · container · REPL"]
@@ -560,6 +560,7 @@ continue to use `claude_approval`.
560
560
  | `pty_list` | Text and structured session list, with explicitly requested non-secret environment values | `env_keys?` |
561
561
  | `pty_observe` | Pane/harness liveness, native process identity, state, and activity | `session_id`, `cursor?` |
562
562
  | `agent_launch` | Canonical agent launch; harness and model are independent | `harness`, `prompt?`, `model?`, `reasoning_effort?`, `cwd?`, `write_scope?`, `trust_project?`, `env_vars?`, `throughline_source_session?`, `throughline_supplement_file?` |
563
+ | `agent_auth` | 公式CLIの認証を開始・確認・取消し、公式URL・device code・入力待ちを返す | `harness`, `action`, `session_id?`, `cwd?`, `env_vars?` |
563
564
  | `agent_models` | List the models and reasoning efforts a harness offers now, read from that harness's own catalog without sending a prompt | `harness`, `cwd?`, `include_hidden?` |
564
565
  | `agent_approval` | Inspect a Codex approval and submit a one-time approval or denial | `action`, `session_id`, `approval_choice?`, `observed_prompt_digest?` |
565
566
  | `claude_agent` / `codex_agent` / `grok_agent` | Deprecated compatibility aliases (`composer_agent` was removed in 0.41.0; run Composer with `agent_launch` on `cursor-cli`) | legacy launcher arguments |
@@ -584,6 +585,14 @@ Consumer flow is `aiterm-runtime-errors snapshot`, then `aiterm-runtime-errors a
584
585
 
585
586
  `agent_configure({ session_id, model?, reasoning_effort? })` changes a running Claude, Codex, Grok, or Cursor TUI through the harness's standard controls, preserving the PTY and conversation context.
586
587
 
588
+ `agent_auth({ harness, action:"start"|"status"|"cancel", session_id?, cwd?, env_vars? })`は、各harnessの公式CLIで認証を進める。Claudeは`claude auth login`、CodexとGrokは`login --device-auth`、Cursorは`NO_OPEN_BROWSER=1 cursor-agent login`を使い、資格情報は各CLIだけが保存する。Aitermは資格情報を読取り・copy・編集せず、独自OAuthも実装しない。`remote`は他toolと同じ標準対応。
589
+
590
+ 結果は`aiterm.agent-auth-result.v1`。`status`は`waiting`/`authenticated`/`blocked`/`failed`、`session_id`・`url`・`user_code`・`input_required`・`message`を返す。`start`のsession IDを保存して`status`へ渡す。公式HTTPS URLと明示device codeだけを返し、CLIの生出力・token・OAuth callback codeを結果へ載せない。`input_required:true`なら同じsessionの`pty_read(screen:true)`で公式画面を表示し、`pty_send`/`pty_key`で人の入力を中継する。
591
+
592
+ Grokには公式認証status commandが無いため、session付きの確認は公式loginのexit 0を正本にし、session無しの確認は`blocked`を返す。他harnessは公式statusも照合する。Claudeは認証後の公式初回案内を同じsessionで進め、選択待ちは`blocked`/`input_required:true`で返す。`authenticated`は認証結果であり、`agent_launch`の起動準備完了は別途確認する。`cancel`は指定した認証sessionだけを閉じ、資格情報を削除しない。
593
+
594
+ PTYが消失した場合は、相関記録の有無にかかわらず`status`が`failed`、`cancel`が既に終了・取消済みを示す`blocked`を返し、どちらも`session_id:null`となる。保存したsession IDを解除して`start`で再開始できる。生存中の通常PTYやharness不一致、記録の破損・読取り失敗はエラーを返す。
595
+
587
596
  `agent_models({ harness, cwd?, include_hidden? })` returns the model IDs and reasoning efforts the installed harness offers right now, so a UI can build its choices from the machine that actually runs the agents. It reads each harness's own catalog and never sends a prompt or starts a turn:
588
597
 
589
598
  | `harness` | Source | Notes |
@@ -0,0 +1,23 @@
1
+ /** CLIが示した公式HTTPS URLだけを返す。tokenやOAuth callbackのcodeは公開しない。 */
2
+ export function authUrl(screen, hosts) {
3
+ for (const match of screen.matchAll(/https:\/\/[^\s<>"']+/g)) {
4
+ const candidate = match[0].replace(/[).,;]+$/, "");
5
+ let url;
6
+ try {
7
+ url = new URL(candidate);
8
+ }
9
+ catch {
10
+ continue;
11
+ }
12
+ if (url.username || url.password || !hosts.includes(url.hostname) || url.hash)
13
+ continue;
14
+ if ([...url.searchParams.keys()].some(key => /^(?:access_token|refresh_token|id_token|token|code)$/i.test(key)))
15
+ continue;
16
+ return candidate;
17
+ }
18
+ return null;
19
+ }
20
+ /** 明示されたdevice code欄だけを読む。一般のtokenや認証情報は抽出しない。 */
21
+ export function authUserCode(screen) {
22
+ return /(?:enter (?:this|the following) (?:one[- ]time )?code|(?:one[- ]time |user |device |verification )?code\s*:)(?:\s*\([^\n]*\))?\s*:?[ \t]*(?:\n\s*)?([A-Z0-9]{4}-[A-Z0-9]{4,5})\b/i.exec(screen)?.[1] ?? null;
23
+ }
package/dist/core.js CHANGED
@@ -20,10 +20,10 @@ import { AitermError, telemetryOwnedFailure, ownTelemetryFailure } from "./error
20
20
  import { isWin, SOCKDIR, tmuxCommand, sendPsmuxPayload, loadPtyBufferChunk, pasteBufferBaseArgs, TMUX_EMPTY_CONFIG, attachCommand, normalizePaneCommand, atomicShellMultiline, appendMarkSentinel, markShellCommand, settlePaneLog, paneCwdArgument, sessionEnvironmentLaunch, } from "./tmux-runtime.js";
21
21
  import { sleep, currentUid, runtimeStateBase, safeStatSize, readFileRange, writeJson0600, createEmpty0600, shq, LAUNCH_ID_RE, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, assertSessionName, agentsDir, agentEventPath, agentMetadataPath, writeAgentMetadata, AGENT_EVENT_TAIL_BYTES, agentLabel, agentHarness, subagentInstruction, agentLineageFields, } from "./agent-shared.js";
22
22
  import { catalogUnavailable } from "./model-catalog.js";
23
- import { realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokModelChoices, grokEventsTranscript, latestGrokCompletion, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, grokTuiBusy, grokPaneObservation, grokRateLimitDialog, grokStartupAction, grokLaunchBlockingDialog, assertGrokSandboxNotRejected, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./harnesses/grok.js";
24
- import { bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexRateLimitModelSwitchDialog, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, codexModelChoices, } from "./harnesses/codex.js";
25
- import { OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, claudeModelChoices, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, claudePaneObservation, claudeUsageLimit, claudeStartupAction, claudeLoginMethodMenu, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, claudeSessionTranscriptPath, claudeApiErrorFromLine, claudeApiErrorAfter, } from "./harnesses/claude.js";
26
- import { bindCursorTranscriptSession, cursorTurnBoundary, latestCursorCompletion, observeCursorDone, cursorTranscriptText, assertCursorAuthenticationReady, assertCursorModelAvailable, cursorModelChoices, buildCursorAgentCmd, cursorAgentArgv, cursorPwshLaunchLine, cursorPromptWithLineage, createCursorAgentMetadata, cursorLaunchNote, cursorEffortNavigation, cursorTuiReady, cursorPaneObservation, cursorPromptHooksRunning, cursorUsageLimit, CURSOR_SUBMIT_SEQUENCE, CURSOR_COMPOSER_CONTENT_MARKER_RE, validateCursorModelEffort, } from "./harnesses/cursor.js";
23
+ import { grokAuthPlan, grokAuthStatus, grokAuthPane, realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokModelChoices, grokEventsTranscript, latestGrokCompletion, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, grokTuiBusy, grokPaneObservation, grokRateLimitDialog, grokStartupAction, grokLaunchBlockingDialog, assertGrokSandboxNotRejected, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./harnesses/grok.js";
24
+ import { codexAuthPlan, codexAuthStatus, codexAuthPane, bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexRateLimitModelSwitchDialog, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, codexModelChoices, } from "./harnesses/codex.js";
25
+ import { claudeAuthPlan, claudeAuthStatus, claudeAuthPane, OPERATION_ID_RE, CLAUDE_RESULT_MAX_BYTES, CLAUDE_EFFORTS, agentManagedClaudeSettingsPath, agentClaudeResultPath, agentClaudeOperationPath, agentClaudeApprovalReceiptPath, agentClaudeDispatchReceiptPath, validateOperationId, readClaudeResultText, assertClaudeAuthenticationReady, claudeModelChoices, buildClaudeAgentCmd, claudeLaunchNote, claudeTuiReady, claudePaneObservation, claudeUsageLimit, claudeStartupAction, claudeLoginMethodMenu, CLAUDE_COMPOSER_MARKER_RE, createClaudeAgentMetadata, claudeSessionTranscriptPath, claudeApiErrorFromLine, claudeApiErrorAfter, } from "./harnesses/claude.js";
26
+ import { cursorAuthPlan, cursorAuthStatus, cursorAuthPane, bindCursorTranscriptSession, cursorTurnBoundary, latestCursorCompletion, observeCursorDone, cursorTranscriptText, assertCursorAuthenticationReady, assertCursorModelAvailable, cursorModelChoices, buildCursorAgentCmd, cursorAgentArgv, cursorPwshLaunchLine, cursorPromptWithLineage, createCursorAgentMetadata, cursorLaunchNote, cursorEffortNavigation, cursorTuiReady, cursorPaneObservation, cursorPromptHooksRunning, cursorUsageLimit, CURSOR_SUBMIT_SEQUENCE, CURSOR_COMPOSER_CONTENT_MARKER_RE, validateCursorModelEffort, } from "./harnesses/cursor.js";
27
27
  import { readInterimWords, recordInterimBoundary } from "./interim-words.js";
28
28
  import { resolveAgentBin, resolveThroughlineBin, runThroughlineHandoffContext, isWindowsNativeExecutable, agentBinForPaneShell, resolveWinPaneShell } from "./agent-resolver.js";
29
29
  export { AitermError } from "./errors.js";
@@ -310,7 +310,8 @@ function cleanupAgentState(name) {
310
310
  f.endsWith(".claude-operation.json") ||
311
311
  f.endsWith(".claude-approval.json") ||
312
312
  f.endsWith(".claude-dispatch") ||
313
- f.endsWith(".interim.json"))
313
+ f.endsWith(".interim.json") ||
314
+ f.endsWith(".auth.json"))
314
315
  fs.unlinkSync(p);
315
316
  else if (f.endsWith(".codex-home") || f.endsWith(".grok-home") || f.endsWith(".cursor-plugin") || f.endsWith(".home")) {
316
317
  fs.rmSync(p, { recursive: true, force: true });
@@ -1339,6 +1340,7 @@ export function killAll() {
1339
1340
  f.endsWith(".claude-operation.json") ||
1340
1341
  f.endsWith(".claude-dispatch") ||
1341
1342
  f.endsWith(".interim.json") ||
1343
+ f.endsWith(".auth.json") ||
1342
1344
  f.endsWith(".codex-home") ||
1343
1345
  f.endsWith(".grok-home") ||
1344
1346
  f.endsWith(".home")) {
@@ -3341,7 +3343,201 @@ async function waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, timeoutM
3341
3343
  }
3342
3344
  return { ready, codexRateLimitModelSwitch };
3343
3345
  }
3344
- /** 同じ対話sessionを保ったまま、harness標準の操作でmodel/effortを変更する。 */
3346
+ function authMetadataPath(name) {
3347
+ assertSessionName(name);
3348
+ return path.join(agentsDir(), `${name}.auth.json`);
3349
+ }
3350
+ function authPlan(kind, phase) {
3351
+ switch (kind) {
3352
+ case "claude": return claudeAuthPlan(phase === "onboarding");
3353
+ case "codex": return codexAuthPlan();
3354
+ case "grok": return grokAuthPlan();
3355
+ case "cursor": return cursorAuthPlan();
3356
+ }
3357
+ }
3358
+ function authStatus(kind, bin, cwd, env = process.env) {
3359
+ switch (kind) {
3360
+ case "claude": return claudeAuthStatus(bin, cwd, env);
3361
+ case "codex": return codexAuthStatus(bin, cwd, env);
3362
+ case "grok": return grokAuthStatus();
3363
+ case "cursor": return cursorAuthStatus(bin, cwd, env);
3364
+ }
3365
+ }
3366
+ function authPane(meta, screen) {
3367
+ switch (meta.kind) {
3368
+ case "claude": return claudeAuthPane(screen, meta.phase === "onboarding");
3369
+ case "codex": return codexAuthPane(screen);
3370
+ case "grok": return grokAuthPane(screen);
3371
+ case "cursor": return cursorAuthPane(screen);
3372
+ }
3373
+ }
3374
+ function loadAuthMetadata(name, kind) {
3375
+ let source;
3376
+ try {
3377
+ source = fs.readFileSync(authMetadataPath(name), "utf8");
3378
+ }
3379
+ catch (error) {
3380
+ if (error.code === "ENOENT")
3381
+ return null;
3382
+ throw error;
3383
+ }
3384
+ let value;
3385
+ try {
3386
+ value = JSON.parse(source);
3387
+ }
3388
+ catch (error) {
3389
+ if (error instanceof SyntaxError)
3390
+ throw new AitermError("AGENT_AUTH_STATE_INVALID: 認証sessionの記録が不正です。", 2);
3391
+ throw error;
3392
+ }
3393
+ if (!value || typeof value !== "object" || Array.isArray(value))
3394
+ throw new AitermError("AGENT_AUTH_STATE_INVALID: 認証sessionの記録が不正です。", 2);
3395
+ if (value.kind !== kind)
3396
+ throw new AitermError("AGENT_AUTH_HARNESS_MISMATCH: 認証sessionのharnessが一致しません。", 2);
3397
+ if (typeof value.bin !== "string" || typeof value.cwd !== "string" || !path.isAbsolute(value.cwd)
3398
+ || !["login", "onboarding"].includes(value.phase) || !Array.isArray(value.env_vars)
3399
+ || value.env_vars.some(key => typeof key !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))) {
3400
+ throw new AitermError("AGENT_AUTH_STATE_INVALID: 認証sessionの記録が不正です。", 2);
3401
+ }
3402
+ return value;
3403
+ }
3404
+ /** 一覧の取得失敗を端末の消失へ丸めず、正規PTY一覧で存在を確認する。 */
3405
+ function authSessionExists(name) {
3406
+ return listSessionsResult().sessions.some(session => session.session_id === name);
3407
+ }
3408
+ function authSessionEnvironment(name, keys) {
3409
+ const env = { ...process.env };
3410
+ for (const key of keys) {
3411
+ const value = tmux("show-environment", "-t", name, key);
3412
+ if (value.code !== 0)
3413
+ throw new AitermError("AGENT_AUTH_ENV_UNAVAILABLE: 認証sessionの環境を読めません。", 2);
3414
+ if (value.stdout.trim() === `-${key}`)
3415
+ delete env[key];
3416
+ else if (value.stdout.startsWith(`${key}=`))
3417
+ env[key] = value.stdout.slice(key.length + 1).replace(/\r?\n$/, "");
3418
+ else
3419
+ throw new AitermError("AGENT_AUTH_ENV_INVALID: 認証sessionの環境の形式が不正です。", 2);
3420
+ }
3421
+ return env;
3422
+ }
3423
+ function launchAuthProcess(name, meta) {
3424
+ const plan = authPlan(meta.kind, meta.phase);
3425
+ const env = plan.env.map(([key, value]) => shq(`${key}=${value}`));
3426
+ const cmd = ["exec", "env", ...env, shq(agentBinForPaneShell(meta.bin)), ...plan.args.map(shq)].join(" ");
3427
+ send(name, `cd ${shq(paneCwdArgument(meta.cwd))} && ${cmd}`, { enter: true, force: true, raw: true });
3428
+ }
3429
+ /** 公式CLIの認証processを起動・観測する。資格情報の読取り・copy・独自OAuthは行わない。 */
3430
+ export async function authenticateAgent(kind, options) {
3431
+ const result = (status, session, message, pane) => ({ schema: "aiterm.agent-auth-result.v1", harness: agentHarness(kind),
3432
+ status, session_id: session, url: pane?.url ?? null, user_code: pane?.user_code ?? null,
3433
+ input_required: pane?.input_required ?? false, message: message ?? pane?.message ?? null });
3434
+ if (options.action === "cancel") {
3435
+ if (!options.session_id)
3436
+ throw new AitermError("AGENT_AUTH_SESSION_REQUIRED: cancelにはsession_idが必要です。", 2);
3437
+ const meta = loadAuthMetadata(options.session_id, kind);
3438
+ if (!authSessionExists(options.session_id)) {
3439
+ if (meta)
3440
+ fs.unlinkSync(authMetadataPath(options.session_id));
3441
+ return result("blocked", null, "認証sessionは既に終了または取消済みです。");
3442
+ }
3443
+ if (!meta)
3444
+ throw new AitermError("AGENT_AUTH_SESSION_NOT_FOUND: 指定sessionはAitermの認証sessionではありません。", 2);
3445
+ closeSession(options.session_id);
3446
+ return result("blocked", null, "認証sessionを取り消しました。");
3447
+ }
3448
+ let name = options.session_id ?? null;
3449
+ let meta;
3450
+ if (options.action === "start") {
3451
+ const cwd = options.cwd ?? process.cwd();
3452
+ if (!path.isAbsolute(cwd) || !fs.statSync(cwd).isDirectory())
3453
+ throw new AitermError("cwdは存在するディレクトリの絶対パスで指定してください。", 2);
3454
+ const bin = resolveAgentBin(kind);
3455
+ if (!bin)
3456
+ return result("failed", null, `${agentLabel(kind)}の公式CLIが見つかりません。`);
3457
+ const before = authStatus(kind, bin, cwd);
3458
+ if (before.status === "failed")
3459
+ return result("failed", null, before.message);
3460
+ if (before.status === "authenticated" && !before.verify_onboarding)
3461
+ return result("authenticated", null, null);
3462
+ const envVars = options.env_vars ?? [];
3463
+ [name] = openSession(name, "bash", envVars);
3464
+ meta = { kind, bin, cwd, phase: before.status === "authenticated" ? "onboarding" : "login",
3465
+ env_vars: envVars.filter(key => process.env[key] !== undefined) };
3466
+ try {
3467
+ const kept = tmux("set-option", "-t", name, "remain-on-exit", "on");
3468
+ if (kept.code !== 0)
3469
+ throw new AitermError("AGENT_AUTH_PANE_CONFIG_FAILED: 認証processの終了観測を設定できません。", 2);
3470
+ writeJson0600(authMetadataPath(name), meta);
3471
+ launchAuthProcess(name, meta);
3472
+ }
3473
+ catch (error) {
3474
+ closeSession(name);
3475
+ throw error;
3476
+ }
3477
+ }
3478
+ else if (!name) {
3479
+ const cwd = options.cwd ?? process.cwd();
3480
+ if (!path.isAbsolute(cwd) || !fs.statSync(cwd).isDirectory())
3481
+ throw new AitermError("cwdは存在するディレクトリの絶対パスで指定してください。", 2);
3482
+ const bin = resolveAgentBin(kind);
3483
+ if (!bin)
3484
+ return result("failed", null, `${agentLabel(kind)}の公式CLIが見つかりません。`);
3485
+ const checked = authStatus(kind, bin, cwd);
3486
+ return result(checked.status === "authenticated" ? "authenticated" : checked.status === "failed" ? "failed" : "blocked", null, checked.message ?? (checked.status === "unauthenticated" ? "公式CLIの認証が必要です。agent_authのstartで開始してください。" : null));
3487
+ }
3488
+ else {
3489
+ const loaded = loadAuthMetadata(name, kind);
3490
+ if (!authSessionExists(name))
3491
+ return result("failed", null, "認証sessionが失われました。agent_authのstartで開始してください。");
3492
+ if (!loaded)
3493
+ throw new AitermError("AGENT_AUTH_SESSION_NOT_FOUND: 指定sessionはAitermの認証sessionではありません。", 2);
3494
+ meta = loaded;
3495
+ }
3496
+ const session = name;
3497
+ const deadline = performance.now() + (options.action === "start" ? 3000 : 0);
3498
+ for (;;) {
3499
+ if (!authSessionExists(session))
3500
+ return result("failed", null, "認証sessionが失われました。agent_authのstartで開始してください。");
3501
+ const observed = tmux("display-message", "-p", "-t", session, "#{pane_dead}\t#{pane_dead_status}");
3502
+ if (observed.code !== 0)
3503
+ return result("failed", session, "認証processの終了状態を取得できません。");
3504
+ const [dead, exit] = observed.stdout.trim().split("\t");
3505
+ const pane = authPane(meta, stripControl(captureScreen(session, 120)));
3506
+ if (dead === "1") {
3507
+ if (exit !== "0")
3508
+ return result("failed", session, `公式認証processが失敗しました(exit=${/^\d+$/.test(exit ?? "") ? exit : "unknown"})。`);
3509
+ const checked = authStatus(kind, meta.bin, meta.cwd, authSessionEnvironment(session, meta.env_vars));
3510
+ if (checked.status === "failed")
3511
+ return result("failed", session, checked.message);
3512
+ if (checked.status === "unauthenticated")
3513
+ return result("failed", session, "公式認証processは終了しましたが、公式statusは未認証です。");
3514
+ if (!checked.verify_onboarding)
3515
+ return result("authenticated", session, null);
3516
+ if (meta.phase === "onboarding")
3517
+ return result("blocked", session, "公式CLIの初回案内の完了を確認できません。");
3518
+ meta.phase = "onboarding";
3519
+ writeJson0600(authMetadataPath(session), meta);
3520
+ const restarted = tmux("respawn-pane", "-k", "-t", session, resolveWinPaneShell("bash"));
3521
+ if (restarted.code !== 0)
3522
+ return result("failed", session, "公式CLIの初回案内を起動できません。");
3523
+ launchAuthProcess(session, meta);
3524
+ return result("waiting", session, "公式CLIの初回案内を確認しています。");
3525
+ }
3526
+ if (dead !== "0")
3527
+ return result("failed", session, "認証processの生存状態の形式を認識できません。");
3528
+ if (pane.onboarding_complete) {
3529
+ const checked = authStatus(kind, meta.bin, meta.cwd, authSessionEnvironment(session, meta.env_vars));
3530
+ if (checked.status === "authenticated")
3531
+ return result("authenticated", session, null);
3532
+ return result("failed", session, checked.message ?? "公式CLIのstatusが未認証です。");
3533
+ }
3534
+ if (pane.input_required)
3535
+ return result("blocked", session, null, pane);
3536
+ if (pane.url || performance.now() >= deadline)
3537
+ return result("waiting", session, null, pane);
3538
+ await sleep(100);
3539
+ }
3540
+ }
3345
3541
  /**
3346
3542
  * harnessが今返すmodelとreasoning effortの候補(agent_models)。各harnessの公式の一覧を読むだけで、
3347
3543
  * promptもturnも送らない。取得不能・形式異常はfallbackせずエラーにする。
@@ -1,3 +1,4 @@
1
+ import { authUrl } from "../agent-auth.js";
1
2
  // Claude Code 固有の制御。完了正本は launch 固有 Stop hook が書く event/result(ADR 0025)。
2
3
  // core 所有のサービス(transcript 不在エラー)は引数で注入し、
3
4
  // 依存方向を core → harnesses → agent-shared の一方向に保つ。
@@ -437,3 +438,27 @@ export function claudeApiErrorAfter(meta, startedAtMs) {
437
438
  }
438
439
  return null;
439
440
  }
441
+ export function claudeAuthPlan(onboarding = false) { return { args: onboarding ? [] : ["auth", "login"], env: [] }; }
442
+ export function claudeAuthStatus(bin, cwd, env = process.env) {
443
+ const result = spawnAgentControlCommand(bin, ["auth", "status", "--json"], cwd, { cwd, env, encoding: "utf8", timeout: 5000, maxBuffer: 64 * 1024 });
444
+ let status;
445
+ try {
446
+ status = JSON.parse(result.stdout);
447
+ }
448
+ catch {
449
+ return { status: "failed", message: "Claude Codeの公式認証状態を読めません。" };
450
+ }
451
+ if (!result.error && result.status === 0 && status?.loggedIn === true)
452
+ return { status: "authenticated", verify_onboarding: true, message: null };
453
+ if (!result.error && status?.loggedIn === false)
454
+ return { status: "unauthenticated", message: null };
455
+ return { status: "failed", message: "Claude Codeの公式認証状態の確認に失敗しました。" };
456
+ }
457
+ export function claudeAuthPane(screen, onboarding = false) {
458
+ const url = authUrl(screen, ["claude.ai", "console.anthropic.com", "platform.claude.com"]);
459
+ const input = /Paste (?:the )?code|Enter (?:the )?(?:authorization )?code|Choose the text style|Select login method:|Press (?:Enter|Return)|do you trust|trust this folder/i.test(screen);
460
+ const complete = onboarding && (claudeTuiReady(screen) || /Is this a project you created or one you trust/.test(screen));
461
+ return { url, user_code: null, input_required: input && !complete,
462
+ message: complete ? null : input ? "Claude Codeの公式画面で入力または初回案内の選択を完了してください。" : null,
463
+ onboarding_complete: complete };
464
+ }
@@ -1,3 +1,4 @@
1
+ import { authUrl, authUserCode } from "../agent-auth.js";
1
2
  // Codex 固有の制御。完了正本は root rollout transcript の task_complete(ADR 0022)。
2
3
  // core 所有のサービス(transcript 行読取・rate limit 検知)は引数で注入し、
3
4
  // 依存方向を core → harnesses → agent-shared の一方向に保つ。
@@ -6,6 +7,7 @@ import * as os from "node:os";
6
7
  import * as path from "node:path";
7
8
  import { randomBytes } from "node:crypto";
8
9
  import { AitermError } from "../errors.js";
10
+ import { spawnAgentControlCommand } from "../agent-resolver.js";
9
11
  import * as steer from "aiterm-steer-delivery";
10
12
  import { AITERM_PROFILE } from "../steer-profile.js";
11
13
  import { catalogInvalid, catalogUnavailable, checkedCatalog } from "../model-catalog.js";
@@ -779,3 +781,17 @@ export function codexCatalogFromPages(pages) {
779
781
  models,
780
782
  });
781
783
  }
784
+ export function codexAuthPlan() { return { args: ["login", "--device-auth"], env: [] }; }
785
+ export function codexAuthStatus(bin, cwd, env = process.env) {
786
+ const result = spawnAgentControlCommand(bin, ["login", "status"], cwd, { cwd, env, encoding: "utf8", timeout: 5000, maxBuffer: 64 * 1024 });
787
+ const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
788
+ if (!result.error && result.status === 0 && /Logged in/i.test(output))
789
+ return { status: "authenticated", message: null };
790
+ if (!result.error && /Not logged in/i.test(output))
791
+ return { status: "unauthenticated", message: null };
792
+ return { status: "failed", message: "Codex CLIの公式認証状態を確認できません。" };
793
+ }
794
+ export function codexAuthPane(screen) {
795
+ return { url: authUrl(screen, ["auth.openai.com", "auth0.openai.com", "login.openai.com"]),
796
+ user_code: authUserCode(screen), input_required: false, message: null };
797
+ }
@@ -1,3 +1,4 @@
1
+ import { authUrl } from "../agent-auth.js";
1
2
  // Cursor Agent CLI 固有の制御。通常 ~/.cursor を共有し、完了正本は
2
3
  // launch markerで一意にbindした agent transcript の turn_ended とする。
3
4
  import * as fs from "node:fs";
@@ -568,3 +569,18 @@ export function cursorPaneObservation(screen) {
568
569
  return { state: "idle", reason: "composer_ready" };
569
570
  return { state: "unknown", reason: "unrecognized_screen" };
570
571
  }
572
+ export function cursorAuthPlan() { return { args: ["login"], env: [["NO_OPEN_BROWSER", "1"]] }; }
573
+ export function cursorAuthStatus(bin, cwd, env = process.env) {
574
+ const result = spawnAgentControlCommand(bin, ["status"], cwd, { cwd, env, encoding: "utf8", timeout: 5000, maxBuffer: 64 * 1024 });
575
+ const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
576
+ if (!result.error && /not logged in|unauthenticated|sign in/i.test(output))
577
+ return { status: "unauthenticated", message: null };
578
+ // launcherと同じ公式status契約。stderrやアカウント情報はreceiptへ載せない。
579
+ if (!result.error && result.status === 0)
580
+ return { status: "authenticated", message: null };
581
+ return { status: "failed", message: "Cursor Agent CLIの公式認証状態を確認できません。" };
582
+ }
583
+ export function cursorAuthPane(screen) {
584
+ return { url: authUrl(screen, ["cursor.com", "www.cursor.com", "auth.cursor.com"]),
585
+ user_code: null, input_required: false, message: null };
586
+ }
@@ -1,3 +1,4 @@
1
+ import { authUrl } from "../agent-auth.js";
1
2
  // Grok 固有の制御。Composer は Cursor の model の一つであり、Grok CLI では扱わない
2
3
  // (2026-09-27 grok 1.0.41 実測でcatalogに無い)。
3
4
  // core 所有のサービス(transcript 行読取・rate limit 検知)は引数で注入し、
@@ -519,3 +520,18 @@ export function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath
519
520
  writeAgentMetadata(meta);
520
521
  return meta;
521
522
  }
523
+ export function grokAuthPlan() { return { args: ["login", "--device-auth"], env: [] }; }
524
+ export function grokAuthStatus() {
525
+ // 現行Grokには公式status commandが無い。auth fileの存在を成功とみなさない。
526
+ return { status: "unsupported", message: "Grokの認証状態はagent_authで開始した公式ログインsessionの終了結果で確認します。" };
527
+ }
528
+ export function grokAuthPane(screen) {
529
+ const url = authUrl(screen, ["auth.x.ai", "accounts.x.ai", "grok.com"]);
530
+ // Grok 1.0.46の公式device画面はverification_uri_completeだけを表示する。
531
+ // 人へ見せるuser_codeは、その公式URLの同名parameterを読む。
532
+ const embedded = url ? new URL(url).searchParams.get("user_code") : null;
533
+ if (embedded !== null && !/^[A-Z0-9-]+$/.test(embedded)) {
534
+ throw new AitermError("AGENT_AUTH_CHALLENGE_INVALID: Grokの公式URLにあるdevice codeの形式が不正です。", 2);
535
+ }
536
+ return { url, user_code: embedded, input_required: false, message: null };
537
+ }
package/dist/index.js CHANGED
@@ -736,6 +736,36 @@ registerRemoteAwareTool("claude_approval", {
736
736
  return fail(e);
737
737
  }
738
738
  });
739
+ registerRemoteAwareTool("agent_auth", {
740
+ description: "各harnessの公式CLI認証を開始・確認・取消する。CLIごとのコマンドと認証URL/codeの抽出はAitermが所有し、資格情報は各CLIだけが保存する。" +
741
+ "waitingのURL/codeを人へ表示し、input_requiredなら同じsessionのpty_send/pty_keyで公式画面へ入力する。" +
742
+ "authenticatedは認証の結果であり、agent_launchの起動準備完了とは別。remoteは他toolと同じ標準対応。",
743
+ inputSchema: {
744
+ harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
745
+ action: z.enum(["start", "status", "cancel"]),
746
+ session_id: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/).optional(),
747
+ cwd: z.string().optional().describe("公式CLIを実行する作業ディレクトリの絶対パス"),
748
+ env_vars: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/)).optional().describe("startで認証PTYへ引き継ぐ環境変数名。値はreceiptへ返さない"),
749
+ },
750
+ outputSchema: {
751
+ schema: z.literal("aiterm.agent-auth-result.v1"),
752
+ harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]),
753
+ status: z.enum(["waiting", "authenticated", "blocked", "failed"]),
754
+ session_id: z.string().nullable(),
755
+ url: z.string().nullable(),
756
+ user_code: z.string().nullable(),
757
+ input_required: z.boolean(),
758
+ message: z.string().nullable(),
759
+ },
760
+ }, async ({ harness, ...options }) => {
761
+ try {
762
+ const result = await core.authenticateAgent(kindForHarness(harness), options);
763
+ return { content: [{ type: "text", text: JSON.stringify(result) }], structuredContent: { ...result } };
764
+ }
765
+ catch (error) {
766
+ return fail(error);
767
+ }
768
+ });
739
769
  registerRemoteAwareTool("agent_configure", {
740
770
  description: "起動済みのClaude/Codex/Grok/Cursor agent sessionを再起動せず、会話contextを保ったままmodel/reasoning effortを変更する。" +
741
771
  "各harnessのCLI標準model操作を使う。Cursorのreasoning_effort変更はmodelと同時指定する。",
package/docs/DESIGN.md CHANGED
@@ -246,6 +246,14 @@ shell、接続先、各harnessの公式CLIが所有する。
246
246
 
247
247
  ## Model catalog
248
248
 
249
+ `agent_auth`の共通進行はcore、公式command・status・認証画面の解釈は`src/harnesses/`が所有する。CLIの認証processを通常HOMEのPTYへ一度だけ起動し、`remain-on-exit`と`pane_dead_status`で終了を観測する。credentialを読み取る状態管理や独自OAuthは持たず、Aitermが保存するのは認証sessionのharness・実行file・cwd・phase・引き継いだenvの名前だけである。取消と通常のPTY closeでこの相関記録を削除する。
250
+
251
+ PTYが消失した場合は、相関記録の有無にかかわらず`status`が`failed`、`cancel`が既に終了・取消済みを示す`blocked`を返し、どちらも`session_id:null`となる。保存したsession IDを解除して`start`で再開始できる。生存中の通常PTYやharness不一致、記録の破損・読取り失敗はエラーを返す。
252
+
253
+ Claude/Codex/Cursorは公式statusを照合する。Grokは公式status commandが無いため、認証sessionの公式login exit 0を正本にし、session無しの確認を`blocked`とする。Claudeはlogin完了後に同じPTYへ公式の初回TUIを用意し、初回案内の入力待ちを`blocked`/`input_required:true`へ写す。認証とlauncherの起動準備は別の結果であり、`agent_launch`のready gateを省略しない。
254
+
255
+ 公開receiptは`aiterm.agent-auth-result.v1`で、公式HTTPS URLと明示device codeだけを抽出する。生の画面本文・credential・token・OAuth callback codeは含めない。人の入力は既存の`pty_send`/`pty_key`で同じPTYへ送る。`remote`は標準のremote tool中継を使い、callerはCLI commandや環境ごとの入力方言を組み立てない。
256
+
249
257
  `agent_models`は、harnessが今選べるmodelとreasoning effortを、そのharness自身の一覧から返す。
250
258
  BellTeam等の画面は、保存した固定の一覧ではなく、agentを実際に動かす端末のharnessが返す候補を使う。
251
259
  取得口、出力形式、model IDとeffortの変換は各harness adapterが持ち、`src/model-catalog.ts`は
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiterm-mcp",
3
- "version": "0.45.2",
3
+ "version": "0.46.1",
4
4
  "mcpName": "io.github.kitepon/aiterm-mcp",
5
5
  "description": "Persistent terminal MCP with one harness-based launcher for Claude Code, Codex CLI, Grok CLI, and Cursor Agent CLI, plus durable PTYs for SSH, containers, and REPLs.",
6
6
  "keywords": [