aiterm-mcp 0.55.3 → 0.55.4
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 +19 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/dist/agent-shared.js +19 -0
- package/dist/core.js +52 -9
- package/dist/harnesses/codex.js +42 -1
- package/dist/harnesses/grok.js +30 -1
- package/docs/DESIGN.md +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.55.4] - 2026-10-06
|
|
11
|
+
|
|
12
|
+
### 変更
|
|
13
|
+
|
|
14
|
+
- 止まっているCodex・Grok・Cursorの席へ`pty_send`で新しいturnを送る時、起動直後のための5秒の確かめを待たない
|
|
15
|
+
(ADR 0097)。受け付けを返すまで毎回5.4〜5.7秒かかっていた。画面を500msおきに11回続けて読む入力受付の確かめ
|
|
16
|
+
(ADR 0014)を、2通目以降の文にも毎回通していた。連携元の本番で、Codexの席への新しいturn17通が
|
|
17
|
+
5,686〜5,858msに揃っているのを見た。
|
|
18
|
+
- 席のharnessが自分の記録(Codexのrollout、Grokの`events.jsonl`、Cursorのtranscript)で「この起動でturnを1つ終えていて、
|
|
19
|
+
次のturnを始めていない」と示している時は、100msおきに2回続けて画面が入力待ちなら送る。
|
|
20
|
+
- 実物の席での受け付けまでの時間: Codex 5.6秒→0.66秒、Grok 5.6秒→0.65秒、Cursor 5.4秒→0.41秒(Linux)。
|
|
21
|
+
Macは5.8秒→0.75秒、Windowsは6.5秒→1.4秒(Codex)。
|
|
22
|
+
- 起動直後の最初の文と、記録の上でturnが動いている間に届いた文は、今までどおり11回の確かめを通る。
|
|
23
|
+
- 起動時prompt、`agent_configure`の後、利用上限のmodalを閉じた直後の確かめは変わらない。Claudeの席は元から
|
|
24
|
+
この確かめを通らない。
|
|
25
|
+
- 道具の返りの形と断りの文面は変わらない。
|
|
26
|
+
|
|
10
27
|
## [0.55.3] - 2026-10-06
|
|
11
28
|
|
|
12
29
|
### 修正
|
|
@@ -2297,7 +2314,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
2297
2314
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
2298
2315
|
provenance.
|
|
2299
2316
|
|
|
2300
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.
|
|
2317
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.4...HEAD
|
|
2318
|
+
[0.55.4]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.3...v0.55.4
|
|
2301
2319
|
[0.55.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.2...v0.55.3
|
|
2302
2320
|
[0.55.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.1...v0.55.2
|
|
2303
2321
|
[0.55.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.55.0...v0.55.1
|
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.55.
|
|
212
|
+
**状態:** 開発継続中 · 現行公開版 **v0.55.4** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
213
213
|
|
|
214
214
|
### 更新と巻き戻し
|
|
215
215
|
|
package/README.md
CHANGED
|
@@ -223,7 +223,7 @@ collection is off by default and performs no network I/O. It ships via
|
|
|
223
223
|
tag-triggered CI with npm provenance (OIDC Trusted Publishing); the GitHub
|
|
224
224
|
Release re-registers the Official MCP Registry entry.
|
|
225
225
|
|
|
226
|
-
**Status:** actively maintained · current public release **v0.55.
|
|
226
|
+
**Status:** actively maintained · current public release **v0.55.4** · 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).
|
|
227
227
|
|
|
228
228
|
### Update and rollback
|
|
229
229
|
|
package/dist/agent-shared.js
CHANGED
|
@@ -16,6 +16,23 @@ export function safeStatSize(p) {
|
|
|
16
16
|
return 0;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* 記録の末尾から、行を新しい順に返す。書いている途中の行(改行で終わっていない末尾)がある時はnullを返す。
|
|
21
|
+
* 読む範囲の先頭で切れた行は捨てる。
|
|
22
|
+
*/
|
|
23
|
+
export function readTailLinesNewestFirst(file, maxBytes) {
|
|
24
|
+
const size = safeStatSize(file);
|
|
25
|
+
if (size === 0)
|
|
26
|
+
return [];
|
|
27
|
+
const from = Math.max(0, size - maxBytes);
|
|
28
|
+
const text = readFileRange(file, from, size).toString("utf8");
|
|
29
|
+
if (!text.endsWith("\n"))
|
|
30
|
+
return null;
|
|
31
|
+
const lines = text.split("\n");
|
|
32
|
+
if (from > 0)
|
|
33
|
+
lines.shift();
|
|
34
|
+
return lines.filter(line => line.trim() !== "").reverse();
|
|
35
|
+
}
|
|
19
36
|
export function readFileRange(p, from, to) {
|
|
20
37
|
const len = Math.max(0, to - from);
|
|
21
38
|
if (len === 0)
|
|
@@ -103,6 +120,8 @@ export function pollGate(intervalMs, now = () => performance.now()) {
|
|
|
103
120
|
}
|
|
104
121
|
export const AGENT_EVENT_MAX_BYTES = 1024 * 1024;
|
|
105
122
|
export const AGENT_EVENT_TAIL_BYTES = 64 * 1024;
|
|
123
|
+
// turnの境界(開始・終わり)を、harnessの記録の末尾から探す時に読む大きさ。終わった席では境界は末尾の近くにある。
|
|
124
|
+
export const AGENT_TURN_BOUNDARY_TAIL_BYTES = 1024 * 1024;
|
|
106
125
|
export const CODEX_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
|
107
126
|
export const GROK_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
|
108
127
|
// session 名はファイルパス(logpath 等)と pipe-pane の /bin/sh 文字列へ流れる。英数 _ - のみ・64字に
|
package/dist/core.js
CHANGED
|
@@ -21,8 +21,8 @@ import { AitermError, telemetryOwnedFailure, ownTelemetryFailure } from "./error
|
|
|
21
21
|
import { isWin, SOCKDIR, tmuxCommand, sendPsmuxPayload, loadPtyBufferChunk, pasteBufferBaseArgs, tmuxNewSession, registeredSessionEnvironment, attachCommand, normalizePaneCommand, atomicShellMultiline, appendMarkSentinel, markShellCommand, settlePaneLog, paneCwdArgument, sessionEnvironmentLaunch, } from "./tmux-runtime.js";
|
|
22
22
|
import { sleep, currentUid, runtimeStateBase, safeStatSize, readFileRange, writeJson0600, createEmpty0600, shq, LAUNCH_ID_RE, AGENT_DONE_POLL_MS, AGENT_RATE_LIMIT_POLL_MS, pollGate, AGENT_EVENT_MAX_BYTES, assertSessionName, agentsDir, agentEventPath, agentMetadataPath, writeAgentMetadata, AGENT_EVENT_TAIL_BYTES, agentLabel, agentHarness, subagentInstruction, agentLineageFields, } from "./agent-shared.js";
|
|
23
23
|
import { catalogUnavailable } from "./model-catalog.js";
|
|
24
|
-
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";
|
|
25
|
-
import { codexAuthPlan, codexAuthStatus, codexAuthPane, bindCodexTranscriptSession, latestCodexCompletion, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexTuiBusy, codexHelperProcess, codexRateLimitModelSwitchDialog, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, codexModelChoices, } from "./harnesses/codex.js";
|
|
24
|
+
import { grokAuthPlan, grokAuthStatus, grokAuthPane, realGrokHome, resolveAndValidateGrokAuth, assertGrokModelAvailable, grokModelChoices, grokEventsTranscript, latestGrokCompletion, grokTurnSettled, observeGrokDone, buildGrokAgentCmd, grokLaunchNote, grokEnvTokens, grokTuiReady, grokTuiBusy, grokPaneObservation, grokRateLimitDialog, grokStartupAction, grokLaunchBlockingDialog, assertGrokSandboxNotRejected, GROK_COMPOSER_MARKER_RE, grokFooterHasConfiguration, grokTranscriptText, createGrokAgentMetadata, } from "./harnesses/grok.js";
|
|
25
|
+
import { codexAuthPlan, codexAuthStatus, codexAuthPane, bindCodexTranscriptSession, latestCodexCompletion, codexTurnSettled, observeCodexDone, buildCodexAgentCmd, codexLaunchNote, codexTuiReady, codexPaneObservation, codexTuiBusy, codexHelperProcess, codexRateLimitModelSwitchDialog, codexApprovalDialog, codexStartupAction, CODEX_COMPOSER_MARKER_RE, codexModelChoice, codexEffortChoice, codexMoreReasoningChoice, codexTranscriptText, createCodexAgentMetadata, codexModelChoices, } from "./harnesses/codex.js";
|
|
26
26
|
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";
|
|
27
27
|
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";
|
|
28
28
|
import { readInterimWords, recordInterimBoundary } from "./interim-words.js";
|
|
@@ -59,6 +59,11 @@ const LAUNCH_ECHO_TAIL_CHARS = 24;
|
|
|
59
59
|
const LAUNCH_ECHO_MIN_CHARS = 8;
|
|
60
60
|
const AGENT_TUI_READY_POLL_MS = 500;
|
|
61
61
|
const AGENT_TUI_READY_STABLE_SAMPLES = 11;
|
|
62
|
+
// 上の11回は、起動の途中に一瞬だけ出る入力欄を採らないための確かめ(ADR 0014)。席のharnessが自分の記録で
|
|
63
|
+
// 「この起動でturnを1つ終えていて、次のturnを始めていない」と示している時は起動の途中ではないので、短い間隔で
|
|
64
|
+
// 続けて確かめるだけで送る(ADR 0097)。記録で示せない時は、今までどおり11回。
|
|
65
|
+
const AGENT_TUI_SETTLED_STABLE_SAMPLES = 2;
|
|
66
|
+
const AGENT_TUI_SETTLED_POLL_MS = 100;
|
|
62
67
|
const AGENT_TUI_READY_LINES = 45;
|
|
63
68
|
const CLAUDE_APPROVAL_SCREEN_LINES = 80;
|
|
64
69
|
// submit座礁観測(dispatch後にcomposerへ送信textが残存していないかの有界チェック)
|
|
@@ -3172,6 +3177,8 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
3172
3177
|
const timeoutMs = opts.timeoutMs ?? AGENT_TUI_READY_TIMEOUT_MS;
|
|
3173
3178
|
const pollMs = opts.pollMs ?? AGENT_TUI_READY_POLL_MS;
|
|
3174
3179
|
const stableSamples = opts.stableSamples ?? agentTuiReadyStableSamplesTestOverride ?? AGENT_TUI_READY_STABLE_SAMPLES;
|
|
3180
|
+
const settledSamples = opts.settledSamples ?? AGENT_TUI_SETTLED_STABLE_SAMPLES;
|
|
3181
|
+
const settledPollMs = opts.settledPollMs ?? AGENT_TUI_SETTLED_POLL_MS;
|
|
3175
3182
|
const now = opts.now ?? (() => performance.now());
|
|
3176
3183
|
const start = now();
|
|
3177
3184
|
let deadline = start + timeoutMs;
|
|
@@ -3179,6 +3186,7 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
3179
3186
|
const firstDrawDeadline = timeoutMs >= AGENT_TUI_READY_TIMEOUT_MS ? start + AGENT_TUI_FIRST_DRAW_TIMEOUT_MS : deadline;
|
|
3180
3187
|
let samples = 0;
|
|
3181
3188
|
let readyStreak = 0;
|
|
3189
|
+
let settledStreak = 0;
|
|
3182
3190
|
let lastScreen = "";
|
|
3183
3191
|
for (;;) {
|
|
3184
3192
|
lastScreen = sample();
|
|
@@ -3189,9 +3197,20 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
3189
3197
|
readyStreak++;
|
|
3190
3198
|
if (readyStreak >= stableSamples)
|
|
3191
3199
|
return { ready: true, samples, lastScreen };
|
|
3200
|
+
if (opts.turnSettled?.()) {
|
|
3201
|
+
settledStreak++;
|
|
3202
|
+
if (settledStreak >= settledSamples)
|
|
3203
|
+
return { ready: true, samples, lastScreen };
|
|
3204
|
+
}
|
|
3205
|
+
else if (settledStreak > 0) {
|
|
3206
|
+
// 記録の上でturnが始まった。短い間隔で数えた分を、11回の確かめへ持ち越さない。
|
|
3207
|
+
settledStreak = 0;
|
|
3208
|
+
readyStreak = 1;
|
|
3209
|
+
}
|
|
3192
3210
|
}
|
|
3193
3211
|
else {
|
|
3194
3212
|
readyStreak = 0;
|
|
3213
|
+
settledStreak = 0;
|
|
3195
3214
|
if (isAgentTuiActionRequired(kind, lastScreen))
|
|
3196
3215
|
return { ready: false, samples, lastScreen };
|
|
3197
3216
|
}
|
|
@@ -3202,11 +3221,30 @@ async function waitAgentTuiReadyImpl(kind, sample, sleepFn, opts = {}) {
|
|
|
3202
3221
|
return { ready: false, samples, lastScreen };
|
|
3203
3222
|
deadline = firstDrawDeadline;
|
|
3204
3223
|
}
|
|
3205
|
-
await sleepFn(pollMs);
|
|
3224
|
+
await sleepFn(settledStreak > 0 ? settledPollMs : pollMs);
|
|
3206
3225
|
}
|
|
3207
3226
|
}
|
|
3208
|
-
|
|
3209
|
-
|
|
3227
|
+
/**
|
|
3228
|
+
* 席のharnessが、この起動でturnを1つ終えていて、次のturnを始めていない事を、自分の記録で示しているか。
|
|
3229
|
+
* 記録はこの起動に結び付いた物だけを読む(Codexはlaunch markerかsession ID、Grokは起動時に決めたsession、
|
|
3230
|
+
* Cursorは最初の文に入れたmarker)。起動直後の席は記録が無いのでfalse。読めない時もfalse。
|
|
3231
|
+
* Claudeはturnの印で差し込みか新しいturnかを決めていて、この確かめを通らない。
|
|
3232
|
+
*/
|
|
3233
|
+
function agentTurnSettled(meta) {
|
|
3234
|
+
try {
|
|
3235
|
+
if (meta.kind === "codex")
|
|
3236
|
+
return codexTurnSettled(meta);
|
|
3237
|
+
if (meta.kind === "grok")
|
|
3238
|
+
return meta.completion_route === "grok_transcript" && grokTurnSettled(meta);
|
|
3239
|
+
if (meta.kind === "cursor") {
|
|
3240
|
+
return meta.completion_route === "cursor_transcript" && latestCursorCompletion(meta, readTranscriptLines) !== null;
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
catch { /* 読めない時は、11回の確かめで決める */ }
|
|
3244
|
+
return false;
|
|
3245
|
+
}
|
|
3246
|
+
async function waitAgentTuiReady(name, meta, timeoutMs = AGENT_TUI_READY_TIMEOUT_MS, o = {}) {
|
|
3247
|
+
const result = await waitAgentTuiReadyImpl(meta.kind, () => captureScreen(name, AGENT_TUI_READY_LINES), sleep, { timeoutMs, launchLine: agentLaunchLines.get(name), ...(o.settledTurn ? { turnSettled: () => agentTurnSettled(meta) } : {}) });
|
|
3210
3248
|
if (result.ready)
|
|
3211
3249
|
agentLaunchLines.delete(name);
|
|
3212
3250
|
return result;
|
|
@@ -3403,7 +3441,10 @@ export async function __testWaitAgentTuiReady(kind, samples, opts = {}) {
|
|
|
3403
3441
|
const sleeps = [];
|
|
3404
3442
|
// virtualClockは待った分だけ進む時計。実時間を使わずに締切の扱いを確かめる。
|
|
3405
3443
|
let clock = 0;
|
|
3406
|
-
const { virtualClock, ...
|
|
3444
|
+
const { virtualClock, settled, ...rest } = opts;
|
|
3445
|
+
let asked = 0;
|
|
3446
|
+
const waitOpts = settled && settled.length > 0
|
|
3447
|
+
? { ...rest, turnSettled: () => settled[Math.min(asked++, settled.length - 1)] } : rest;
|
|
3407
3448
|
const result = await waitAgentTuiReadyImpl(kind, () => samples[Math.min(i++, samples.length - 1)], async (ms) => {
|
|
3408
3449
|
sleeps.push(ms);
|
|
3409
3450
|
clock += ms;
|
|
@@ -3714,9 +3755,10 @@ async function recoverCodexRateLimitModelSwitch(name, meta) {
|
|
|
3714
3755
|
return true;
|
|
3715
3756
|
}
|
|
3716
3757
|
/** ready待機中に現れたCodex利用上限modalも、同じ一時keep復旧へ戻す。 */
|
|
3717
|
-
async function waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, timeoutMs) {
|
|
3758
|
+
async function waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, timeoutMs, o = {}) {
|
|
3718
3759
|
let codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
|
|
3719
|
-
|
|
3760
|
+
// 上限のmodalを閉じた直後は、画面が落ち着くのを11回の確かめで待つ。
|
|
3761
|
+
let ready = await waitAgentTuiReady(name, meta, timeoutMs, codexRateLimitModelSwitch ? {} : o);
|
|
3720
3762
|
if (!ready.ready && !codexRateLimitModelSwitch) {
|
|
3721
3763
|
codexRateLimitModelSwitch = await recoverCodexRateLimitModelSwitch(name, meta);
|
|
3722
3764
|
if (codexRateLimitModelSwitch)
|
|
@@ -4189,7 +4231,8 @@ async function dispatchAgentTurnInLock(name, text, o) {
|
|
|
4189
4231
|
sendKey(name, limitDialog.dismissKey);
|
|
4190
4232
|
}
|
|
4191
4233
|
if (meta.kind !== "claude" || claudeColdStart) {
|
|
4192
|
-
|
|
4234
|
+
// 上限のpanelを閉じた直後(Grok)は、画面が落ち着くのを11回の確かめで待つ。
|
|
4235
|
+
const recovery = await waitAgentTuiReadyAfterCodexRateLimitRecovery(name, meta, o.ready_timeout ?? AGENT_TUI_READY_TIMEOUT_MS, { settledTurn: !limitDialog });
|
|
4193
4236
|
codexRateLimitModelSwitch = recovery.codexRateLimitModelSwitch;
|
|
4194
4237
|
const { ready } = recovery;
|
|
4195
4238
|
if (!ready.ready) {
|
package/dist/harnesses/codex.js
CHANGED
|
@@ -11,7 +11,7 @@ import { spawnAgentControlCommand } from "../agent-resolver.js";
|
|
|
11
11
|
import * as steer from "aiterm-steer-delivery";
|
|
12
12
|
import { AITERM_PROFILE } from "../steer-profile.js";
|
|
13
13
|
import { catalogInvalid, catalogUnavailable, checkedCatalog } from "../model-catalog.js";
|
|
14
|
-
import { shq, subagentInstruction, writeScopeLaunchNote, safeStatSize, readFileRange, sleep, agentMetadataPath, writeAgentMetadata, agentEventPath, createEmpty0600, agentLineageFields, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, AGENT_EVENT_TAIL_BYTES, CODEX_TRANSCRIPT_INCREMENT_MAX_BYTES, agentHarness, } from "../agent-shared.js";
|
|
14
|
+
import { shq, subagentInstruction, writeScopeLaunchNote, safeStatSize, readFileRange, sleep, agentMetadataPath, writeAgentMetadata, agentEventPath, createEmpty0600, agentLineageFields, AGENT_DONE_POLL_MS, AGENT_EVENT_MAX_BYTES, AGENT_EVENT_TAIL_BYTES, AGENT_TURN_BOUNDARY_TAIL_BYTES, readTailLinesNewestFirst, CODEX_TRANSCRIPT_INCREMENT_MAX_BYTES, agentHarness, } from "../agent-shared.js";
|
|
15
15
|
export function realCodexHome() {
|
|
16
16
|
return process.env.CODEX_HOME || path.join(process.env.HOME ?? os.homedir(), ".codex");
|
|
17
17
|
}
|
|
@@ -261,6 +261,47 @@ export function codexUsageLimit(record) {
|
|
|
261
261
|
const error = codexTurnError(record);
|
|
262
262
|
return error?.info === "usage_limit_exceeded" ? error.message : null;
|
|
263
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* この起動のroot rolloutで、最後のturnが終わっていて、次のturnが始まっていないか。
|
|
266
|
+
* Codexはturnの開始でtask_started、終わりでtask_complete、Escで止めた時はturn_aborted(reasonはinterrupted)を書く
|
|
267
|
+
* (0.160.1の実記録)。末尾から見て最初の境界が、task_completeか、止めた時のturn_abortedの時だけtrue。
|
|
268
|
+
* rolloutが無い、この起動に結び付かない、書いている途中、境界が読む範囲に無い時はfalse(分からない時は「終わった」と数えない)。
|
|
269
|
+
*/
|
|
270
|
+
export function codexTurnSettled(meta) {
|
|
271
|
+
let transcript;
|
|
272
|
+
try {
|
|
273
|
+
transcript = codexRootTranscript(meta);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (!transcript)
|
|
279
|
+
return false;
|
|
280
|
+
const lines = readTailLinesNewestFirst(transcript, AGENT_TURN_BOUNDARY_TAIL_BYTES);
|
|
281
|
+
if (!lines)
|
|
282
|
+
return false;
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
if (!line.includes("task_started") && !line.includes("task_complete") && !line.includes("turn_aborted"))
|
|
285
|
+
continue;
|
|
286
|
+
let record;
|
|
287
|
+
try {
|
|
288
|
+
record = JSON.parse(line);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (record?.type !== "event_msg")
|
|
294
|
+
continue;
|
|
295
|
+
if (record?.payload?.type === "task_started")
|
|
296
|
+
return false;
|
|
297
|
+
if (record?.payload?.type === "task_complete")
|
|
298
|
+
return typeof record.payload.turn_id === "string" && record.payload.turn_id !== "";
|
|
299
|
+
// 見たのはEscで止めた時の形だけ。他の理由(別のturnへの置き換え等)は、次のturnが続くかも知れないので数えない。
|
|
300
|
+
if (record?.payload?.type === "turn_aborted")
|
|
301
|
+
return record.payload.reason === "interrupted";
|
|
302
|
+
}
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
264
305
|
export function latestCodexCompletion(meta, readTranscriptLines) {
|
|
265
306
|
const transcript = codexRootTranscript(meta);
|
|
266
307
|
if (!transcript)
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -10,7 +10,7 @@ import { randomBytes, randomUUID } from "node:crypto";
|
|
|
10
10
|
import { AitermError } from "../errors.js";
|
|
11
11
|
import { runAgentProtocolCommand, spawnAgentControlCommand } from "../agent-resolver.js";
|
|
12
12
|
import { catalogInvalid, catalogUnavailable, checkedCatalog, findJsonLine, processSummary } from "../model-catalog.js";
|
|
13
|
-
import { shq, subagentInstruction, writeScopeLaunchNote, safeStatSize, readFileRange, sleep, agentMetadataPath, writeAgentMetadata, agentEventPath, createEmpty0600, agentLineageFields, AGENT_DONE_POLL_MS, AGENT_RATE_LIMIT_POLL_MS, pollGate, AGENT_EVENT_MAX_BYTES, GROK_TRANSCRIPT_INCREMENT_MAX_BYTES, agentHarness, } from "../agent-shared.js";
|
|
13
|
+
import { shq, subagentInstruction, writeScopeLaunchNote, safeStatSize, readFileRange, sleep, agentMetadataPath, writeAgentMetadata, agentEventPath, createEmpty0600, agentLineageFields, AGENT_DONE_POLL_MS, AGENT_RATE_LIMIT_POLL_MS, pollGate, AGENT_EVENT_MAX_BYTES, AGENT_TURN_BOUNDARY_TAIL_BYTES, readTailLinesNewestFirst, GROK_TRANSCRIPT_INCREMENT_MAX_BYTES, agentHarness, } from "../agent-shared.js";
|
|
14
14
|
const GROK_MODELS_MAX_BYTES = 1024 * 1024;
|
|
15
15
|
const GROK_MODELS_TIMEOUT_MS = 15_000;
|
|
16
16
|
// grok CLI はモデル未指定だと端末側 default に従うため、ツール契約として既定 slug を固定する。
|
|
@@ -191,6 +191,35 @@ export function latestGrokCompletion(meta, readTranscriptLines) {
|
|
|
191
191
|
}
|
|
192
192
|
return latest;
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* この起動のevents.jsonlで、最後のturnが終わっていて、次のturnが始まっていないか。
|
|
196
|
+
* 末尾から見て最初の境界が、完了と数えるturn_ended(grokCompletionEvent)の時だけtrue。差し込みの継ぎ目
|
|
197
|
+
* (cancelledかつsend_now)とturn_startedはfalse。記録が無い、書いている途中、境界が読む範囲に無い時もfalse。
|
|
198
|
+
*/
|
|
199
|
+
export function grokTurnSettled(meta) {
|
|
200
|
+
const transcript = grokEventsTranscript(meta);
|
|
201
|
+
if (!transcript || !fs.existsSync(transcript))
|
|
202
|
+
return false;
|
|
203
|
+
const lines = readTailLinesNewestFirst(transcript, AGENT_TURN_BOUNDARY_TAIL_BYTES);
|
|
204
|
+
if (!lines)
|
|
205
|
+
return false;
|
|
206
|
+
for (const line of lines) {
|
|
207
|
+
if (!line.includes("turn_started") && !line.includes("turn_ended"))
|
|
208
|
+
continue;
|
|
209
|
+
let record;
|
|
210
|
+
try {
|
|
211
|
+
record = JSON.parse(line);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (record?.type === "turn_started")
|
|
217
|
+
return false;
|
|
218
|
+
if (record?.type === "turn_ended")
|
|
219
|
+
return grokCompletionEvent(meta, record) !== null;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
194
223
|
export function grokInitializationComplete(meta) {
|
|
195
224
|
const transcript = grokEventsTranscript(meta);
|
|
196
225
|
if (!transcript || !fs.existsSync(transcript))
|
package/docs/DESIGN.md
CHANGED
|
@@ -104,6 +104,9 @@ Grokは待ち行列へ入れた後に「send now」を押す。旧turnは`cancel
|
|
|
104
104
|
新turnが作業を継ぐので、完了判定はこの継ぎ目を完了と数えない。Grokが待ち行列へ入れない時とCursorの入力欄に本文が残る時は`steered`を返さない。
|
|
105
105
|
Cursorのsubmitはadapterがextended keyboard protocolのEnterへ変換し、呼び出し側は通常のdispatchだけを使う。
|
|
106
106
|
起動直後のClaude sessionへの初回dispatchは、他harnessと同じくTUIの入力受付を確認してから貼付とEnterを送る。
|
|
107
|
+
Codex・Grok・Cursorの席は、新しいturnを送るたびに入力受付を確かめる。起動の途中の入力欄を採らないよう、500msおきに11回続けて
|
|
108
|
+
入力待ちを読む(ADR 0014)。席のharnessが自分の記録で「この起動でturnを1つ終えていて、次のturnを始めていない」と示している時は、
|
|
109
|
+
100msおきに2回で通す(ADR 0097)。記録の上でturnが動いている間は、画面が入力待ちに見えても11回の確かめを通る。
|
|
107
110
|
|
|
108
111
|
### 親配送の共通モジュール化(設計)
|
|
109
112
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.4",
|
|
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": [
|