aiterm-mcp 0.59.0 → 0.59.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 +12 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/dist/agent-shared.js +36 -0
- package/dist/harnesses/codex.js +31 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.59.1] - 2026-10-13
|
|
11
|
+
|
|
12
|
+
### 修正
|
|
13
|
+
|
|
14
|
+
- **長く続いているCodexの番へ、回答を流している間に届いた文が、30秒待って断られていた。**(ADR 0101 の直し)
|
|
15
|
+
回答を流している間のCodexは画面に動作中の行を出さないので、番が動いているかは会話の記録で確かめている。その開始の印を記録の末尾1MiBの中でしか
|
|
16
|
+
探しておらず、番が1MiBより多く書いた後は「動いていない」と読んで、新しいturnの道で入力待ちを30秒待ち、
|
|
17
|
+
`…TUI が入力受付状態になりません。文字列は送信していません。`で断っていた(2026-10-13、20分を過ぎた番で4通)。
|
|
18
|
+
開始の印を、境界に当たるまで記録をさかのぼって探す。動作中の行が出ている間に届いた文と、止まっている席への文は、今までと同じ。
|
|
19
|
+
|
|
10
20
|
## [0.59.0] - 2026-10-13
|
|
11
21
|
|
|
12
22
|
### 追加
|
|
@@ -2619,7 +2629,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
2619
2629
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
2620
2630
|
provenance.
|
|
2621
2631
|
|
|
2622
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.59.
|
|
2632
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.59.1...HEAD
|
|
2633
|
+
[0.59.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.59.0...v0.59.1
|
|
2623
2634
|
[0.59.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.58.4...v0.59.0
|
|
2624
2635
|
[0.58.4]: https://github.com/kitepon/aiterm-mcp/compare/v0.58.3...v0.58.4
|
|
2625
2636
|
[0.58.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.58.2...v0.58.3
|
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.59.
|
|
212
|
+
**状態:** 開発継続中 · 現行公開版 **v0.59.1** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
213
213
|
|
|
214
214
|
### 更新と巻き戻し
|
|
215
215
|
|
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.59.
|
|
230
|
+
**Status:** actively maintained · current public release **v0.59.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).
|
|
231
231
|
|
|
232
232
|
### Update and rollback
|
|
233
233
|
|
package/dist/agent-shared.js
CHANGED
|
@@ -33,6 +33,41 @@ export function readTailLinesNewestFirst(file, maxBytes) {
|
|
|
33
33
|
lines.shift();
|
|
34
34
|
return lines.filter(line => line.trim() !== "").reverse();
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* 記録を末尾から先頭へさかのぼり、行を新しい順に`visit`へ渡す。`visit`がundefined以外を返したら、そこで止めてその値を返す。
|
|
38
|
+
* 先頭まで読んで何も返らなければundefined。書いている途中の行(改行で終わっていない末尾)がある時と、読めなかった時はnull。
|
|
39
|
+
* 1回に読むのは`chunkBytes`ずつ。行が読む範囲に収まらない時は、収まるまで範囲を広げる(行の途中では切らない)。
|
|
40
|
+
*/
|
|
41
|
+
export function scanLinesNewestFirst(file, visit, chunkBytes) {
|
|
42
|
+
let end = safeStatSize(file);
|
|
43
|
+
for (let tail = true; end > 0; tail = false) {
|
|
44
|
+
let span = chunkBytes;
|
|
45
|
+
let start = Math.max(0, end - span);
|
|
46
|
+
let chunk = readFileRange(file, start, end);
|
|
47
|
+
// 範囲の先頭で切れた行は、次の回で読む。改行が範囲の最後の1つだけなら、その行は範囲に収まっていない。
|
|
48
|
+
let cut = start === 0 ? 0 : chunk.indexOf(0x0a) + 1;
|
|
49
|
+
while (chunk.length === end - start && start > 0 && (cut === 0 || cut === chunk.length)) {
|
|
50
|
+
span *= 2;
|
|
51
|
+
start = Math.max(0, end - span);
|
|
52
|
+
chunk = readFileRange(file, start, end);
|
|
53
|
+
cut = start === 0 ? 0 : chunk.indexOf(0x0a) + 1;
|
|
54
|
+
}
|
|
55
|
+
if (chunk.length !== end - start)
|
|
56
|
+
return null;
|
|
57
|
+
if (tail && chunk[chunk.length - 1] !== 0x0a)
|
|
58
|
+
return null;
|
|
59
|
+
const lines = chunk.subarray(cut).toString("utf8").split("\n");
|
|
60
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
61
|
+
if (lines[index].trim() === "")
|
|
62
|
+
continue;
|
|
63
|
+
const found = visit(lines[index]);
|
|
64
|
+
if (found !== undefined)
|
|
65
|
+
return found;
|
|
66
|
+
}
|
|
67
|
+
end = start + cut;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
36
71
|
export function readFileRange(p, from, to) {
|
|
37
72
|
const len = Math.max(0, to - from);
|
|
38
73
|
if (len === 0)
|
|
@@ -121,6 +156,7 @@ export function pollGate(intervalMs, now = () => performance.now()) {
|
|
|
121
156
|
export const AGENT_EVENT_MAX_BYTES = 1024 * 1024;
|
|
122
157
|
export const AGENT_EVENT_TAIL_BYTES = 64 * 1024;
|
|
123
158
|
// turnの境界(開始・終わり)を、harnessの記録の末尾から探す時に読む大きさ。終わった席では境界は末尾の近くにある。
|
|
159
|
+
// 動いている番の開始は、番が長いとこれより前になる。開始を探す所は、この大きさずつさかのぼる(scanLinesNewestFirst)。
|
|
124
160
|
export const AGENT_TURN_BOUNDARY_TAIL_BYTES = 1024 * 1024;
|
|
125
161
|
export const CODEX_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
|
126
162
|
export const GROK_TRANSCRIPT_INCREMENT_MAX_BYTES = 16 * 1024 * 1024;
|
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, AGENT_TURN_BOUNDARY_TAIL_BYTES, readTailLinesNewestFirst, 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, scanLinesNewestFirst, 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
|
}
|
|
@@ -383,12 +383,31 @@ export function codexUsageLimit(record) {
|
|
|
383
383
|
}
|
|
384
384
|
// 完了待ちが、会話の切り替えを確かめる間隔。送った文はすぐ記録へ書かれるので、普通のturnはここへ来ない。
|
|
385
385
|
const CODEX_SWITCH_CHECK_MS = 3_000;
|
|
386
|
+
/** 1行がturnの境界ならその境界、境界でなければundefined、境界の語を含むのに読めない行ならnull。 */
|
|
387
|
+
function codexTurnBoundaryLine(line) {
|
|
388
|
+
if (!line.includes("task_started") && !line.includes("task_complete") && !line.includes("turn_aborted"))
|
|
389
|
+
return undefined;
|
|
390
|
+
let record;
|
|
391
|
+
try {
|
|
392
|
+
record = JSON.parse(line);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
if (record?.type !== "event_msg")
|
|
398
|
+
return undefined;
|
|
399
|
+
const type = record?.payload?.type;
|
|
400
|
+
return type === "task_started" || type === "task_complete" || type === "turn_aborted"
|
|
401
|
+
? { type, turn_id: record.payload.turn_id, reason: record.payload.reason } : undefined;
|
|
402
|
+
}
|
|
386
403
|
/**
|
|
387
404
|
* この起動のroot rolloutの、末尾から見て最初のturnの境界。
|
|
388
405
|
* Codexはturnの開始でtask_started、終わりでtask_complete、止めた時はturn_aborted(reasonはinterrupted)を書く(0.160.1の実記録)。
|
|
389
406
|
* rolloutが無い、この起動に結び付かない、書いている途中、境界が読む範囲に無い時はnull。
|
|
407
|
+
* 読む範囲は、`reach`が"tail"なら末尾の1MiB(終わった番の境界は末尾の近くにある。見回りで何度も呼ぶ所はこちら)、
|
|
408
|
+
* "whole"なら境界に当たるまで先頭へさかのぼる(動いている番の開始は、番が長いと末尾の1MiBより前にある)。
|
|
390
409
|
*/
|
|
391
|
-
function codexLatestTurnBoundary(meta) {
|
|
410
|
+
function codexLatestTurnBoundary(meta, reach) {
|
|
392
411
|
let transcript;
|
|
393
412
|
try {
|
|
394
413
|
transcript = codexRootTranscript(meta);
|
|
@@ -398,24 +417,15 @@ function codexLatestTurnBoundary(meta) {
|
|
|
398
417
|
}
|
|
399
418
|
if (!transcript)
|
|
400
419
|
return null;
|
|
420
|
+
if (reach === "whole")
|
|
421
|
+
return scanLinesNewestFirst(transcript, codexTurnBoundaryLine, AGENT_TURN_BOUNDARY_TAIL_BYTES) ?? null;
|
|
401
422
|
const lines = readTailLinesNewestFirst(transcript, AGENT_TURN_BOUNDARY_TAIL_BYTES);
|
|
402
423
|
if (!lines)
|
|
403
424
|
return null;
|
|
404
425
|
for (const line of lines) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
try {
|
|
409
|
-
record = JSON.parse(line);
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
if (record?.type !== "event_msg")
|
|
415
|
-
continue;
|
|
416
|
-
const type = record?.payload?.type;
|
|
417
|
-
if (type === "task_started" || type === "task_complete" || type === "turn_aborted")
|
|
418
|
-
return { type, turn_id: record.payload.turn_id, reason: record.payload.reason };
|
|
426
|
+
const boundary = codexTurnBoundaryLine(line);
|
|
427
|
+
if (boundary !== undefined)
|
|
428
|
+
return boundary;
|
|
419
429
|
}
|
|
420
430
|
return null;
|
|
421
431
|
}
|
|
@@ -425,7 +435,7 @@ function codexLatestTurnBoundary(meta) {
|
|
|
425
435
|
* rolloutが無い、この起動に結び付かない、書いている途中、境界が読む範囲に無い時はfalse(分からない時は「終わった」と数えない)。
|
|
426
436
|
*/
|
|
427
437
|
export function codexTurnSettled(meta) {
|
|
428
|
-
const boundary = codexLatestTurnBoundary(meta);
|
|
438
|
+
const boundary = codexLatestTurnBoundary(meta, "tail");
|
|
429
439
|
if (boundary?.type === "task_complete")
|
|
430
440
|
return typeof boundary.turn_id === "string" && boundary.turn_id !== "";
|
|
431
441
|
// 見たのはEscで止めた時の形だけ。他の理由(別のturnへの置き換え等)は、次のturnが続くかも知れないので数えない。
|
|
@@ -433,11 +443,13 @@ export function codexTurnSettled(meta) {
|
|
|
433
443
|
}
|
|
434
444
|
/**
|
|
435
445
|
* この起動のroot rolloutの上で動いているturnのID。末尾から見て最初の境界がtask_startedの時だけ返す。
|
|
436
|
-
* rollout
|
|
446
|
+
* rolloutが無い、この起動に結び付かない、書いている途中、境界が1つも無い時はnull(分からない時は「動いている」と数えない)。
|
|
447
|
+
* 開始の印は、境界に当たるまで記録をさかのぼって探す。末尾の1MiBだけを読むと、長い番(開始から1MiBより多く書いた番)を
|
|
448
|
+
* 動いていると読めず、回答を流している間に届いた文が、新しいturnの道で入力待ちを30秒待って断られる(実被弾 2026-10-13)。
|
|
437
449
|
* Codexは、processが生きている間、turnの終わりを必ず書く。書かずに終わるのは落ちた時なので、呼ぶ側はprocessの生死も見る。
|
|
438
450
|
*/
|
|
439
451
|
export function codexRunningTurn(meta) {
|
|
440
|
-
const boundary = codexLatestTurnBoundary(meta);
|
|
452
|
+
const boundary = codexLatestTurnBoundary(meta, "whole");
|
|
441
453
|
return boundary?.type === "task_started" && typeof boundary.turn_id === "string" && boundary.turn_id !== "" ? boundary.turn_id : null;
|
|
442
454
|
}
|
|
443
455
|
export function latestCodexCompletion(meta, readTranscriptLines) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.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": [
|