aiterm-mcp 0.61.3 → 0.61.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 +9 -2
- package/README.ja.md +2 -1
- package/README.md +2 -1
- package/dist/execution-effect.js +14 -4
- package/dist/execution-runtime.js +5 -3
- package/dist/execution-service.js +11 -6
- package/dist/execution-tools.js +3 -3
- package/dist/tmux-runtime.js +12 -3
- package/docs/DESIGN.md +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.61.4] - 2026-10-22
|
|
11
|
+
|
|
12
|
+
### 修正
|
|
13
|
+
|
|
14
|
+
- POSIXのnative実行口で最初の`agent_launch`がtmuxソケット未作成時に`EXECUTION_UNSUPPORTED`で止まっていた。発行直前にも空状態を確かめ、最初の`new-session`自身が作ったserverのPID・開始識別子とsession IDを実receiptから照合して起動する。補助sessionや入力再送は作らず、読取不能・receipt不一致は未確認で止める。
|
|
15
|
+
|
|
10
16
|
## [0.61.3] - 2026-10-22
|
|
11
17
|
|
|
12
18
|
### Fixed
|
|
@@ -18,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
24
|
|
|
19
25
|
### 修正
|
|
20
26
|
|
|
21
|
-
- tmuxがまだ起動しておらず、対象ソケットが存在しない時、新規実行の登録が`EXECUTION_UNCONFIRMED
|
|
27
|
+
- tmuxがまだ起動しておらず、対象ソケットが存在しない時、新規実行の登録が`EXECUTION_UNCONFIRMED`で止まっていた。実行世代の観測でもソケット未作成を空状態として扱う。別ソケットや権限・接続の異常は引き続き不明として止める。この版では、その次の初回席作成が既存serverを要求しており、冷間起動はまだ完了しなかった。
|
|
22
28
|
|
|
23
29
|
## [0.61.1] - 2026-10-22
|
|
24
30
|
|
|
@@ -2722,7 +2728,8 @@ prototype (preserved under `prototype/python/` as the porting source and referen
|
|
|
2722
2728
|
`ubuntu-latest` for Node 18/20/22, publishing to npm on `v*` tags with
|
|
2723
2729
|
provenance.
|
|
2724
2730
|
|
|
2725
|
-
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.
|
|
2731
|
+
[Unreleased]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.4...HEAD
|
|
2732
|
+
[0.61.4]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.3...v0.61.4
|
|
2726
2733
|
[0.61.3]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.2...v0.61.3
|
|
2727
2734
|
[0.61.2]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.1...v0.61.2
|
|
2728
2735
|
[0.61.1]: https://github.com/kitepon/aiterm-mcp/compare/v0.61.0...v0.61.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.61.
|
|
212
|
+
**状態:** 開発継続中 · 現行公開版 **v0.61.4** · 動作対象は Linux · WSL2 · macOS · Windows ネイティブ · MIT · [変更履歴](CHANGELOG.md)。
|
|
213
213
|
|
|
214
214
|
### 更新と巻き戻し
|
|
215
215
|
|
|
@@ -736,6 +736,7 @@ agent_launch({ "harness": "codex-cli", "remote": { "host": "rabbit" }, "cwd": "/
|
|
|
736
736
|
## 人が覗く
|
|
737
737
|
|
|
738
738
|
セッションは共有tmux socket(Windows nativeはpsmux namespace)上にある。`pty_open`/`agent_launch`の戻り値に表示されるattachコマンドで、人間がClaude/Codex/Grok/Cursor harnessの同じ端末へ入り、途中でキーボードを引き取れる。
|
|
739
|
+
POSIXでは、tmuxのソケットがまだ無くても最初の`agent_launch`でserverを起動できる。Aitermは作成したsessionとserverを実際のreceiptから照合して受付を確定し、不明な結果を入力の再送で補わない。
|
|
739
740
|
|
|
740
741
|
## 要件
|
|
741
742
|
|
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.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).
|
|
231
231
|
|
|
232
232
|
### Update and rollback
|
|
233
233
|
|
|
@@ -794,6 +794,7 @@ Each `pty_send` accepts at most 64 KiB of UTF-8 text. Sends to the same session
|
|
|
794
794
|
## A human can watch
|
|
795
795
|
|
|
796
796
|
Sessions live on a shared tmux socket on POSIX or a shared psmux namespace on native Windows. The attach line printed by `pty_open` and `agent_launch` lets a human attach to the same terminal and intervene, including a Claude/Codex/Grok/Cursor harness session: `tmux -S … attach -t <id>` on POSIX, or `psmux -L <namespace> attach -t <id>` on native Windows.
|
|
797
|
+
On POSIX, the first `agent_launch` can create the tmux server when its socket does not yet exist. Aiterm verifies the exact created session and server before accepting that launch; an uncertain result is reported without retrying the input.
|
|
797
798
|
|
|
798
799
|
## Requirements
|
|
799
800
|
|
package/dist/execution-effect.js
CHANGED
|
@@ -197,10 +197,13 @@ export function prepareExecutionCreation() {
|
|
|
197
197
|
refuse(scope, "EXECUTION_UNSUPPORTED");
|
|
198
198
|
try {
|
|
199
199
|
const server = scope.creation.prepare();
|
|
200
|
-
if (!Number.isSafeInteger(server?.pid) || server.pid < 1
|
|
200
|
+
if (server !== null && (!Number.isSafeInteger(server?.pid) || server.pid < 1
|
|
201
|
+
|| typeof server.started_identity !== "string" || !server.started_identity))
|
|
201
202
|
throw new ExecutionError("EXECUTION_UNSUPPORTED");
|
|
202
|
-
|
|
203
|
+
if (server)
|
|
204
|
+
scope.creationServer = { ...server };
|
|
203
205
|
scope.creationPrepared = true;
|
|
206
|
+
return server === null;
|
|
204
207
|
}
|
|
205
208
|
catch (error) {
|
|
206
209
|
scope.failure = new ExecutionError(error instanceof ExecutionError ? error.code : "EXECUTION_UNCONFIRMED", "not_started");
|
|
@@ -208,7 +211,7 @@ export function prepareExecutionCreation() {
|
|
|
208
211
|
}
|
|
209
212
|
}
|
|
210
213
|
/** Private core metadata stamp, derived only from this frame's actual create
|
|
211
|
-
* receipt and
|
|
214
|
+
* receipt and verified native server proof. Not a tool input or name lookup.
|
|
212
215
|
*/
|
|
213
216
|
export function executionCreatedNativeBinding(session) {
|
|
214
217
|
const scope = scopes.getStore();
|
|
@@ -237,7 +240,14 @@ export function captureExecutionCreatedNative(receipt) {
|
|
|
237
240
|
if (!parsed || parsed[2] !== scope.session || (scope.createdNative && scope.createdNative !== parsed[1]))
|
|
238
241
|
refuse(scope, "EXECUTION_CONFLICT");
|
|
239
242
|
try {
|
|
240
|
-
scope.creation.native(scope.session, parsed[1], Number(parsed[3]));
|
|
243
|
+
const server = scope.creation.native(scope.session, parsed[1], Number(parsed[3]));
|
|
244
|
+
if (server) {
|
|
245
|
+
if (!Number.isSafeInteger(server.pid) || server.pid < 1 || typeof server.started_identity !== "string" || !server.started_identity)
|
|
246
|
+
throw new ExecutionError("EXECUTION_UNCONFIRMED", "possible");
|
|
247
|
+
scope.creationServer = { ...server };
|
|
248
|
+
}
|
|
249
|
+
if (!scope.creationServer)
|
|
250
|
+
throw new ExecutionError("EXECUTION_UNCONFIRMED", "possible");
|
|
241
251
|
}
|
|
242
252
|
catch (error) {
|
|
243
253
|
scope.failure = new ExecutionError(error instanceof ExecutionError ? error.code : "EXECUTION_UNCONFIRMED", "possible");
|
|
@@ -84,11 +84,13 @@ export function createExecutionRuntime(bindings) {
|
|
|
84
84
|
session_id: executionLaunchSchema.shape.session_id, native_id: z.string().regex(/^\$\d+$/),
|
|
85
85
|
}).strict()) }).strict().parse(bindings.observeNativeSessionsSync());
|
|
86
86
|
if (new Set(parsed.sessions.map(value => value.session_id)).size !== parsed.sessions.length
|
|
87
|
-
|| new Set(parsed.sessions.map(value => value.native_id)).size !== parsed.sessions.length
|
|
87
|
+
|| new Set(parsed.sessions.map(value => value.native_id)).size !== parsed.sessions.length
|
|
88
|
+
|| (parsed.server === null && parsed.sessions.length > 0))
|
|
88
89
|
return { state: "unknown" };
|
|
90
|
+
const server = parsed.server === null ? { server: null } : parsed.server ? { server: parsed.server } : {};
|
|
89
91
|
const target = parsed.sessions.find(value => value.session_id === session);
|
|
90
|
-
return target ? { state: "present", native_id: target.native_id, ...
|
|
91
|
-
: { state: "absent", ...
|
|
92
|
+
return target ? { state: "present", native_id: target.native_id, ...server }
|
|
93
|
+
: { state: "absent", ...server };
|
|
92
94
|
}
|
|
93
95
|
catch {
|
|
94
96
|
return { state: "unknown" };
|
|
@@ -408,7 +408,7 @@ export class ExecutionService {
|
|
|
408
408
|
if (record.operations.launch !== "preparing" || record.relation.lifecycle === "retired")
|
|
409
409
|
throw new ExecutionError("EXECUTION_CONFLICT");
|
|
410
410
|
const native = this.runtime.nativeSessionSync(record.reservation.target.session_id);
|
|
411
|
-
if (native.state !== "absent" ||
|
|
411
|
+
if (native.state !== "absent" || native.server === undefined)
|
|
412
412
|
throw new ExecutionError("EXECUTION_UNSUPPORTED");
|
|
413
413
|
return native.server;
|
|
414
414
|
}, { readOnly: true });
|
|
@@ -441,7 +441,7 @@ export class ExecutionService {
|
|
|
441
441
|
throw new ExecutionError("EXECUTION_UNSUPPORTED");
|
|
442
442
|
this.store.transactionSync(state => {
|
|
443
443
|
const record = find(state, request), native = this.runtime.nativeSessionSync?.(record.reservation.target.session_id);
|
|
444
|
-
if (
|
|
444
|
+
if (native?.state !== "absent" || !same(native.server, expected))
|
|
445
445
|
throw new ExecutionError("EXECUTION_UNCONFIRMED");
|
|
446
446
|
}, { readOnly: true });
|
|
447
447
|
}
|
|
@@ -449,18 +449,23 @@ export class ExecutionService {
|
|
|
449
449
|
const { request } = executionRefMetaSchema.parse(metaInput);
|
|
450
450
|
if (!/^\$\d+$/.test(nativeId) || !this.store.transactionSync)
|
|
451
451
|
throw new ExecutionError("EXECUTION_UNSUPPORTED", "possible");
|
|
452
|
-
this.store.transactionSync(state => {
|
|
452
|
+
return this.store.transactionSync(state => {
|
|
453
453
|
const record = find(state, request), slot = state.slots.find(value => same(value.request, request) && value.stage === "launch");
|
|
454
|
+
// A cold first new-session creates the server itself. Prove its owner
|
|
455
|
+
// from the exact receipt and a stable native listing before stamping it.
|
|
456
|
+
const observed = expectedServer ? null : this.runtime.nativeSessionSync?.(session);
|
|
457
|
+
const server = expectedServer ?? (observed?.state === "present" && observed.native_id === nativeId ? observed.server : undefined);
|
|
454
458
|
if (!slot || slot.state !== "issued" || record.operations.launch !== "issued" || slot.session_id !== session
|
|
455
459
|
|| record.reservation.target.expected_launch_id !== null || record.relation.lifecycle === "retired"
|
|
456
|
-
||
|
|
460
|
+
|| !server || server.pid !== receiptPid
|
|
457
461
|
|| (slot.created_native_id && slot.created_native_id !== nativeId)
|
|
458
|
-
|| (slot.created_server && !same(slot.created_server,
|
|
462
|
+
|| (slot.created_server && !same(slot.created_server, server)))
|
|
459
463
|
throw new ExecutionError("EXECUTION_CONFLICT", "possible");
|
|
460
464
|
// Preserve this genuine native receipt even if a stop is already fencing
|
|
461
465
|
// its caller. Binding metadata does not authorize another native action.
|
|
462
466
|
slot.created_native_id = nativeId;
|
|
463
|
-
slot.created_server =
|
|
467
|
+
slot.created_server = server;
|
|
468
|
+
return server;
|
|
464
469
|
});
|
|
465
470
|
}
|
|
466
471
|
bindCreatedLaunch(metaInput, input) {
|
package/dist/execution-tools.js
CHANGED
|
@@ -126,12 +126,12 @@ async function runExecutionReceipt(service, meta, stage, body, targetSessionId,
|
|
|
126
126
|
return run();
|
|
127
127
|
}, stage === "launch" ? {
|
|
128
128
|
prepare: () => { expectedServer = service.prepareCreation(ref); return expectedServer; },
|
|
129
|
-
verifyServer: () => { if (
|
|
129
|
+
verifyServer: () => { if (expectedServer === undefined)
|
|
130
130
|
throw new ExecutionError("EXECUTION_UNSUPPORTED"); service.verifyCreationServer(ref, expectedServer); },
|
|
131
131
|
native: (session, nativeId, serverPid) => {
|
|
132
|
-
if (
|
|
132
|
+
if (expectedServer === undefined)
|
|
133
133
|
throw new ExecutionError("EXECUTION_UNSUPPORTED");
|
|
134
|
-
service.captureCreatedNative(ref, session, nativeId, expectedServer, serverPid);
|
|
134
|
+
return service.captureCreatedNative(ref, session, nativeId, expectedServer, serverPid);
|
|
135
135
|
},
|
|
136
136
|
launch: launch => service.bindCreatedLaunch(ref, launch),
|
|
137
137
|
} : undefined, prepare);
|
package/dist/tmux-runtime.js
CHANGED
|
@@ -493,12 +493,12 @@ export function registeredSessionEnvironment(name) {
|
|
|
493
493
|
export function tmuxNewSession(observe, name, launch, registered = []) {
|
|
494
494
|
const guarded = hasExecutionCreationScope();
|
|
495
495
|
// Windows per-session server creation needs a separate native-owner proof.
|
|
496
|
-
// The legacy public launcher is unchanged
|
|
497
|
-
|
|
496
|
+
// The legacy public launcher is unchanged.
|
|
497
|
+
let cold = false;
|
|
498
498
|
if (guarded) {
|
|
499
499
|
if (isWin)
|
|
500
500
|
throw new ExecutionError("EXECUTION_UNSUPPORTED");
|
|
501
|
-
prepareExecutionCreation();
|
|
501
|
+
cold = prepareExecutionCreation();
|
|
502
502
|
}
|
|
503
503
|
const printNative = guarded ? ["-P", "-F", "#{session_id}\t#{session_name}\t#{pid}"] : [];
|
|
504
504
|
if (isWin) {
|
|
@@ -508,6 +508,15 @@ export function tmuxNewSession(observe, name, launch, registered = []) {
|
|
|
508
508
|
return result;
|
|
509
509
|
}
|
|
510
510
|
const env = sessionCallerEnvironment();
|
|
511
|
+
if (cold) {
|
|
512
|
+
// With no server, there is no global environment to adjust. The first
|
|
513
|
+
// new-session inherits this caller's environment and prints its native ID
|
|
514
|
+
// and newly created server PID for the guarded receipt proof.
|
|
515
|
+
const r = runTmux(observe, undefined, ["new-session", "-d", "-s", name, ...printNative, ...launch.args, launch.shell], env);
|
|
516
|
+
if (r.code === 0 || r.stdout)
|
|
517
|
+
captureExecutionCreatedNative(r.stdout);
|
|
518
|
+
return r;
|
|
519
|
+
}
|
|
511
520
|
const wildcard = tmuxHasSessionEnvironmentFlags();
|
|
512
521
|
// tmux は起動時に PWD を共通環境へ足す。
|
|
513
522
|
const others = new Set(["PWD"]);
|
package/docs/DESIGN.md
CHANGED
|
@@ -28,8 +28,10 @@ native emit直前に照合したhook参照は既存deliveryへ保存する。Cod
|
|
|
28
28
|
POSIXのraw tty回復もnative作用であり、tmux打鍵と同じ受付を通す。ttyは正確なsession selectorで読み、
|
|
29
29
|
実stty直前にも同じdeviceを確認する。停止や世代/tty交代後に古いdeviceへ回復を発行しない。
|
|
30
30
|
一つのRPCが複数のnative作用を発行するときも、各回に同じissued slotとsource/target世代・停止を再確認する。
|
|
31
|
-
新launchは、既存POSIX serverのnative owner
|
|
32
|
-
|
|
31
|
+
新launchは、既存POSIX serverのnative owner、またはserver未作成の完全な空観測を受付時と発行直前に確認する。
|
|
32
|
+
冷間時は最初のnew-session自身でserverを作り、その実receiptのnative IDとserver PIDを、作成後の安定したnative一覧とprocess開始識別子で照合する。
|
|
33
|
+
既存server時も冷間時も、作成したsessionのnative ID・server owner・core内部のlaunch世代を同じ要求slotへ段階結合する。
|
|
34
|
+
物理shell準備はnative IDへ固定し、未結合の同名世代を採用しない。冷間起動のための補助sessionや再送は作らない。
|
|
33
35
|
作成時のnative ID/server ownerをmanaged metadataへ保存し、private runtimeの世代読取と期待closeでも再照合する。
|
|
34
36
|
古いmetadataの欠測を現観測で補わず、既存席の自動登録は行わない。
|
|
35
37
|
証拠の無い新server起動、Windows固有のserver作成、auto-name/通常PTY/auth等はまだunsupported。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiterm-mcp",
|
|
3
|
-
"version": "0.61.
|
|
3
|
+
"version": "0.61.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": [
|