aiterm-mcp 0.53.2 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/README.ja.md +2 -2
- package/README.md +2 -2
- package/dist/core.js +14 -5
- package/dist/harnesses/codex.js +11 -3
- package/dist/harnesses/cursor.js +22 -6
- package/dist/harnesses/grok.js +6 -3
- package/dist/index.js +9 -0
- package/dist/parent-delivery.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.54.0] - 2026-10-06
|
|
11
|
+
|
|
12
|
+
### 変更
|
|
13
|
+
|
|
14
|
+
- `pty_read(agent_transcript:true)`は、回答が空で終わったturnを誤りにせず、`text`が空文字列、`answer_empty`が`true`の
|
|
15
|
+
結果で返す(ADR 0092)。`aiterm.pty-read-result.v1`へ`answer_empty`を足した(回答がある時は`false`、terminalの読み取りは`null`)。
|
|
16
|
+
今までは、回答を空で終えただけのturnと、記録の形が変わって回答を読めなくなった時が、同じ
|
|
17
|
+
「最終 assistant メッセージを特定できませんでした」の誤りになり、呼び出し側が見分けられなかった。
|
|
18
|
+
Codex(0.160.1)は、道具で用を済ませて回答を空で終えたturnを、`text`が空の`final_answer`として記録に残す。
|
|
19
|
+
誤りにするのは、回答の場所を記録から見つけられない時だけにした(文面は同じ)。
|
|
20
|
+
|
|
21
|
+
### 修正
|
|
22
|
+
|
|
23
|
+
- 親への自動配送で、Codexの子が回答を空で終えると、親へ`AGENT_RESULT_UNAVAILABLE: transcript がまだありません。
|
|
24
|
+
ターン完了後に再取得してください`が`outcome=error`で届いていた。turnは終わっているので、
|
|
25
|
+
「子のターンは完了しましたが、回答の本文は空でした。」を`outcome=done`で届ける。Claude Code・Grok・Cursorの子も同じ。
|
|
26
|
+
- Codexの回答の読み取りは、turn ID付きの本文が空の時に、turn IDの無い旧形式の本文(前のturnの物かもしれない)へ
|
|
27
|
+
落とす作りだった。turn ID付きの本文が記録にあれば、空でもそれを回答とする。今のCodexは旧形式を書かず、
|
|
28
|
+
実記録で前のturnの回答が返った例は確認していない。
|
|
29
|
+
|
|
10
30
|
## [0.53.2] - 2026-10-05
|
|
11
31
|
|
|
12
32
|
### 修正
|
|
@@ -2215,7 +2235,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
2215
2235
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
2216
2236
|
provenance.
|
|
2217
2237
|
|
|
2218
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.
|
|
2238
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.54.0...HEAD
|
|
2239
|
+
[0.54.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.53.2...v0.54.0
|
|
2219
2240
|
[0.53.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.53.1...v0.53.2
|
|
2220
2241
|
[0.53.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.53.0...v0.53.1
|
|
2221
2242
|
[0.53.0]: https://github.com/kitepon/aiterm-mcp/compare/v0.52.3...v0.53.0
|
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.
|
|
212
|
+
**状態:** 開発継続中 · 現行公開版 **v0.54.0** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
213
213
|
|
|
214
214
|
### 更新と巻き戻し
|
|
215
215
|
|
|
@@ -648,7 +648,7 @@ handoff contextを前置きできる。この任意経路は`throughline >= 0.9.
|
|
|
648
648
|
`THROUGHLINE_BIN`、次に`PATH`から解決し、不在・不正・空のexportはPTY作成前に明示失敗する。
|
|
649
649
|
任意の`throughline_supplement_file`はThroughline 0.10.8以降と`throughline_source_session`を必要とし、Aitermは内容を解釈せずThroughlineへ渡す。
|
|
650
650
|
|
|
651
|
-
エージェントの回答が画面tailより長ければ、`pty_read({ agent_transcript:true })`で再promptなしに全文回収する。既存の人間向けcontentは診断suffixを維持し、機械呼出し側は`aiterm.pty-read-result.v1`の`structuredContent.text`から回答本文だけを取得する。Claudeはlaunch相関Stop hook、Codexは通常rollout、Grokは最後の実user行以後の最後の空でないassistantメッセージだけ、Cursorはlaunch IDでbindした通常agent transcriptから同じturn
|
|
651
|
+
エージェントの回答が画面tailより長ければ、`pty_read({ agent_transcript:true })`で再promptなしに全文回収する。既存の人間向けcontentは診断suffixを維持し、機械呼出し側は`aiterm.pty-read-result.v1`の`structuredContent.text`から回答本文だけを取得する。Claudeはlaunch相関Stop hook、Codexは通常rollout、Grokは最後の実user行以後の最後の空でないassistantメッセージだけ、Cursorはlaunch IDでbindした通常agent transcriptから同じturnを回収する。回答を空で終えたturnは誤りにせず、`text`が空文字列、`answer_empty`が`true`で返すので、回答を読めなかった時と見分けられる。回答の場所を記録から見つけられない時だけ誤りにする。
|
|
652
652
|
|
|
653
653
|
### 完了検出(5 層)
|
|
654
654
|
|
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.
|
|
226
|
+
**Status:** actively maintained · current public release **v0.54.0** · 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
|
|
|
@@ -689,7 +689,7 @@ unchanged. Optional `throughline_supplement_file` is passed unchanged to Through
|
|
|
689
689
|
`throughline_source_session` and Throughline 0.10.8 or later; Aiterm does not read or classify the supplement. Throughline is resolved through `THROUGHLINE_BIN` and then `PATH`; a missing or invalid
|
|
690
690
|
export fails before the PTY exists instead of silently launching clean.
|
|
691
691
|
|
|
692
|
-
When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lines), callers recover it in full with `pty_read({ agent_transcript: true })`. It returns the most recently completed turn's final assistant message in plain text with no re-prompting. The existing human-readable content keeps its diagnostic suffix; machine callers read the answer alone from `structuredContent.text` in `aiterm.pty-read-result.v1`. Claude reads the bounded owner-only result captured by the launch-correlated Stop hook and verifies its digest/byte count; it never reads Claude's private transcript. Durable machine callers should use `claude_turn`: `issue` sends once, `recover` never sends, `pending` is distinct from unsafe or malformed state, and only `completed` carries the exact verified `raw_output`. Codex uses the normal rollout transcript's `task_complete.turn_id`; Grok returns the last non-empty assistant message after the last real user row, excluding tool-use preambles; Cursor uses the normal agent transcript bound to the launch ID and current turn. Missing or ambiguous attribution remains an explicit error.
|
|
692
|
+
When an agent's answer is longer than the on-screen tail (pane height ≈ 24 lines), callers recover it in full with `pty_read({ agent_transcript: true })`. It returns the most recently completed turn's final assistant message in plain text with no re-prompting. The existing human-readable content keeps its diagnostic suffix; machine callers read the answer alone from `structuredContent.text` in `aiterm.pty-read-result.v1`. Claude reads the bounded owner-only result captured by the launch-correlated Stop hook and verifies its digest/byte count; it never reads Claude's private transcript. Durable machine callers should use `claude_turn`: `issue` sends once, `recover` never sends, `pending` is distinct from unsafe or malformed state, and only `completed` carries the exact verified `raw_output`. Codex uses the normal rollout transcript's `task_complete.turn_id`; Grok returns the last non-empty assistant message after the last real user row, excluding tool-use preambles; Cursor uses the normal agent transcript bound to the launch ID and current turn. A turn that completed with an empty answer is not an error: `text` is the empty string and `answer_empty` is `true`, so callers can tell it apart from an answer that could not be read. Missing or ambiguous attribution, including an answer that cannot be located in the record, remains an explicit error.
|
|
693
693
|
|
|
694
694
|
### Completion detection (5 layers)
|
|
695
695
|
|
package/dist/core.js
CHANGED
|
@@ -2681,6 +2681,8 @@ function agentCompletionCursor(meta) {
|
|
|
2681
2681
|
function transcriptUnavailable() {
|
|
2682
2682
|
throw new AitermError(`transcript がまだありません。ターン完了後に再取得してください。${agentWaitGuide()}`, 2);
|
|
2683
2683
|
}
|
|
2684
|
+
/** 完了したturnの回答が空だった時に、人とAIが読む表示へ出す文。機械はstructuredContentのanswer_emptyを見る。 */
|
|
2685
|
+
const AGENT_ANSWER_EMPTY_NOTICE = "このターンは完了しましたが、回答の本文は空でした。";
|
|
2684
2686
|
function transcriptNotFound(vendor) {
|
|
2685
2687
|
throw new AitermError(`最終 assistant メッセージを特定できませんでした(harness=${agentHarness(vendor)}, vendor=${vendor})。screen で確認してください。`, 2);
|
|
2686
2688
|
}
|
|
@@ -2763,7 +2765,8 @@ export async function readAgentTranscriptResult(name, o = {}) {
|
|
|
2763
2765
|
throw new AitermError(`operation ${operationId} はまだ完了していません。同じoperation_idで後から再取得してください。${agentWaitGuide(name)}`, 2);
|
|
2764
2766
|
}
|
|
2765
2767
|
const turnId = o.completion?.turn_id ?? done?.turn_id ?? null;
|
|
2766
|
-
|
|
2768
|
+
// 各harnessの読み取りは、回答の場所を見つけたら文字列(空でもよい)、見つからなければnullを返す。
|
|
2769
|
+
let text;
|
|
2767
2770
|
if (meta.kind === "claude") {
|
|
2768
2771
|
if (!done)
|
|
2769
2772
|
transcriptUnavailable();
|
|
@@ -2780,12 +2783,16 @@ export async function readAgentTranscriptResult(name, o = {}) {
|
|
|
2780
2783
|
transcriptUnavailable();
|
|
2781
2784
|
text = grokTranscriptText(meta, readTranscriptLines, transcriptUnavailable, o.completion?.turn_id);
|
|
2782
2785
|
}
|
|
2783
|
-
|
|
2786
|
+
// 回答の場所が見つからない時だけ誤りにする。場所があって中身が空白だけのturnは「回答が空」として返す。
|
|
2787
|
+
if (text === null)
|
|
2784
2788
|
transcriptNotFound(meta.kind);
|
|
2785
|
-
|
|
2789
|
+
const answerEmpty = !text.trim();
|
|
2790
|
+
if (answerEmpty)
|
|
2791
|
+
text = "";
|
|
2792
|
+
else if (o.lines != null)
|
|
2786
2793
|
text = text.split("\n").slice(-o.lines).join("\n");
|
|
2787
2794
|
const rawChars = text.length;
|
|
2788
|
-
const [body, outputMeta] = o.raw ? [text, ""] : reduceOutput(text, name, true);
|
|
2795
|
+
const [body, outputMeta] = answerEmpty || o.raw ? [text, ""] : reduceOutput(text, name, true);
|
|
2789
2796
|
const transcriptMeta = [
|
|
2790
2797
|
"agent_transcript",
|
|
2791
2798
|
`vendor=${meta.kind}`,
|
|
@@ -2793,14 +2800,16 @@ export async function readAgentTranscriptResult(name, o = {}) {
|
|
|
2793
2800
|
`harness=${agentHarness(meta.kind)}`,
|
|
2794
2801
|
done?.operation_id ? `operation_id=${done.operation_id}` : null,
|
|
2795
2802
|
`raw_chars=${rawChars}`,
|
|
2803
|
+
answerEmpty ? "answer_empty=true" : null,
|
|
2796
2804
|
].filter(Boolean).join(" ");
|
|
2797
2805
|
return {
|
|
2798
2806
|
text: body,
|
|
2799
|
-
display: `${body}\n${outputMeta} [${transcriptMeta}]`,
|
|
2807
|
+
display: `${answerEmpty ? AGENT_ANSWER_EMPTY_NOTICE : body}\n${outputMeta} [${transcriptMeta}]`,
|
|
2800
2808
|
vendor: meta.kind,
|
|
2801
2809
|
turn_id: turnId,
|
|
2802
2810
|
harness: agentHarness(meta.kind),
|
|
2803
2811
|
raw_chars: rawChars,
|
|
2812
|
+
answer_empty: answerEmpty,
|
|
2804
2813
|
};
|
|
2805
2814
|
}
|
|
2806
2815
|
/** 人間向け互換表示を維持する。機械利用は readAgentTranscriptResult の text を使う。 */
|
package/dist/harnesses/codex.js
CHANGED
|
@@ -618,6 +618,9 @@ export function codexMoreReasoningChoice(screen) {
|
|
|
618
618
|
return null;
|
|
619
619
|
}
|
|
620
620
|
// 回収対象turnの最終assistantメッセージをroot rollout transcriptから抽出する。
|
|
621
|
+
// 回答の場所が記録にあれば、中身が空でも文字列を返す。場所が見つからない時(記録の形が変わった時を含む)はnull。
|
|
622
|
+
// Codexは回答を空で終えたturnも、textが空のfinal_answerをturn ID付きで残す(0.160.1の実記録 2026-10-06。
|
|
623
|
+
// task_completeのlast_agent_messageはnull)。
|
|
621
624
|
export function codexTranscriptText(meta, turnId, readTranscriptLines, transcriptUnavailable, exactCompletion = false) {
|
|
622
625
|
if (!meta.codex_home || !meta.vendor_session_id)
|
|
623
626
|
transcriptUnavailable();
|
|
@@ -642,7 +645,7 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
|
|
|
642
645
|
if (typeof payload.last_agent_message === "string")
|
|
643
646
|
return payload.last_agent_message;
|
|
644
647
|
const text = matching.text();
|
|
645
|
-
if (text)
|
|
648
|
+
if (text !== null)
|
|
646
649
|
return text;
|
|
647
650
|
transcriptUnavailable();
|
|
648
651
|
}
|
|
@@ -650,7 +653,7 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
|
|
|
650
653
|
transcriptUnavailable();
|
|
651
654
|
}
|
|
652
655
|
const matching = codexTurnAnswer();
|
|
653
|
-
let finalAnswer =
|
|
656
|
+
let finalAnswer = null;
|
|
654
657
|
for (const line of lines) {
|
|
655
658
|
if (!line.trim())
|
|
656
659
|
continue;
|
|
@@ -670,7 +673,9 @@ export function codexTranscriptText(meta, turnId, readTranscriptLines, transcrip
|
|
|
670
673
|
finalAnswer = payload.message;
|
|
671
674
|
}
|
|
672
675
|
}
|
|
673
|
-
|
|
676
|
+
// turnの本文が記録にあれば、空でもそれが回答。agent_message(turn IDの無い旧形式)は別turnの物かもしれないので、
|
|
677
|
+
// turnの本文が見つからず、かつ空でない時だけ使う。
|
|
678
|
+
return matching.text() ?? (finalAnswer?.trim() ? finalAnswer : null);
|
|
674
679
|
}
|
|
675
680
|
// turnのassistant本文を集める。Codexは作業途中の報告をphase=commentary、回答をphase=final_answerで残すので、
|
|
676
681
|
// final_answerがあるturnは途中の報告を回答に含めない。phaseの無い旧形式はturnの本文を全部つなぐ。
|
|
@@ -691,7 +696,10 @@ function codexTurnAnswer() {
|
|
|
691
696
|
finals.push(item.text);
|
|
692
697
|
}
|
|
693
698
|
},
|
|
699
|
+
// turnのassistant本文が1つも無ければnull(回答の場所が見つからない)。
|
|
694
700
|
text() {
|
|
701
|
+
if (all.length === 0)
|
|
702
|
+
return null;
|
|
695
703
|
return (finals.length > 0 ? finals : all).join("\n");
|
|
696
704
|
},
|
|
697
705
|
};
|
package/dist/harnesses/cursor.js
CHANGED
|
@@ -245,6 +245,10 @@ export function cursorTranscriptText(meta, readTranscriptLines, transcriptUnavai
|
|
|
245
245
|
transcriptUnavailable();
|
|
246
246
|
// tool_useで区切った本文の塊を持つ。道具を呼ぶ前の文は作業途中の報告なので、回答は最後の塊だけにする。
|
|
247
247
|
let current = [[]];
|
|
248
|
+
// 回答の場所が分かるturnか。assistant行があり、そのpartがtextとtool_useだけの時に限る(実記録にあるのはこの2種類)。
|
|
249
|
+
// 知らないpartがあるturnは、本文がそこへ移ったのかもしれないので「回答が空」とは言わない。
|
|
250
|
+
let assistantRows = 0;
|
|
251
|
+
let unknownParts = false;
|
|
248
252
|
let completed = null;
|
|
249
253
|
for (const line of readTranscriptLines(transcript)) {
|
|
250
254
|
if (!line.trim())
|
|
@@ -256,22 +260,34 @@ export function cursorTranscriptText(meta, readTranscriptLines, transcriptUnavai
|
|
|
256
260
|
catch {
|
|
257
261
|
continue;
|
|
258
262
|
}
|
|
259
|
-
if (record?.role === "user")
|
|
263
|
+
if (record?.role === "user") {
|
|
260
264
|
current = [[]];
|
|
265
|
+
assistantRows = 0;
|
|
266
|
+
unknownParts = false;
|
|
267
|
+
}
|
|
261
268
|
if (record?.role === "assistant" && Array.isArray(record?.message?.content)) {
|
|
269
|
+
assistantRows += 1;
|
|
262
270
|
for (const part of record.message.content) {
|
|
263
271
|
if (part?.type === "text" && typeof part?.text === "string")
|
|
264
272
|
current.at(-1).push(part.text);
|
|
265
|
-
else if (part?.type === "tool_use"
|
|
266
|
-
current.
|
|
273
|
+
else if (part?.type === "tool_use") {
|
|
274
|
+
if (current.at(-1).length > 0)
|
|
275
|
+
current.push([]);
|
|
276
|
+
}
|
|
277
|
+
else
|
|
278
|
+
unknownParts = true;
|
|
267
279
|
}
|
|
268
280
|
}
|
|
269
|
-
if (record?.type === "turn_ended")
|
|
270
|
-
completed = current.map((chunk) => [...chunk]);
|
|
281
|
+
if (record?.type === "turn_ended") {
|
|
282
|
+
completed = { chunks: current.map((chunk) => [...chunk]), located: assistantRows > 0 && !unknownParts };
|
|
283
|
+
}
|
|
271
284
|
}
|
|
272
285
|
if (completed === null)
|
|
273
286
|
transcriptUnavailable();
|
|
274
|
-
|
|
287
|
+
const text = (completed.chunks.filter((chunk) => chunk.length > 0).at(-1) ?? []).join("\n");
|
|
288
|
+
if (text.trim())
|
|
289
|
+
return text;
|
|
290
|
+
return completed.located ? "" : null;
|
|
275
291
|
}
|
|
276
292
|
export function createCursorAgentMetadata(name, cwd, initialPrompt, writeScope, lineage) {
|
|
277
293
|
const launchId = randomBytes(16).toString("hex");
|
package/dist/harnesses/grok.js
CHANGED
|
@@ -500,9 +500,12 @@ export function grokTranscriptText(meta, readTranscriptLines, transcriptUnavaila
|
|
|
500
500
|
const replies = records
|
|
501
501
|
.slice(lastUser + 1, end)
|
|
502
502
|
.filter((record) => record?.type === "assistant" && typeof record?.content === "string")
|
|
503
|
-
.map((record) => record.content.trim())
|
|
504
|
-
|
|
505
|
-
|
|
503
|
+
.map((record) => record.content.trim());
|
|
504
|
+
// contentが文字列のassistant行が1つも無ければnull(回答の場所が見つからない)。
|
|
505
|
+
// 道具を呼ぶだけの行はcontentが空文字列で残るので、そういう行だけのturnは「回答が空」になる。
|
|
506
|
+
if (replies.length === 0)
|
|
507
|
+
return null;
|
|
508
|
+
return replies.filter(Boolean).at(-1) ?? "";
|
|
506
509
|
}
|
|
507
510
|
export function createGrokAgentMetadata(kind, name, cwd, initialPrompt, authPath, writeScope, lineageContext) {
|
|
508
511
|
const launchId = randomBytes(16).toString("hex");
|
package/dist/index.js
CHANGED
|
@@ -413,6 +413,7 @@ registerRemoteAwareTool("pty_read", {
|
|
|
413
413
|
description: "セッションの出力をトークン削減して読む(既定は前回読取位置からの増分)。" +
|
|
414
414
|
"削減: 制御文字除去 / 反復圧縮 / head+tail 折りたたみ+復元ヒント+メタ併記。" +
|
|
415
415
|
"agent_transcript:true は agent session の直近完了ターンの最終 assistant メッセージを公開されたharness記録から平文で返す。" +
|
|
416
|
+
"回答が空で終わったターンは誤りにせず、空のtextとanswer_empty:trueを返す。" +
|
|
416
417
|
"長い回答が screen tail で切れた時の回収用。",
|
|
417
418
|
inputSchema: {
|
|
418
419
|
session_id: z.string(),
|
|
@@ -454,6 +455,12 @@ registerRemoteAwareTool("pty_read", {
|
|
|
454
455
|
turn_id: z.string().nullable(),
|
|
455
456
|
harness: z.enum(["claude-code", "codex-cli", "grok-cli", "cursor-cli"]).nullable(),
|
|
456
457
|
raw_chars: z.number().int().nonnegative().nullable(),
|
|
458
|
+
// remote付きは現地の結果をそのまま返す。この項目が無い旧版(0.53.2以前)の結果も通すのでoptional。
|
|
459
|
+
answer_empty: z
|
|
460
|
+
.boolean()
|
|
461
|
+
.nullable()
|
|
462
|
+
.optional()
|
|
463
|
+
.describe("agent_transcriptで、完了したturnの回答が空だった時だけtrue(textは空文字列)。回答を読めなかった時は誤りを返す。terminalではnull"),
|
|
457
464
|
},
|
|
458
465
|
}, async ({ session_id, wait, until, until_regex, timeout, screen, full, lines, line_range, raw, rtk, agent_transcript, operation_id }) => {
|
|
459
466
|
try {
|
|
@@ -478,6 +485,7 @@ registerRemoteAwareTool("pty_read", {
|
|
|
478
485
|
turn_id: result.turn_id,
|
|
479
486
|
harness: result.harness,
|
|
480
487
|
raw_chars: result.raw_chars,
|
|
488
|
+
answer_empty: result.answer_empty,
|
|
481
489
|
},
|
|
482
490
|
};
|
|
483
491
|
}
|
|
@@ -521,6 +529,7 @@ registerRemoteAwareTool("pty_read", {
|
|
|
521
529
|
turn_id: null,
|
|
522
530
|
harness: null,
|
|
523
531
|
raw_chars: null,
|
|
532
|
+
answer_empty: null,
|
|
524
533
|
},
|
|
525
534
|
};
|
|
526
535
|
}
|
package/dist/parent-delivery.js
CHANGED
|
@@ -300,7 +300,8 @@ export class ParentDeliveryManager {
|
|
|
300
300
|
...(record.boundary.remote ? { remote: record.boundary.remote } : {}) });
|
|
301
301
|
if (record.state !== "waiting")
|
|
302
302
|
return;
|
|
303
|
-
|
|
303
|
+
// 回答を空で終えたturnも完了として届ける。親が完了を知る手段はこの配送だけなので、本文の代わりに知らせを入れる。
|
|
304
|
+
record.text = answer.text.trim() ? answer.text : "子のターンは完了しましたが、回答の本文は空でした。";
|
|
304
305
|
}
|
|
305
306
|
else {
|
|
306
307
|
record.text = completion.error ?? completion.rate_limit ?? "子のセッションが閉じられたため、回答はありません。";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
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": [
|