aiterm-mcp 0.61.12 → 0.61.14
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 +24 -1
- package/README.ja.md +3 -1
- package/README.md +3 -1
- package/dist/agent-screen-observation.js +2 -0
- package/dist/core.js +53 -26
- package/dist/execution-parent-worker.js +16 -1
- package/docs/DESIGN.md +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.61.14] - 2026-10-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Native execution participants retain their verified current-process identity instead of spawning another OS identity probe at every scope check. Foreign process, multiplexer, source/target generation and participant checks remain fresh. This reduces admission overhead; it does not reconcile unknown sends or establish recovery from a production mutex timeout.
|
|
15
|
+
|
|
16
|
+
## [0.61.13] - 2026-10-24
|
|
17
|
+
|
|
18
|
+
### Validation scope
|
|
19
|
+
|
|
20
|
+
- Linux full validation passed (1591 passed, 46 skipped, 0 failed). The affected readiness and input paths passed the hosted macOS gate (196 passed, 0 skipped, 0 failed). Windows initially reported 114 passed, 49 skipped and 23 fixture failures: persistent fake CLI shells used sh while their screen renderer emitted PowerShell. Correcting only the Windows fixture shell left product runtime unchanged; all three affected files then passed (62 passed, 9 skipped, 0 failed), retaining the other successful Windows files. POSIX-only native fixtures remain skipped on Windows; this is not native Windows execution-checkpoint acceptance. Fixtures use isolated substitutes without production seats or delivery replay.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Ordinary agent input that has not yet been issued can select the existing steer path when a live busy turn becomes visible during readiness waiting. It stays in the original request and per-session send lock; no native input is retried.
|
|
25
|
+
- Agent sends reject a confirmed exited harness before readiness waiting, recheck the captured generation and harness while preparing, and check again before paste and Enter. Exit after paste retains a possible effect and prevents replay.
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
|
|
29
|
+
- Controlled native fixtures cover readiness-to-busy selection, initial busy input, startup and modal boundaries, exit before and during preparation, stop, source/target replacement, unrelated unknown slots, and exit between paste and Enter. Historical screen samples missing from reported deliveries are not reconstructed from these fixtures.
|
|
30
|
+
|
|
10
31
|
## [0.61.12] - 2026-10-24
|
|
11
32
|
|
|
12
33
|
### Validation scope
|
|
@@ -2821,7 +2842,9 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
2821
2842
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
2822
2843
|
provenance.
|
|
2823
2844
|
|
|
2824
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.
|
|
2845
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.14...HEAD
|
|
2846
|
+
[0.61.14]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.13...v0.61.14
|
|
2847
|
+
[0.61.13]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.12...v0.61.13
|
|
2825
2848
|
[0.61.12]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.11...v0.61.12
|
|
2826
2849
|
[0.61.11]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.10...v0.61.11
|
|
2827
2850
|
[0.61.10]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.9...v0.61.10
|
package/README.ja.md
CHANGED
|
@@ -209,7 +209,7 @@ runtime-error store は canonical dotagents config の `collection.enabled: true
|
|
|
209
209
|
場合だけ収集し、既定OFF、network送信は行いません。tag起点CIのnpm provenance(OIDC Trusted
|
|
210
210
|
Publishing)で公開し、GitHub Release が Official MCP Registry を再登録します。
|
|
211
211
|
|
|
212
|
-
**状態:** 開発継続中 · 現行公開版 **v0.61.
|
|
212
|
+
**状態:** 開発継続中 · 現行公開版 **v0.61.14** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
213
213
|
|
|
214
214
|
### 更新と巻き戻し
|
|
215
215
|
|
|
@@ -257,6 +257,8 @@ pty_read(id, { wait: true }) → 削減済みの出力を読む(完了
|
|
|
257
257
|
|
|
258
258
|
`aiterm.agent-launch-result.v1`は正規`harness`を返し、旧`provider`は互換fieldとして残す。同じ`harness`はagent dispatch、`aiterm-wait`、`agent_configure`、`pty_list`のagent行にも載り、旧vendor/provider/agent fieldは互換用に残る。Codexは通常rollout、Grok CLIは通常session event、Claudeはlaunch固有Stop hook、Cursorは通常agent transcript末尾の`turn_ended`を完了正本に使う。agent sessionへの送信は`pty_send`だけで行い、子の状態はAitermが送る時点の画面で見て振り分ける。Claudeは画面の実行中表示ではなくStopまで残るturnの印で判定する。子のturnが実行中なら各harness標準の操作で現在のturnへ差し込み(`mode=agent_steer`)、完了は差し込み後の作業の終わりに元の依頼へ1回だけ届く。新しい`event_cursor`と配送は作らない。それ以外は非ブロックdispatch(`mode=agent_dispatch`)で、vendor別完了境界を表すopaqueな整数`event_cursor`を返す。Codex親は選択に応じて公式Steerまたは受信キュー、Claude Code親は公式非同期hookで回答本文を自動受信する。それ以外の親は`aiterm-wait`を親のターンを塞がない別processで受ける。Cursorのsubmitキーはadapterが現行CLIのextended keyboard protocolへ変換する。送信textがCursorのcomposerへ残った場合は成功receiptを返さず失敗する。
|
|
259
259
|
|
|
260
|
+
通常の送信は、本文をまだ発行していない入力待ちの間にbusyへ変わると、同じ呼出しの中で差し込みへ切り替える。確定したharness終了は入力待ちの上限まで待たずに拒否する。貼付後の終了は送信済みの可能性を保持し、同じ入力を再送しない。
|
|
261
|
+
|
|
260
262
|
ClaudeがAPIエラーや安全判定の拒否で終了した時は、Stop hookが発火しなくても次の`pty_send`を新しいturnとして扱う。現在のturn開始後のエラー記録だけを確認し、過去のエラーで実行中のturnを解除しない。上流の拒否はエラーのまま返す。
|
|
261
263
|
|
|
262
264
|
Codexがサービスの誤りや通信の失敗でturnを打ち切った時は、完了待ちが`done`ではなく`error`を返す(`aiterm-wait`はexit 7、親への配送は`outcome=error`)。`error`は応答の本文とURLを落とした1行の文で、Codexが記録した種類(`internal_server_error`・`http_connection_failed`など)は`error_kind`に載る。利用上限は今までどおり`rate_limited`。誤りで終わった後の席は、次の`pty_send`を新しいturnとして受ける。
|
package/README.md
CHANGED
|
@@ -227,7 +227,7 @@ collection is off by default and performs no network I/O. It ships via
|
|
|
227
227
|
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
|
|
228
228
|
Release re-registers the Official MCP Registry entry.
|
|
229
229
|
|
|
230
|
-
**Status:** actively maintained · current public release **v0.61.
|
|
230
|
+
**Status:** actively maintained · current public release **v0.61.14** · 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).
|
|
231
231
|
|
|
232
232
|
### Update and rollback
|
|
233
233
|
|
|
@@ -287,6 +287,8 @@ The same primitive hosts another agent's TUI. `agent_launch` starts a selected e
|
|
|
287
287
|
|
|
288
288
|
起動結果には正規`harness`を含む`aiterm.agent-launch-result.v1`が付き、旧`provider`は互換fieldとして残る。同じ`harness`はagent dispatch、`aiterm-wait`、`agent_configure`、`pty_list`にも載る。Codexは通常rollout、Grokは通常session event、Claudeはlaunch固有Stop hook、Cursorは通常agent transcriptの`turn_ended`を完了正本に使う。agentへの送信は`pty_send`だけで行い、Aitermが送る時点で子の状態を見て振り分ける。Claudeは画面の実行中表示ではなくStopまで残るturnの印で判定する。実行中のturnへは差し込み(`mode=agent_steer`、新しい`event_cursor`と配送は作らない)、それ以外は非ブロックdispatch(`mode=agent_dispatch`)で、harnessごとの完了境界を表す整数`event_cursor`を返す。Codex親は選択に応じて公式Steerまたはqueue、Claude Code親は公式非同期hookで本文を自動受信する。他の親は[`aiterm-wait`](#completion-push-for-parent-agents-aiterm-wait)を使う。CursorのsubmitはadapterがCLIのextended keyboard protocolへ変換し、送信本文がcomposerへ残る場合は明示errorにする。
|
|
289
289
|
|
|
290
|
+
An ordinary send can select steer while its input is still unissued and readiness waiting observes a live busy turn. A confirmed exited harness is rejected before the readiness deadline. Exit after paste retains a possible effect; the same input is never replayed.
|
|
291
|
+
|
|
290
292
|
Set `require_agent:true` on `pty_send` when the integration requires agent delivery. If the agent registration is missing, Aiterm refuses before sending any text and returns `AGENT_SESSION_REQUIRED` with an explicit unsent message. The default preserves ordinary PTY sends; combining it with `force:true` is rejected before sending.
|
|
291
293
|
|
|
292
294
|
`pty_send` to an agent session accepts an optional `preface`: one line placed before the text, so the message reads "`preface`, blank line, `text`". For a Claude Code session Aiterm enters the `preface` without the terminal's paste markers and pastes the blank line and the text in one paste. Claude Code wraps long pasted text in `<pasted_content>` and follows instructions inside it only where the user's own words outside the tags ask it to, so this keeps an integration's header (who the message is from) outside the wrapper. For Codex, Grok and Cursor sessions the three parts are joined and pasted as before. The `preface` must be a single line of at most 200 characters with no control characters, must not start with a symbol or whitespace, and must not contain `@`; otherwise Aiterm returns `AGENT_PREFACE_INVALID` and "文字列は送信していません。" before sending anything. It cannot be used with ordinary PTY sends, `force:true`, or `remote`.
|
|
@@ -121,6 +121,8 @@ export async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
121
121
|
samples++;
|
|
122
122
|
if (kind === "grok")
|
|
123
123
|
assertGrokSandboxNotRejected(lastScreen);
|
|
124
|
+
if (opts.selectSteer?.(lastScreen))
|
|
125
|
+
return { ready: false, samples, lastScreen, steer: true };
|
|
124
126
|
if (isAgentTuiIdleReady(kind, lastScreen)) {
|
|
125
127
|
readyStreak++;
|
|
126
128
|
if (readyStreak >= stableSamples)
|
package/dist/core.js
CHANGED
|
@@ -3853,7 +3853,8 @@ function agentTurnSettled(meta) {
|
|
|
3853
3853
|
return false;
|
|
3854
3854
|
}
|
|
3855
3855
|
async function waitAgentTuiReady(name, meta, timeoutMs = AGENT_TUI_READY_TIMEOUT_MS, o = {}) {
|
|
3856
|
-
const result = await waitAgentTuiReadyImpl(meta.kind, () => captureScreen(name, AGENT_TUI_READY_LINES), sleep, { timeoutMs, codexRateLimitRecovery: o.codexRateLimitRecovery,
|
|
3856
|
+
const result = await waitAgentTuiReadyImpl(meta.kind, () => { o.checkInput?.(); return captureScreen(name, AGENT_TUI_READY_LINES); }, sleep, { timeoutMs, codexRateLimitRecovery: o.codexRateLimitRecovery, selectSteer: o.selectSteer,
|
|
3857
|
+
launchLine: agentLaunchLines.get(name), ...(o.settledTurn ? { turnSettled: () => agentTurnSettled(meta) } : {}) });
|
|
3857
3858
|
if (result.ready)
|
|
3858
3859
|
agentLaunchLines.delete(name);
|
|
3859
3860
|
return result;
|
|
@@ -3972,6 +3973,7 @@ export async function __testRetryCursorSubmitIfResidue(kind, first, second) {
|
|
|
3972
3973
|
async function submitAgentComposerText(name, kind, body, o) {
|
|
3973
3974
|
const admit = async (effect) => o.admission === "native" ? withExecutionNativeAdmission(name, effect) : effect();
|
|
3974
3975
|
await admit(() => {
|
|
3976
|
+
o.checkInput?.(false);
|
|
3975
3977
|
o.beforePaste?.();
|
|
3976
3978
|
send(name, body, {
|
|
3977
3979
|
enter: false,
|
|
@@ -3994,7 +3996,10 @@ async function submitAgentComposerText(name, kind, body, o) {
|
|
|
3994
3996
|
else {
|
|
3995
3997
|
await sleep(AGENT_SUBMIT_DELAY_MS);
|
|
3996
3998
|
}
|
|
3997
|
-
await admit(() =>
|
|
3999
|
+
await admit(() => {
|
|
4000
|
+
o.checkInput?.(true);
|
|
4001
|
+
sendKey(name, "Enter", { preserveAgentOperation: o.preserveAgentOperation });
|
|
4002
|
+
});
|
|
3998
4003
|
}
|
|
3999
4004
|
async function sendAgentPromptText(name, text, kind) {
|
|
4000
4005
|
// 起動時の文にも、包みになる長さなら出所の1行を包みの外へ出す(ADR 0116)。
|
|
@@ -4243,13 +4248,14 @@ async function recoverCodexRateLimitModelSwitch(name, meta) {
|
|
|
4243
4248
|
}
|
|
4244
4249
|
/** ready待機中に現れたCodex利用上限modalも、同じ一時keep復旧へ戻す。 */
|
|
4245
4250
|
async function waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, timeoutMs, o = {}) {
|
|
4251
|
+
o.checkInput?.();
|
|
4246
4252
|
let codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
|
|
4247
4253
|
// 上限のmodalを閉じた直後は、画面が落ち着くのを11回の確かめで待つ。
|
|
4248
|
-
let ready = await waitAgentTuiReady(name, meta, timeoutMs, codexRateLimitModelSwitch ? { codexRateLimitRecovery: true } : o);
|
|
4249
|
-
if (!ready.ready && !codexRateLimitModelSwitch) {
|
|
4254
|
+
let ready = await waitAgentTuiReady(name, meta, timeoutMs, codexRateLimitModelSwitch ? { ...o, settledTurn: false, codexRateLimitRecovery: true } : o);
|
|
4255
|
+
if (!ready.ready && !ready.steer && !codexRateLimitModelSwitch) {
|
|
4250
4256
|
codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
|
|
4251
4257
|
if (codexRateLimitModelSwitch)
|
|
4252
|
-
ready = await waitAgentTuiReady(name, meta, timeoutMs, { codexRateLimitRecovery: true });
|
|
4258
|
+
ready = await waitAgentTuiReady(name, meta, timeoutMs, { ...o, settledTurn: false, codexRateLimitRecovery: true });
|
|
4253
4259
|
}
|
|
4254
4260
|
return { ready, codexRateLimitModelSwitch };
|
|
4255
4261
|
}
|
|
@@ -5528,7 +5534,7 @@ export async function dispatchAgentTurn(name, text, o = {}) {
|
|
|
5528
5534
|
loadAgentMetadata(name);
|
|
5529
5535
|
return withAgentSendLock(name, () => dispatchAgentTurnInLock(name, text, o));
|
|
5530
5536
|
}
|
|
5531
|
-
async function dispatchAgentTurnInLock(name, text, o) {
|
|
5537
|
+
async function dispatchAgentTurnInLock(name, text, o, pendingSend) {
|
|
5532
5538
|
const meta = loadAgentMetadata(name);
|
|
5533
5539
|
const operationId = o.operation_id == null ? null : validateOperationId(o.operation_id);
|
|
5534
5540
|
if (operationId && meta.kind !== "claude") {
|
|
@@ -5574,9 +5580,16 @@ async function dispatchAgentTurnInLock(name, text, o) {
|
|
|
5574
5580
|
}
|
|
5575
5581
|
if (meta.kind !== "claude" || claudeColdStart) {
|
|
5576
5582
|
// 上限のpanelを閉じた直後(Grok)は、画面が落ち着くのを11回の確かめで待つ。
|
|
5577
|
-
const recovery = await waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS, { settledTurn: !limitDialog
|
|
5583
|
+
const recovery = await waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS, { settledTurn: !limitDialog,
|
|
5584
|
+
...(pendingSend ? { checkInput: () => assertAgentSendHarness(name, meta),
|
|
5585
|
+
selectSteer: (screen) => agentPaneObservation(meta.kind, screen).state === "busy"
|
|
5586
|
+
&& agentSendTurnRunning(meta, screen) } : {}) });
|
|
5578
5587
|
codexRateLimitModelSwitch = recovery.codexRateLimitModelSwitch;
|
|
5579
5588
|
const { ready } = recovery;
|
|
5589
|
+
if (ready.steer && pendingSend)
|
|
5590
|
+
return steerRunningTurn(name, meta, text, {
|
|
5591
|
+
raw: o.raw, pane_input_recovery: paneInputRecovery, preface: o.preface, follow: pendingSend.follow,
|
|
5592
|
+
});
|
|
5580
5593
|
if (!ready.ready) {
|
|
5581
5594
|
if (codexRateLimitModelSwitch) {
|
|
5582
5595
|
throw new AitermError(`CODEX_RATE_LIMIT_MODEL_SWITCH_RECOVERY_FAILED: ${codexPaneObservation(ready.lastScreen).reason}。今回の文字列は送信していません。`, 2);
|
|
@@ -5612,6 +5625,7 @@ async function dispatchAgentTurnInLock(name, text, o) {
|
|
|
5612
5625
|
await submitAgentComposerText(name, meta.kind, dispatchText, {
|
|
5613
5626
|
force: o.force, raw: o.raw, typedPreface: prefaced.typedPreface, preserveAgentOperation: meta.kind === "claude",
|
|
5614
5627
|
admission: "native", subject: "送信prompt",
|
|
5628
|
+
...(pendingSend ? { checkInput: (started) => assertAgentSendHarness(name, meta, started) } : {}),
|
|
5615
5629
|
beforePaste: () => {
|
|
5616
5630
|
captureExecutionSendBoundary({ launch: { session_id: name, launch_id: meta.launch_id }, boundary: {
|
|
5617
5631
|
event_cursor: startOffset, operation_id: operationId, turn_id: null, steer_id: null
|
|
@@ -5757,37 +5771,49 @@ export async function sendAgentMessage(name, text, o = {}) {
|
|
|
5757
5771
|
}
|
|
5758
5772
|
async function sendAgentMessageInLock(name, text, o) {
|
|
5759
5773
|
const meta = loadAgentMetadata(name);
|
|
5774
|
+
assertAgentSendHarness(name, meta);
|
|
5760
5775
|
// 前面回復は busy 判定より先(bash 前面のままだと画面の実行中マーカーを読んでも打鍵が届かない)。
|
|
5761
5776
|
const paneInputRecovery = await ensureAgentOwnsPaneInput(name, meta.kind);
|
|
5762
5777
|
if (meta.kind === "claude")
|
|
5763
5778
|
paneInputRecovery.push(...await clearClaudeSelectMenuBeforeInput(name));
|
|
5764
5779
|
const screen = captureScreen(name, AGENT_TUI_READY_LINES);
|
|
5765
|
-
let running =
|
|
5766
|
-
// Claudeのtool処理中は画面のbusy表示が消えることがある。Stopまで保持するturnの印を正とし、
|
|
5767
|
-
// APIエラーでStopが来なかった印は、会話記録の終了時刻を確認して消す。
|
|
5768
|
-
// 有効な印がある間は差し込み、終了後は画面にbusy表示が残っても新しいturnとして送る。
|
|
5769
|
-
if (meta.kind === "claude") {
|
|
5770
|
-
clearClaudeApiErrorOperation(meta);
|
|
5771
|
-
running = readClaudeOperationMarker(meta) !== null;
|
|
5772
|
-
}
|
|
5773
|
-
// Cursorはturn_endedを記録へ書いたあとも、しばらく画面にbusy表示を残す。その間に差し込むと待ち行列へ入らず
|
|
5774
|
-
// STEER_NOT_QUEUEDで失敗していた(実測 2026-09-28、完了通知の直後の送信で3回中2回)。記録の末尾がturn_endedなら
|
|
5775
|
-
// turnは終わっているので新しいturnとして送る。dispatchは入力を受け付けるまで待ってから送る。
|
|
5776
|
-
if (running && meta.kind === "cursor" && latestCursorCompletion(meta, readTranscriptLines) !== null)
|
|
5777
|
-
running = false;
|
|
5780
|
+
let running = agentSendTurnRunning(meta, screen);
|
|
5778
5781
|
// Codexは回答を流している間、画面に動作中の行を出さない。その間に届いた文を、Codexは動いているturnへ取り込む(ADR 0101)。
|
|
5779
5782
|
if (!running && meta.kind === "codex" && await codexTurnRunningBehindIdleScreen(name, meta, screen))
|
|
5780
5783
|
running = true;
|
|
5781
|
-
|
|
5784
|
+
if (running) {
|
|
5785
|
+
return steerRunningTurn(name, meta, text, { raw: o.raw, pane_input_recovery: paneInputRecovery, preface: o.preface, follow: o.follow });
|
|
5786
|
+
}
|
|
5787
|
+
return dispatchAgentTurnInLock(name, text, { raw: o.raw, before_send: o.before_send, pane_input_recovery: paneInputRecovery, preface: o.preface }, { follow: o.follow });
|
|
5788
|
+
}
|
|
5789
|
+
// The same generation and existing process must still own this pending input.
|
|
5790
|
+
// Unknown native process observations remain unknown; only proven exit is a
|
|
5791
|
+
// death refusal. This check runs before waiting and inside each input admission.
|
|
5792
|
+
function assertAgentSendHarness(name, meta, inputStarted = false) {
|
|
5793
|
+
const current = observeSession(name);
|
|
5794
|
+
if (current.exists && current.launch_id !== meta.launch_id)
|
|
5795
|
+
throw new ExecutionError("EXECUTION_STALE_GENERATION");
|
|
5796
|
+
if (!current.exists || current.harness_alive === false) {
|
|
5797
|
+
throw new AitermError((inputStarted ? `agent session '${name}' の ${agentLabel(meta.kind)} への入力完了を確認できません。本文を再送しないでください。`
|
|
5798
|
+
: `agent session '${name}' の ${agentLabel(meta.kind)} TUI が入力受付状態になりません。文字列は送信していません。`) +
|
|
5799
|
+
`\nstate=${current.state} reason=${current.reason}`, 2);
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
function agentSendTurnRunning(meta, screen) {
|
|
5803
|
+
// Claude's operation marker survives tool work without a busy indicator.
|
|
5804
|
+
if (meta.kind === "claude") {
|
|
5805
|
+
clearClaudeApiErrorOperation(meta);
|
|
5806
|
+
return readClaudeOperationMarker(meta) !== null;
|
|
5807
|
+
}
|
|
5808
|
+
const running = isAgentTuiBusy(meta.kind, screen);
|
|
5809
|
+
// An unaccepted Grok prompt has no running turn to receive another input.
|
|
5782
5810
|
if (running && meta.kind === "grok" && grokPromptAwaitingAccept(screen)) {
|
|
5783
|
-
throw new AitermError(`GROK_PROMPT_NOT_ACCEPTED_YET vendor=grok session=${
|
|
5811
|
+
throw new AitermError(`GROK_PROMPT_NOT_ACCEPTED_YET vendor=grok session=${meta.aiterm_session}\n` +
|
|
5784
5812
|
"Grokが前の文をまだ受け取っていません(画面の「Waiting for the agent to accept the prompt」)。文字列は送信していません。" +
|
|
5785
5813
|
"前の文の完了待ちが終わってから送り直してください。Grokは120秒受け取れないと、前の文を自分から取り消します。", 2);
|
|
5786
5814
|
}
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
}
|
|
5790
|
-
return dispatchAgentTurnInLock(name, text, { raw: o.raw, before_send: o.before_send, pane_input_recovery: paneInputRecovery, preface: o.preface });
|
|
5815
|
+
// Cursor can retain a stale busy indicator after its recorded turn ended.
|
|
5816
|
+
return running && (meta.kind !== "cursor" || latestCursorCompletion(meta, readTranscriptLines) === null);
|
|
5791
5817
|
}
|
|
5792
5818
|
/**
|
|
5793
5819
|
* 実行中turnへ追加の指示を渡す。各harnessの標準操作だけを使い、完了境界は1つに保つ。
|
|
@@ -5830,6 +5856,7 @@ async function steerRunningTurn(name, meta, text, o) {
|
|
|
5830
5856
|
await submitAgentComposerText(name, meta.kind, text, {
|
|
5831
5857
|
force: true, raw: o.raw, typedPreface: prefaced.typedPreface, preserveAgentOperation: meta.kind === "claude",
|
|
5832
5858
|
admission: "native", subject: "差し込む文",
|
|
5859
|
+
checkInput: started => assertAgentSendHarness(name, meta, started),
|
|
5833
5860
|
});
|
|
5834
5861
|
// GrokとCursorは実行中の送信を待ち行列へ入れる。そのままだと現在turnの完了後に別turnとして動き、
|
|
5835
5862
|
// 完了通知が差し込み前の回答で届いてしまう。待ち行列へ入ったことを確かめ、標準の「今すぐ送る」で現在turnへ移す。
|
|
@@ -15,9 +15,24 @@ import { createSteerExecutionAdmission } from "./execution-steer.js";
|
|
|
15
15
|
import { ExecutionError } from "./execution-wire.js";
|
|
16
16
|
import { parentHookReference } from "./execution-parent-alias.js";
|
|
17
17
|
import { requireExecutionEffectScope } from "./execution-effect.js";
|
|
18
|
+
let localActor;
|
|
19
|
+
function currentLocalActor() {
|
|
20
|
+
// This code is executing in the observed process. Its PID/start identity
|
|
21
|
+
// cannot change during its lifetime; another process has its own module.
|
|
22
|
+
// Keep only a verified self identity, never a server/parent/target sample.
|
|
23
|
+
// Missing/failed first reads remain failures and may be observed again.
|
|
24
|
+
if (!localActor || localActor.pid !== process.pid) {
|
|
25
|
+
const observed = readProcessIdentities([process.pid])[0];
|
|
26
|
+
if (!observed || observed.pid !== process.pid || !observed.started_identity
|
|
27
|
+
|| !Number.isFinite(Date.parse(observed.started_identity)))
|
|
28
|
+
return null;
|
|
29
|
+
localActor = Object.freeze({ pid: observed.pid, started_identity: observed.started_identity });
|
|
30
|
+
}
|
|
31
|
+
return { ...localActor };
|
|
32
|
+
}
|
|
18
33
|
export const localParentWorkerRuntime = {
|
|
19
34
|
scope: () => ({ state_root: stateRoot(), multiplexer: isWin ? { kind: "namespace", key: WIN_NS } : { kind: "socket", key: SOCK } }),
|
|
20
|
-
actor:
|
|
35
|
+
actor: currentLocalActor,
|
|
21
36
|
observeActors: readProcessIdentities,
|
|
22
37
|
observeParentProcess: observeExecutionParentProcess, observeSessionGeneration: observeExecutionSessionGeneration,
|
|
23
38
|
};
|
package/docs/DESIGN.md
CHANGED
|
@@ -53,6 +53,10 @@ private participant factoryはproducer/service/dispatcher/同process用worker pr
|
|
|
53
53
|
source環境の既存8名は一度だけ読み、必要な値だけを固定してproducerとdispatcherへ渡す。
|
|
54
54
|
呼出側のprovider/profile/runtime methodの差替えは構築済みparticipantへ混入させず、現在のscope/actorの変化は作用前に拒否する。
|
|
55
55
|
親managerの保存先とownerも同じscope/actorへ照合する。別processのhookはactorを流用せず、自分のworker factoryで接続する。
|
|
56
|
+
native runtimeの自己actorは、現在実行中のprocessのPIDと開始identityを一度だけOSで確定して保持する。
|
|
57
|
+
同processの生存中に開始identityは変わらないため、scope確認ごとの自己PID照会を行わない。
|
|
58
|
+
初回の欠測・失敗は確定値として保持せず、返すobjectも複製する。注入runtimeのactor、他process・server・
|
|
59
|
+
source/target世代の観測は保持せず、従来どおり各受付で再照合する。
|
|
56
60
|
注入runtimeへ欠けたparent依存を既定OS処理で補わず、完全なfake依存を要求する。このfactoryも通常mainには未注入。
|
|
57
61
|
同producer serviceの停止closeは、既存のissued close slotを持つ間だけ同期の期待世代primitiveへ渡す。
|
|
58
62
|
実killの直前にもruntimeの同期世代読取を通し、scope/actor/期待launchの交代を拒否する。
|
|
@@ -242,6 +246,8 @@ Claude Codeはtool処理中に画面の実行中表示が消え、Stop hookの
|
|
|
242
246
|
それ以外は新しいturnとしてdispatchする。
|
|
243
247
|
振り分けから送り終えるまでの間には、起動直後の入力受付待ちなどの待ちが挟まる。同じsessionへの送信(`pty_send`、`claude_turn issue`、起動時prompt)は1本ずつ通し、
|
|
244
248
|
重なった後の1通は前の1通が終わってから振り分け直す。同じprocessの中は着いた順の待ち行列、別のprocessの間はsocketの置き場の`<name>.agent-send.lock`で排他する。
|
|
249
|
+
通常の`pty_send`は、本文未発行の入力受付待ちに生存busyが見えた時、同じrequest・送信lock内で既存steerを選ぶ。起動・設定の待ちはsteerへ変えない。
|
|
250
|
+
送信前、準備中の観測、貼付とEnterの各native受付内で元launchとharness生存を再確認する。確定した死亡は30秒待たずに断り、貼付後の死亡は作用可能性を保持する(ADR 0134)。
|
|
245
251
|
待つ上限は、起動から最初の描画を待つ上限と貼り付けの上限の和で、越えたら`AGENT_SEND_BUSY`で未送信を返す。判断の記録はADR 0088(`docs/adr/0088-one-agent-send-per-session.md`)。
|
|
246
252
|
CodexとClaude Codeは次のtool境界で同じturnへ取り込む。Cursorは「follow-ups」枠へ入った文を「enter steer」で現在turnへ移し、`turn_ended`は最後に1回書く。
|
|
247
253
|
待ち行列の表示は5秒待ち、子のturnが続いている間だけ30秒まで延ばす(表示が遅い端末がある。turnが終わっていれば、文は新しいturnとして始まっている)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.61.
|
|
3
|
+
"version": "0.61.14",
|
|
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": [
|