diff --git a/PARITY.md b/PARITY.md
index 30a5f4f..815ae5b 100644
--- a/PARITY.md
+++ b/PARITY.md
@@ -1,5 +1,22 @@
# Python compatibility and npm migration
+## 0.5.1 parser correctness patch — 2026-10-27 KST
+
+Issue #163 updates only the TOML parser dependency from 1.9.0 to 1.9.1.
+Malformed repeated exponent signs such as `numeric_setting = 3e++20` previously
+parsed successfully and could make configuration inspection return `default`.
+The corrected parser rejects that input, so doctor reports
+`unknown / codex_config_unreadable` and Codex send/dry-run refuse before queueing.
+Valid exponent signs and the explicit root `sqlite_home` refusal are preserved.
+This is a reproduced parser-correctness fix; no storage-relocation bypass or
+published vulnerability is claimed.
+
+The exact 0.5.1 doctor row preserves the four profiles accepted by 0.5.0;
+all historical rows and immutable skill pins are unchanged. SSH endpoints must
+both use exactly 0.5.1. Unsupported platform/native/automatic-ACK cells and
+excluded experiments/reporter/harness remain outside the patch.
+
+
## 0.5.0 release scope — 2026-10-20 KST
The 0.5.0 source carries platform-qualification groundwork. It does not activate additional observation or
diff --git a/README.ja.md b/README.ja.md
index 84ef708..ecfabac 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -14,9 +14,9 @@
-**文書・ソースのバージョン: 0.5.0。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](VALIDATION.md)、リリース手順は [RELEASING.md](RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.6 と 0.5.0 は互いの SSH 事前確認を拒否します。
+**文書・ソースのバージョン: 0.5.1。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](VALIDATION.md)、リリース手順は [RELEASING.md](RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.6 と 0.5.1 は互いの SSH 事前確認を拒否します。
-**0.4.0 handoff の範囲:** 任意の native 観測は macOS arm64 の厳密な Codex 0.160.1/0.162.1 に限定し、明示的な `operator_confirmed` 受領確認には対応する POSIX ledger を使います。Linux・Windows の native 観測と自動 ACK 収集は未対応です。 0.5.0 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。
+**0.4.0 handoff の範囲:** 任意の native 観測は macOS arm64 の厳密な Codex 0.160.1/0.162.1 に限定し、明示的な `operator_confirmed` 受領確認には対応する POSIX ledger を使います。Linux・Windows の native 観測と自動 ACK 収集は未対応です。 0.5.1 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。
**実行中の Claude Code・Codex セッションを見つけ、ローカルまたは SSH でメッセージを送信します。**
Python 不要の Node.js クライアントです。パッケージ名とコマンド名は `session-peer` です。
@@ -57,15 +57,15 @@ SSH は既存の鍵・ホスト信頼設定と両端の**同じバージョン
`Get-Command session-peer -All` で PATH を確認してください。利用する実装を選び、
別の管理ツールのファイルを `--force` で上書きしないでください。
-**0.5.0 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](docs/guide.ja.md#ソースからビルド)を使います。
+**0.5.1 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](docs/guide.ja.md#ソースからビルド)を使います。
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-期待する出力: `session-peer 0.5.0 (typescript)`。分離インストール・ソースビルド・
+期待する出力: `session-peer 0.5.1 (typescript)`。分離インストール・ソースビルド・
Windows・削除の手順は以下の詳細ガイドを参照してください。
任意の `sp` 短縮名は 0.3.0 以降に含まれ(0.2.1 以前にはありません)、自動では有効になりません。
@@ -74,12 +74,12 @@ Windows・削除の手順は以下の詳細ガイドを参照してください
### Update
npm で導入したクライアントは npm で更新します。タグと対象バージョンを確認してから
-正確なバージョンを指定してください。以下は古い npm 版を選択した 0.5.0 に更新する例です。
+正確なバージョンを指定してください。以下は古い npm 版を選択した 0.5.1 に更新する例です。
CLI は更新をインストールせず、`session-peer update --check` は確認のみ行います。
```sh
npm view session-peer dist-tags
-npm install --global --ignore-scripts session-peer@0.5.0
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
diff --git a/README.ko.md b/README.ko.md
index 8c1ed33..d6394e6 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -19,9 +19,9 @@ Python을 설치하지 않아도 Node.js에서 실행되는 TypeScript 클라이
-**문서·소스 기준 버전: 0.5.0.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](VALIDATION.md), 릴리스 절차는 [RELEASING.md](RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.6과 0.5.0은 서로의 SSH 사전 확인을 거부합니다.
+**문서·소스 기준 버전: 0.5.1.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](VALIDATION.md), 릴리스 절차는 [RELEASING.md](RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.6과 0.5.1은 서로의 SSH 사전 확인을 거부합니다.
-**0.4.0 handoff 범위:** 선택 기능인 native 관측은 macOS arm64의 정확한 Codex 0.160.1/0.162.1에 한정되며, 명시적 `operator_confirmed` 수령 확인에는 지원되는 POSIX ledger를 사용합니다. Linux·Windows의 native 관측과 자동 ACK 수집은 미지원입니다. 0.5.0 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다.
+**0.4.0 handoff 범위:** 선택 기능인 native 관측은 macOS arm64의 정확한 Codex 0.160.1/0.162.1에 한정되며, 명시적 `operator_confirmed` 수령 확인에는 지원되는 POSIX ledger를 사용합니다. Linux·Windows의 native 관측과 자동 ACK 수집은 미지원입니다. 0.5.1 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다.
## 시연
@@ -66,15 +66,15 @@ PowerShell에서는 `Get-Command session-peer -All`로 PATH(명령 검색 경로
실행 파일을 찾는지 확인하세요. 사용할 구현을 선택하고 다른 패키지 관리자가 설치한
파일을 `--force`로 덮어쓰지 마세요. 설치와 갱신은 사용자가 npm으로 직접 수행합니다.
-**0.5.0 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](docs/guide.ko.md#소스에서-빌드)를 사용하세요.
+**0.5.1 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](docs/guide.ko.md#소스에서-빌드)를 사용하세요.
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-예상 결과는 `session-peer 0.5.0 (typescript)`입니다. 별도 경로에 설치하거나,
+예상 결과는 `session-peer 0.5.1 (typescript)`입니다. 별도 경로에 설치하거나,
소스에서 빌드하거나, Windows에서 설치·제거하는 방법은 [사용자 가이드](docs/guide.ko.md#설치)를 참고하세요.
선택 사항인 `sp` 단축 명령은 0.3.0 이상에 포함되며(0.2.1 이하에는 없음) 자동으로
@@ -85,13 +85,13 @@ session-peer --version
### 업데이트
npm으로 설치한 클라이언트는 사용자가 직접 npm으로 업데이트합니다. 사용 가능한 배포 태그
-(dist-tag)와 대상 버전을 확인한 뒤 해당 버전을 지정해 설치하세요. 아래 예시는 이전 npm 설치본을 선택한 0.5.0으로
+(dist-tag)와 대상 버전을 확인한 뒤 해당 버전을 지정해 설치하세요. 아래 예시는 이전 npm 설치본을 선택한 0.5.1으로
업데이트합니다. `session-peer` CLI는 업데이트를 설치하지 않습니다. `session-peer
update --check`는 새 버전이 있는지 확인할 뿐입니다.
```sh
npm view session-peer dist-tags
-npm install --global --ignore-scripts session-peer@0.5.0
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
diff --git a/README.md b/README.md
index 8105462..183369e 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,9 @@
-**Documentation and source version: 0.5.0.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](VALIDATION.md); release procedures are in [RELEASING.md](RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.6 and 0.5.0 refuse each other’s SSH preflight.
+**Documentation and source version: 0.5.1.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](VALIDATION.md); release procedures are in [RELEASING.md](RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.6 and 0.5.1 refuse each other’s SSH preflight.
-**0.4.0 handoff scope:** opt-in native observation is limited to exact Codex 0.160.1/0.162.1 on macOS arm64; explicit `operator_confirmed` receipts use supported POSIX ledgers. Linux/Windows native observation and automatic ACK collection remain unsupported. The 0.5.0 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported.
+**0.4.0 handoff scope:** opt-in native observation is limited to exact Codex 0.160.1/0.162.1 on macOS arm64; explicit `operator_confirmed` receipts use supported POSIX ledgers. Linux/Windows native observation and automatic ACK collection remain unsupported. The 0.5.1 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported.
**Find and message running Claude Code and Codex sessions, locally or over SSH.**
A Node.js client that runs without Python. Package and command: `session-peer`.
@@ -57,15 +57,15 @@ Check `type -a session-peer` on macOS/Linux or `Get-Command session-peer -All`
in PowerShell before installing. Choose the intended PATH entry; do not use
`--force` to overwrite another manager’s files.
-**Version 0.5.0 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](docs/guide.md#build-from-source).
+**Version 0.5.1 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](docs/guide.md#build-from-source).
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-Expected: `session-peer 0.5.0 (typescript)`. For isolated installation, source
+Expected: `session-peer 0.5.1 (typescript)`. For isolated installation, source
builds, Windows and removal, see the detailed guide below.
Optional `sp` shorthand ships with 0.3.0 and later (not 0.2.1 or earlier) and is
@@ -75,12 +75,12 @@ never enabled automatically. See [explicit activation and collisions](docs/short
Use npm for an npm-managed installation. Check the available tags and review
the target version, then install that exact version; this example updates an
-older npm installation to the selected 0.5.0 release. The CLI does not install updates;
+older npm installation to the selected 0.5.1 release. The CLI does not install updates;
`session-peer update --check` only reports them.
```sh
npm view session-peer dist-tags
-npm install --global --ignore-scripts session-peer@0.5.0
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
diff --git a/README.zh-CN.md b/README.zh-CN.md
index b56d466..1a9c539 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -14,9 +14,9 @@
-**文档和源码版本:0.5.0。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](VALIDATION.md),发布流程见 [RELEASING.md](RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.6 与 0.5.0 会拒绝彼此的 SSH 预检。
+**文档和源码版本:0.5.1。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](VALIDATION.md),发布流程见 [RELEASING.md](RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.6 与 0.5.1 会拒绝彼此的 SSH 预检。
-**0.4.0 handoff 范围:** 可选 native 观测仅限 macOS arm64 上精确 Codex 0.160.1/0.162.1 版本;明确的 `operator_confirmed` 收件确认使用受支持的 POSIX ledger。Linux、Windows 的 native 观测和自动 ACK 收集仍不支持。 0.5.0 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。
+**0.4.0 handoff 范围:** 可选 native 观测仅限 macOS arm64 上精确 Codex 0.160.1/0.162.1 版本;明确的 `operator_confirmed` 收件确认使用受支持的 POSIX ledger。Linux、Windows 的 native 观测和自动 ACK 收集仍不支持。 0.5.1 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。
**发现正在运行的 Claude Code 和 Codex 会话,并在本机或通过 SSH 发送消息。**
这是不需要 Python 的 Node.js 客户端。包名与命令名均为 `session-peer`。
@@ -55,15 +55,15 @@ session-peer send --to CLAUDE_PID --message '请检查 API 契约并回复。' -
`Get-Command session-peer -All` 检查 PATH。选择所需实现,不要用 `--force`
覆盖其他安装管理器的文件。
-**安装 0.5.0:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](docs/guide.zh-CN.md#从源码构建)。
+**安装 0.5.1:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](docs/guide.zh-CN.md#从源码构建)。
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-预期输出:`session-peer 0.5.0 (typescript)`。隔离安装、源码构建、Windows 和
+预期输出:`session-peer 0.5.1 (typescript)`。隔离安装、源码构建、Windows 和
卸载步骤见下方详细指南。
可选的 `sp` 简写包含在 0.3.0 及更高版本中(0.2.1 及更早版本没有),不会自动启用。
@@ -72,11 +72,11 @@ session-peer --version
### Update
使用 npm 更新由 npm 管理的安装。先检查标签并审阅目标版本,再安装指定版本。
-下面示例将旧 npm 安装更新至所选的 0.5.0。CLI 不会安装更新,`session-peer update --check` 只做检查。
+下面示例将旧 npm 安装更新至所选的 0.5.1。CLI 不会安装更新,`session-peer update --check` 只做检查。
```sh
npm view session-peer dist-tags
-npm install --global --ignore-scripts session-peer@0.5.0
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
diff --git a/RELEASING.md b/RELEASING.md
index 670f65e..a84633d 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -9,6 +9,59 @@ the historical 0.3.2 settings-read exception is recorded below. The workflow
uses OIDC and no npm token. The protected GitHub environment requires a reviewer
and permits deployments from `main` only.
+## 0.5.1 dependency hotfix procedure — 2026-10-27 KST
+
+Issue #163 pins smol-toml 1.9.1 to reject malformed repeated exponent signs in
+Codex configuration. It preserves explicit relocated-storage refusal and the
+four exact 0.5.0 skill profiles in a finite 0.5.1 row. This is a parser correctness
+patch, not a claimed CVE remediation or newly qualified platform/ACK feature.
+Draft #155, research #82/#142 and the repository-only reporter #160 are excluded.
+
+The read-only registry baseline is latest=0.5.0, preview=0.1.0-preview.1 and
+0.5.1 absent. Previous stable integrity is
+`sha512-lLc7SFRUydY5TjcX8aE18kgfC88Wi4Mx/OXcbLlk0W9yH+JYLkxGwmZIiC7S1bczFgXzsP3xSe8DqiD7YplpDw==`.
+Previous provenance endpoint:
+`https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.5.0`.
+
+For this 0.5.1 release, the user explicitly skipped the browser-only package
+publish-MFA policy read. That setting is not freshly audited. Authenticated npm
+ownership and absence of pending stages were confirmed; the fresh Trusted
+Publisher lookup returned repository `abruption/session-peer-ts`, workflow
+`publish.yml`, environment `npm` and only `createStagedPackage` permission.
+The protected GitHub environment and actual final npm stage 2FA approval remain
+required. No publisher/protection/credential settings were changed. This
+settings-read exception applies only to 0.5.1.
+
+1. Merge the reviewed patch under normal protections and require the exact
+ merged-main release gate, Windows Node 22/24 and CodeQL checks.
+2. Freshly verify npm ownership, pending stages, stage-only Trusted Publisher,
+ package publish-MFA and the protected npm environment/main restriction.
+ Historical exceptions apply only to their named releases; any unread gate
+ needs a current explicit user exception.
+3. Verify reproducible exact-source archive bytes, all packaged Markdown,
+ isolated installation/version/removal and default/undefined/inspectSkill
+ behavior. Both SSH endpoints must use exactly 0.5.1.
+4. Obtain source approval naming the exact merged 40-character SHA,
+ `session-peer@0.5.1`, `stable-stage` and eventual latest promotion to 0.5.1.
+5. Dispatch the approved source/version/mode once; inspect retained Actions
+ bytes before protected-environment approval and stage/provenance before the
+ separate final npm 2FA approval. Do not retry an uncertain upload.
+6. Verify public bytes, registry signatures/provenance and isolated lifecycle,
+ preserve preview and record publication. Create the authorized v0.5.1 tag
+ and GitHub Release on the verified publication source. No fleet install.
+
+## Public 0.5.0 checkpoint — 2026-10-21 KST
+
+[GitHub v0.5.0](https://github.com/abruption/session-peer-ts/releases/tag/v0.5.0)
+and npm 0.5.0 are public from
+`12f98f2793b78f647107368c06e83286c5026e05`.
+The reviewed/Actions/public tarball SHA-256 is
+`50f603d9e5b6db8a99b4bcab464767ad30ff38ecac3fbebc22fc03fdd4c66b1d`;
+[stage-only run 39415871999](https://github.com/abruption/session-peer-ts/actions/runs/39415871999)
+records publication and human gates. Its GitHub release retains the complete
+public validation and settings-read exception record. The following 0.5.0
+procedure is historical and must not be replayed for 0.5.1.
+
## 0.5.0 stable release procedure — 2026-10-20 KST
The 0.5.0 source includes platform-qualification groundwork from #153, #156
diff --git a/VALIDATION.md b/VALIDATION.md
index a52a9cb..3b96be1 100644
--- a/VALIDATION.md
+++ b/VALIDATION.md
@@ -1,5 +1,32 @@
# Validation record — updated 2026-10-20 KST
+## 0.5.1 dependency patch preparation — 2026-10-27 KST
+
+Baseline source: main `028780338310c9cfe469e1e1587857f730e99a1a`.
+The only intervening change from public 0.5.0 is README download-badge wording.
+Fresh npm advisory bulk checks across current main and the four open PR locks
+reported no advisory; open Dependabot and CodeQL alerts were empty. The scheduled
+[audit run](https://github.com/abruption/session-peer-ts/actions/runs/40295976850)
+on that exact main passed npm audit and signature/provenance checks on 2026-10-26.
+These facts do not prove the absence of every vulnerability.
+
+The actionable dependency defect is official smol-toml 1.9.1's
+[repeated-exponent-sign fix](https://github.com/squirrelchat/smol-toml/releases/tag/v1.9.1).
+With installed 1.9.0, `foo=3e++20` and `foo=3e--20` both produced `{foo:3}`.
+Two new doctor/transport regressions failed before the update: doctor reported
+`available` instead of `unknown`, and send/dry-run did not report
+`codex_config_unreadable`. With pinned 1.9.1 both regressions pass, including
+root/nested invalid numbers, valid single-sign controls and no fixture queue call
+on refusal. This establishes incorrect malformed-config handling, not an actual
+relocated-storage bypass, credential exposure or live-agent exploit.
+
+The tests use isolated fixture homes/queues. No existing user session, remote
+host, global installation or native/model/ACK experiment is used. Full package,
+profile/release and current-head CI results belong to the patch PR and its exact
+source. This dated preparation record does not claim publication or fresh npm
+publisher/MFA settings approval.
+
+
## 0.5.0 preparation scope — 2026-10-20 KST
This release preparation includes the merged platform groundwork from #153,
diff --git a/docs/api.md b/docs/api.md
index 3234060..7aac65b 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,18 +1,18 @@
# API reference
-This reference describes source/package version **0.5.0**. Check registry availability and review the selected release before installation. Dated release evidence is recorded in [VALIDATION.md](../VALIDATION.md); before publication use a reviewed source artifact.
+This reference describes source/package version **0.5.1**. Check registry availability and review the selected release before installation. Dated release evidence is recorded in [VALIDATION.md](../VALIDATION.md); before publication use a reviewed source artifact.
[Back to README](../README.md) · [User guide](guide.md)
The package exposes a small ESM API with TypeScript declarations. Install it
-locally with `npm install --ignore-scripts session-peer@0.5.0` before importing it.
+locally with `npm install --ignore-scripts session-peer@0.5.1` before importing it.
Importing the package does not execute the CLI or submit a message.
```js
import { VERSION, VERSION_LINE, reply, envelope } from 'session-peer';
-console.log(VERSION); // 0.5.0
-console.log(VERSION_LINE); // session-peer 0.5.0 (typescript)
+console.log(VERSION); // 0.5.1
+console.log(VERSION_LINE); // session-peer 0.5.1 (typescript)
const address = 'session-peer://v1/reply?agent=claude&session=12345&transport=local';
console.log(reply(address)); // { to: '12345' }
console.log(envelope('Please review and reply.', true, address));
@@ -91,7 +91,7 @@ message/transcript scan manufactures ACK. Ordinary generated Reply-To stays v1.
POSIX opt-in `send --host HOST` performs an exact-version preflight and a
no-effect preparation before one destination-side send. Both ends must contain
-the same reviewed 0.5.0 implementation. This workflow was introduced in
+the same reviewed 0.5.1 implementation. This workflow was introduced in
0.4.0; a 0.3.x banner does not establish the capability. Initialize the destination's private ledger explicitly; no command
auto-installs a handler or initializes missing history. The origin does not
create a local receipt collector. Reconcile with `handoff status`/`wait` on the
@@ -193,7 +193,7 @@ requested evidence goal. After any possible RPC write, no legacy fallback or
second submission occurs.
Selection is by the exact host platform/architecture and native version.
-The 0.5.0 source records a Linux arm64 Codex 0.162.1 candidate (pinned musl
+The 0.5.1 source records a Linux arm64 Codex 0.162.1 candidate (pinned musl
executable, procfs storage binding, `/proc/locks` writer-owner check) but does
not activate it. Linux and any other unlisted platform/version have no
profile, so required delivery refuses before effect and best-effort uses the
@@ -273,7 +273,7 @@ a completed milestone. Exact evidence and limits are in [VALIDATION](../VALIDATI
## 0.4.0 implementation scope
-The 0.5.0 source preserves this supported handoff scope; its Linux and Windows
+The 0.5.1 source preserves this supported handoff scope; its Linux and Windows
groundwork does not enable additional platform or ACK support.
The 0.4.0 implementation includes the qualified macOS arm64 observer and explicit
diff --git a/docs/design/typescript-skill-compatibility.md b/docs/design/typescript-skill-compatibility.md
index f26d068..190c005 100644
--- a/docs/design/typescript-skill-compatibility.md
+++ b/docs/design/typescript-skill-compatibility.md
@@ -2,7 +2,24 @@
This document retains the original TS source proposal and dated design review for [#118](https://github.com/abruption/session-peer-ts/issues/118), based on the companion [Draft #20 at `963c3d7`](https://github.com/abruption/session-peer-skill/blob/963c3d748a11b1164013ee26c9c0ef285c52b285/docs/typescript-compatibility.md). Companion [design acceptance at `699a325`](https://github.com/abruption/session-peer-skill/blob/699a3251830c30c62d0f812363faf4d0eb0c8e0c/docs/typescript-compatibility.md) records the full static review of the exact candidates below on 2026-10-09. This design-only acceptance does not authorize runtime merge/release, successor skill metadata/publication, installation or pin changes, content authenticity, or live delivery support.
-## 0.5.0 finite row — release preparation
+## 0.5.1 finite row — 2026-10-27 KST release preparation
+
+The 0.5.1 source candidate explicitly accepts the same four exact metadata
+profiles as public 0.5.0: skill 0.1.0, 0.2.0, 0.2.1 and 0.3.0. Compiled default,
+explicit `undefined`, explicit 0.5.1 and outer `inspectSkill` use this finite row.
+The literal compatible result remains
+`compatible / skill_contract_compatible / metadata_only`. Changed profiles and
+unreviewed runtime identities such as 0.5.2 remain incompatible; malformed
+runtime identities remain `unknown / skill_metadata_missing` without a
+`verification` property. All historical rows remain explicit and unchanged.
+
+The TOML dependency-correctness patch does not change this metadata parser or
+its bounded reader, publish a new skill, adopt another installation pin or add
+platform/ACK qualification. Runtime publication still requires its release gates;
+these source fixtures are not proof of an actual npm publication. The existing
+four-language skill 0.2.1 source/hash below remain unchanged.
+
+## 0.5.0 finite row — historical release preparation
The 0.5.0 source candidate explicitly carries forward the same four exact skill
profiles as public 0.4.0: 0.1.0, 0.2.0, 0.2.1 and 0.3.0. The 0.3.0 profile
diff --git a/docs/guide.ja.md b/docs/guide.ja.md
index 8dc8e0f..460538a 100644
--- a/docs/guide.ja.md
+++ b/docs/guide.ja.md
@@ -4,11 +4,11 @@
[English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md)
-**文書・ソースのバージョン: 0.5.0。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](../VALIDATION.md)、リリース手順は [RELEASING.md](../RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.6 と 0.5.0 は互いの SSH 事前確認を拒否します。
+**文書・ソースのバージョン: 0.5.1。** インストール前に、選択したリリースとレジストリのメタデータを確認してください。日付付きの準備・検証記録は [VALIDATION.md](../VALIDATION.md)、リリース手順は [RELEASING.md](../RELEASING.md) にあります。SSH の両端には同じ TypeScript クライアントのバージョンが必要で、0.3.6 と 0.5.1 は互いの SSH 事前確認を拒否します。
-**0.4.0 handoff の範囲:** 任意の native 観測は macOS arm64 の厳密な Codex 0.160.1/0.162.1 に限定し、明示的な `operator_confirmed` 受領確認には対応する POSIX ledger を使います。Linux・Windows の native 観測と自動 ACK 収集は未対応です。 0.5.0 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。
+**0.4.0 handoff の範囲:** 任意の native 観測は macOS arm64 の厳密な Codex 0.160.1/0.162.1 に限定し、明示的な `operator_confirmed` 受領確認には対応する POSIX ledger を使います。Linux・Windows の native 観測と自動 ACK 収集は未対応です。 0.5.1 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。
-0.5.0 の詳細ガイドです([0.3.1 の信頼性の修正](../PARITY.md#031-reliability-fixes))。0.3.0 は複数ホスト SSH、送信者情報と返信経路、更新確認、任意の `sp` 短縮名を追加しました([0.3.0 の変更点](../PARITY.md#030-remote-usability-and-maintenance))。以前の公開記録は [0.3.1 公開リリースの証拠](../VALIDATION.md#public-031--2026-10-04-kst) にあります。以下の 0.1.0 の説明は旧版との比較です。
+0.5.1 の詳細ガイドです([0.3.1 の信頼性の修正](../PARITY.md#031-reliability-fixes))。0.3.0 は複数ホスト SSH、送信者情報と返信経路、更新確認、任意の `sp` 短縮名を追加しました([0.3.0 の変更点](../PARITY.md#030-remote-usability-and-maintenance))。以前の公開記録は [0.3.1 公開リリースの証拠](../VALIDATION.md#public-031--2026-10-04-kst) にあります。以下の 0.1.0 の説明は旧版との比較です。
@@ -44,7 +44,7 @@
実行中の **Claude Code と Codex セッション**に、ローカルまたは SSH 経由でメッセージを送る TypeScript クライアントです。Node.js で動作し、Python は不要です。
-**0.5.0 の利用ガイドです。**パッケージ名は `session-peer`、CLI コマンドは **`session-peer`**。Relay サーバーやホスティングサービスは提供しません。
+**0.5.1 の利用ガイドです。**パッケージ名は `session-peer`、CLI コマンドは **`session-peer`**。Relay サーバーやホスティングサービスは提供しません。
## 機能と範囲
@@ -61,7 +61,7 @@ Relay 通信、MCP、wake/resume、Antigravity、自動更新は未実装です
ビルド後の `node dist/cli.js list --json` は Claude/Codex をまとめて表示し、`list --agent codex --json` は既知の home を検索します。公開 npm **0.1.0** では引き続き agent と Codex 一覧の home を明示します。0.2.0 でも明示的なコマンド形式を利用できます。
-対象は既定の `~/.codex`、`CODEX_HOME`、macOS Orca 直下のアカウント home、JSON 配列 `SESSION_PEER_CODEX_HOMES` のみです。`--codex-home` は Codex 一覧を固定して無関係な設定エラーを回避し、`--agent claude` は Codex 探索を省略します。同じ home の別名は統合し、異なる home の同じ UUID は保持します。送信には各行の `codexHome` を使ってください。任意 home の不在はエラーではなく、明示した home の不在・不正は読み取れた行を保持して終了コード 1 を返します。一覧は writer を選択せず、メッセージを提出しません。[一覧契約](../PARITY.md#source-unified-listing-contract--16--020)に順序・診断・`--all`・SSH を記載しています。0.2.0の Codex 送信は `--codex-home` 省略時に一意で安定した live writer を選択します。明示 home でも既知の競合 home をすべて検査します。非アクティブなキュー送信には保存済みスレッド、全候補の非アクティブ検証、および `--codex-home HOME --allow-inactive-codex-home` が必要で、wake/resume は実行しません。Dry-run は提出しません。JSON に診断 `codexHomeResolution` と、ネイティブ出力にある場合のみ `queueId` を追加しますが、消費確認ではありません。[選択契約](../PARITY.md#source-codex-home-selection--17--020)を参照してください。公開 **0.1.0** には明示 live home が必要で、非アクティブ許可オプションはありません。SSH の両端には同じ TypeScript クライアントのバージョン(0.5.0)が必要です。
+対象は既定の `~/.codex`、`CODEX_HOME`、macOS Orca 直下のアカウント home、JSON 配列 `SESSION_PEER_CODEX_HOMES` のみです。`--codex-home` は Codex 一覧を固定して無関係な設定エラーを回避し、`--agent claude` は Codex 探索を省略します。同じ home の別名は統合し、異なる home の同じ UUID は保持します。送信には各行の `codexHome` を使ってください。任意 home の不在はエラーではなく、明示した home の不在・不正は読み取れた行を保持して終了コード 1 を返します。一覧は writer を選択せず、メッセージを提出しません。[一覧契約](../PARITY.md#source-unified-listing-contract--16--020)に順序・診断・`--all`・SSH を記載しています。0.2.0の Codex 送信は `--codex-home` 省略時に一意で安定した live writer を選択します。明示 home でも既知の競合 home をすべて検査します。非アクティブなキュー送信には保存済みスレッド、全候補の非アクティブ検証、および `--codex-home HOME --allow-inactive-codex-home` が必要で、wake/resume は実行しません。Dry-run は提出しません。JSON に診断 `codexHomeResolution` と、ネイティブ出力にある場合のみ `queueId` を追加しますが、消費確認ではありません。[選択契約](../PARITY.md#source-codex-home-selection--17--020)を参照してください。公開 **0.1.0** には明示 live home が必要で、非アクティブ許可オプションはありません。SSH の両端には同じ TypeScript クライアントのバージョン(0.5.1)が必要です。
### 0.2.0 CLI の使いやすさ
@@ -84,15 +84,15 @@ macOS / Linux / Windows native、Node **22.x の 22.13 以上、または 24.x**
Node 22.x の 22.13 以上または 24.x を使用し、インストールする正確なバージョンの公開状況を確認してください。Python CLI も同じコマンド名を使うため、既存のインストールがあれば先に PATH を確認してください。
-**0.5.0 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](#ソースからビルド)を使います。
+**0.5.1 のインストール:** npm の導入・更新例を実行する前に、レジストリで公開状況と選択したリリースを確認してください。利用できない場合は[レビュー済みソースビルド](#ソースからビルド)を使います。
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-期待値は `session-peer 0.5.0 (typescript)`。
+期待値は `session-peer 0.5.1 (typescript)`。
1. `session-peer list --agent claude --json` で宛先を探し、正確な PID を選びます。
2. `session-peer send --to CLAUDE_PID --message 'Please reply after checking.' --dry-run --json` で提出せずに確認します。
@@ -111,7 +111,7 @@ npm run build
node dist/cli.js --version
npm pack --ignore-scripts
# 任意のグローバルインストール前に PATH の既存コマンドを確認
-npm install --global --ignore-scripts ./session-peer-0.5.0.tgz
+npm install --global --ignore-scripts ./session-peer-0.5.1.tgz
session-peer --version
```
@@ -121,7 +121,7 @@ session-peer --version
インストール前後に `type -a session-peer` と `command -v session-peer` を確認します。他の実装も同名コマンドを提供するため、PATH 上の一つを選ぶか `node /absolute/path/dist/cli.js` を使ってください。`--force` で他の管理ツールのファイルを上書きしないでください。Python パッケージ・スキル・サービスを自動変更しません。削除は `npm uninstall --global session-peer` を使い、PATH を再確認します。
-Windows PowerShell では `Get-Command session-peer -All` で既存コマンドを確認します。Python CLI を置換せずに試すには、`npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts` の後、`npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.0.tgz` を実行し、`& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` で確認します。同じ prefix の `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` で削除します。
+Windows PowerShell では `Get-Command session-peer -All` で既存コマンドを確認します。Python CLI を置換せずに試すには、`npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts` の後、`npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.1.tgz` を実行し、`& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` で確認します。同じ prefix の `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` で削除します。
## 使い方
@@ -212,13 +212,13 @@ Python は開発時の比較基準です(v1.1.0、`8ca77cf31254c8287a2593fe484
## npm リリース
-公開 **0.3.2** は 2026-10-07 KST に検証されました([固定された公開記録](../VALIDATION.md#public-032--2026-10-07-kst))。以下のレジストリ照会で 0.5.0 の現在の公開状況と npm タグを確認してください。日付付きの準備記録は、その後の公開結果を証明しません。
+公開 **0.3.2** は 2026-10-07 KST に検証されました([固定された公開記録](../VALIDATION.md#public-032--2026-10-07-kst))。以下のレジストリ照会で 0.5.1 の現在の公開状況と npm タグを確認してください。日付付きの準備記録は、その後の公開結果を証明しません。
0.2.1 は 2026-09-29 KST、0.2.0 は 2026-09-28 KST に公開・検証されました。
[0.2.1 公開記録](../VALIDATION.md#public-021--2026-09-29-kst)を参照し(以降のリリースはそれぞれ日付付きの記録を追加します)、導入前に正確なバージョンと現在のタグを確認してください。
```sh
-npm view session-peer@0.5.0 version dist.integrity
+npm view session-peer@0.5.1 version dist.integrity
npm view session-peer dist-tags
```
@@ -260,7 +260,7 @@ session-peer update --check --channel preview --output-format text
実行中の CLI のパスからインストールの管理者を確実に特定できた場合にだけ示します。
対象は、自身の `session-peer` ランチャーがこのパッケージを指す npm グローバル prefix
(既定、Homebrew、nvm、nvm-windows、fnm。例:
-`npm install --global --ignore-scripts session-peer@0.5.0`)、マニフェストで
+`npm install --global --ignore-scripts session-peer@0.5.1`)、マニフェストで
`session-peer` を宣言している pnpm・Yarn・Bun のグローバルストア、Volta、npx キャッシュ
です。プロジェクトへのインストール(`npm_project`、`pnpm_project`)、ソースチェック
アウト(`source`)、それ以外(`unknown`)では `updateCommand: null` と `guidance` の文だけを
@@ -355,4 +355,4 @@ native 観測は、macOS arm64 の厳密な Codex 0.160.1/0.162.1 と既存の
[Observation](api.md#unreleased-local-delivery-observation--72)
-Linux・Windows の native 注入観測は未対応です。 0.5.0 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。対応する POSIX ledger では、運用者が明示的に確認した受領を `operator_confirmed` として記録できますが、自動 ACK 収集ではありません。0.4.0 の範囲は、検証済みの macOS 観測と明示的な運用者確認です。Linux・Windows 観測と receipt bootstrap は [#130](https://github.com/abruption/session-peer-ts/issues/130)、[#131](https://github.com/abruption/session-peer-ts/issues/131)、[#132](https://github.com/abruption/session-peer-ts/issues/132) の後続作業です。
+Linux・Windows の native 注入観測は未対応です。 0.5.1 のソースは選択できない Linux arm64 の Codex 0.162.1 候補(固定された上流 `aarch64-unknown-linux-musl` 実行ファイル、スキーマ、注入テスト 1 回)を記録しますが、Linux 観測を有効化しません。Linux の native 観測は引き続き未対応です。対応する POSIX ledger では、運用者が明示的に確認した受領を `operator_confirmed` として記録できますが、自動 ACK 収集ではありません。0.4.0 の範囲は、検証済みの macOS 観測と明示的な運用者確認です。Linux・Windows 観測と receipt bootstrap は [#130](https://github.com/abruption/session-peer-ts/issues/130)、[#131](https://github.com/abruption/session-peer-ts/issues/131)、[#132](https://github.com/abruption/session-peer-ts/issues/132) の後続作業です。
diff --git a/docs/guide.ko.md b/docs/guide.ko.md
index 1a7d358..c6f4395 100644
--- a/docs/guide.ko.md
+++ b/docs/guide.ko.md
@@ -4,11 +4,11 @@
[English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md)
-**문서·소스 기준 버전: 0.5.0.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](../VALIDATION.md), 릴리스 절차는 [RELEASING.md](../RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.6과 0.5.0은 서로의 SSH 사전 확인을 거부합니다.
+**문서·소스 기준 버전: 0.5.1.** 설치 전에 선택한 릴리스와 레지스트리 메타데이터를 확인하세요. 날짜별 준비·검증 기록은 [VALIDATION.md](../VALIDATION.md), 릴리스 절차는 [RELEASING.md](../RELEASING.md)에 있습니다. SSH 양쪽에는 같은 TypeScript 클라이언트 버전이 필요하며, 0.3.6과 0.5.1은 서로의 SSH 사전 확인을 거부합니다.
-**0.4.0 handoff 범위:** 선택 기능인 native 관측은 macOS arm64의 정확한 Codex 0.160.1/0.162.1에 한정되며, 명시적 `operator_confirmed` 수령 확인에는 지원되는 POSIX ledger를 사용합니다. Linux·Windows의 native 관측과 자동 ACK 수집은 미지원입니다. 0.5.0 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다.
+**0.4.0 handoff 범위:** 선택 기능인 native 관측은 macOS arm64의 정확한 Codex 0.160.1/0.162.1에 한정되며, 명시적 `operator_confirmed` 수령 확인에는 지원되는 POSIX ledger를 사용합니다. Linux·Windows의 native 관측과 자동 ACK 수집은 미지원입니다. 0.5.1 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다.
-이 가이드는 0.5.0을 다룹니다([0.3.1 신뢰성 수정](../PARITY.md#031-reliability-fixes)). 0.3.0에는 여러 호스트를 통한 SSH 전송, 발신자 정보와 회신 경로, 업데이트 확인, 선택 기능인 `sp` 단축 명령이 추가됐습니다([0.3.0 변경 사항](../PARITY.md#030-remote-usability-and-maintenance)). [0.3.1 공개 릴리스 기록](../VALIDATION.md#public-031--2026-10-04-kst)은 이전 버전의 발행 내역입니다. 아래에서 0.1.0을 언급할 때는 이전 버전을 설명합니다.
+이 가이드는 0.5.1을 다룹니다([0.3.1 신뢰성 수정](../PARITY.md#031-reliability-fixes)). 0.3.0에는 여러 호스트를 통한 SSH 전송, 발신자 정보와 회신 경로, 업데이트 확인, 선택 기능인 `sp` 단축 명령이 추가됐습니다([0.3.0 변경 사항](../PARITY.md#030-remote-usability-and-maintenance)). [0.3.1 공개 릴리스 기록](../VALIDATION.md#public-031--2026-10-04-kst)은 이전 버전의 발행 내역입니다. 아래에서 0.1.0을 언급할 때는 이전 버전을 설명합니다.
@@ -96,7 +96,7 @@ JSON에는 정제된 `codexHomeResolution`이 포함되며, 네이티브 응답
`queueId`도 포함됩니다. 둘 다 메시지를 읽거나 처리했음을 확인하지 않습니다.
[홈 선택 계약](../PARITY.md#source-codex-home-selection--17--020)을 참고하세요.
공개 **0.1.0**은 여전히 실행 중인 홈을 명시해야 하며 비활성 세션 허용 옵션이 없습니다.
-SSH를 사용할 때는 발신·수신 양쪽에 같은 TypeScript 클라이언트 버전(0.5.0)을 설치해야
+SSH를 사용할 때는 발신·수신 양쪽에 같은 TypeScript 클라이언트 버전(0.5.1)을 설치해야
합니다.
@@ -136,20 +136,20 @@ Claude Code에는 받은 메시지함에 접근할 수 있는 실행 중 TUI(터
Node 22.x의 22.13 이상 또는 24.x를 사용하세요. Python CLI도 같은 `session-peer`
명령 이름을 쓰므로, 설치 전에 PATH(명령 검색 경로)에서 어떤 실행 파일을 찾는지
-확인하세요. 아래 명령은 선택한 0.5.0을 npm으로 설치하는 예입니다.
+확인하세요. 아래 명령은 선택한 0.5.1을 npm으로 설치하는 예입니다.
CLI 자체는 패키지를 설치하거나 업데이트하지 않습니다.
-**0.5.0 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](#소스에서-빌드)를 사용하세요.
+**0.5.1 설치:** npm 설치·업데이트 예시를 실행하기 전에 레지스트리 제공 여부와 선택한 릴리스를 확인하세요. 해당 버전이 제공되지 않으면 [검토한 소스 빌드](#소스에서-빌드)를 사용하세요.
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
-session-peer --version # session-peer 0.5.0 (typescript)
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
+session-peer --version # session-peer 0.5.1 (typescript)
session-peer list --agent claude --json
session-peer send --to CLAUDE_PID --message 'Please review the API contract.' --dry-run --json
```
-예상 결과는 `session-peer 0.5.0 (typescript)`입니다.
+예상 결과는 `session-peer 0.5.1 (typescript)`입니다.
1. 세션을 조회하고 `CLAUDE_PID`를 대상으로 선택한 정확한 PID로 바꾸세요.
2. `--dry-run`으로 검증합니다. 이 단계에서는 메시지를 제출하지 않습니다.
@@ -177,11 +177,11 @@ npm run build
node dist/cli.js --version
npm pack --ignore-scripts
# Optional global install: first check which session-peer your PATH selects.
-npm install --global --ignore-scripts ./session-peer-0.5.0.tgz
+npm install --global --ignore-scripts ./session-peer-0.5.1.tgz
session-peer --version
```
-0.5.0에서 예상되는 결과는 `session-peer 0.5.0 (typescript)`입니다. 로컬 빌드
+0.5.1에서 예상되는 결과는 `session-peer 0.5.1 (typescript)`입니다. 로컬 빌드
산출물을 선택하려면 `./...tgz` 경로를 그대로 두세요. 이후 버전에 이 명령을 사용할
때는 먼저 체크아웃한 소스의 버전을 확인하세요.
@@ -202,7 +202,7 @@ Python CLI를 덮어쓰지 않으려면 격리 prefix에 설치·제거할 수
npm ci --ignore-scripts
npm run build
npm pack --ignore-scripts
-npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.0.tgz
+npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.1.tgz
& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version
# Later: npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer
```
@@ -446,7 +446,7 @@ Windows x64/Node 24에서 실제 TUI로 확인한 ACK는 별도의 일회성 증
## npm 릴리스
-아래 레지스트리 조회로 0.5.0의 현재 제공 여부와 npm 태그를 확인하세요. 날짜가 기록된 준비 근거는 이후 발행 결과를 입증하지 않습니다.
+아래 레지스트리 조회로 0.5.1의 현재 제공 여부와 npm 태그를 확인하세요. 날짜가 기록된 준비 근거는 이후 발행 결과를 입증하지 않습니다.
0.3.2는 2026-10-07 KST에, 0.3.1은 2026-10-04 KST에 공개·검증했습니다.
[0.3.2 공개 기록](../VALIDATION.md#public-032--2026-10-07-kst)과
@@ -457,7 +457,7 @@ Windows x64/Node 24에서 실제 TUI로 확인한 ACK는 별도의 일회성 증
설치 전에는 정확한 버전과 현재 npm 태그를 확인하세요.
```sh
-npm view session-peer@0.5.0 version dist.integrity
+npm view session-peer@0.5.1 version dist.integrity
npm view session-peer dist-tags
```
@@ -512,7 +512,7 @@ session-peer update --check --channel preview --output-format text
`updateCommand`는 실행 중인 CLI 경로로 설치 주체를 확인할 수 있을 때 사용자에게 안내합니다.
해당하는 경우는
자체 `session-peer` 실행기가 이 패키지를 가리키는 npm 전역 prefix(기본, Homebrew, nvm,
-nvm-windows, fnm. 예: `npm install --global --ignore-scripts session-peer@0.5.0`),
+nvm-windows, fnm. 예: `npm install --global --ignore-scripts session-peer@0.5.1`),
매니페스트에 `session-peer`가 선언된 pnpm·Yarn·Bun 전역 저장소, Volta, npx 캐시입니다.
프로젝트 설치(`npm_project`, `pnpm_project`), 소스 체크아웃(`source`), 그 밖의
경우(`unknown`)에는 `updateCommand: null`과 `guidance` 문장만 돌려주므로, 관련 없는 현재
@@ -608,4 +608,4 @@ native 전송 관찰은 macOS arm64의 정확한 Codex 0.160.1/0.162.1과 이미
[Observation](api.md#unreleased-local-delivery-observation--72)
-Linux·Windows의 native 주입 관측은 미지원입니다. 0.5.0 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다. 지원되는 POSIX ledger에서는 운영자가 명시적으로 확인한 수령 사실을 `operator_confirmed`로 기록할 수 있지만, 자동 ACK 수집은 아닙니다. 0.4.0 범위는 검증된 macOS 관측과 명시적 운영자 확인입니다. Linux·Windows 관측과 receipt bootstrap은 각각 [#130](https://github.com/abruption/session-peer-ts/issues/130), [#131](https://github.com/abruption/session-peer-ts/issues/131), [#132](https://github.com/abruption/session-peer-ts/issues/132)에서 후속 작업으로 관리합니다.
+Linux·Windows의 native 주입 관측은 미지원입니다. 0.5.1 소스는 선택할 수 없는 Linux arm64 Codex 0.162.1 후보(고정된 업스트림 `aarch64-unknown-linux-musl` 실행 파일, 스키마, 주입 테스트 1회)를 기록하며 Linux 관측을 활성화하지 않습니다. Linux native 관측은 계속 미지원입니다. 지원되는 POSIX ledger에서는 운영자가 명시적으로 확인한 수령 사실을 `operator_confirmed`로 기록할 수 있지만, 자동 ACK 수집은 아닙니다. 0.4.0 범위는 검증된 macOS 관측과 명시적 운영자 확인입니다. Linux·Windows 관측과 receipt bootstrap은 각각 [#130](https://github.com/abruption/session-peer-ts/issues/130), [#131](https://github.com/abruption/session-peer-ts/issues/131), [#132](https://github.com/abruption/session-peer-ts/issues/132)에서 후속 작업으로 관리합니다.
diff --git a/docs/guide.md b/docs/guide.md
index a2e4ac5..c7bdca7 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -4,11 +4,11 @@
[English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md)
-**Documentation and source version: 0.5.0.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](../VALIDATION.md); release procedures are in [RELEASING.md](../RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.6 and 0.5.0 refuse each other’s SSH preflight.
+**Documentation and source version: 0.5.1.** Review the selected release and registry metadata before installation. Dated preparation and validation records are in [VALIDATION.md](../VALIDATION.md); release procedures are in [RELEASING.md](../RELEASING.md). SSH endpoints require the same TypeScript client version; 0.3.6 and 0.5.1 refuse each other’s SSH preflight.
-**0.4.0 handoff scope:** opt-in native observation is limited to exact Codex 0.160.1/0.162.1 on macOS arm64; explicit `operator_confirmed` receipts use supported POSIX ledgers. Linux/Windows native observation and automatic ACK collection remain unsupported. The 0.5.0 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported.
+**0.4.0 handoff scope:** opt-in native observation is limited to exact Codex 0.160.1/0.162.1 on macOS arm64; explicit `operator_confirmed` receipts use supported POSIX ledgers. Linux/Windows native observation and automatic ACK collection remain unsupported. The 0.5.1 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported.
-This guide covers version 0.5.0 ([0.3.1 reliability fixes](../PARITY.md#031-reliability-fixes)). Version 0.3.0 introduced multi-host SSH, sender context and return routes, update checks and the optional `sp` shorthand ([0.3.0 changes](../PARITY.md#030-remote-usability-and-maintenance)). [Public 0.3.1 release evidence](../VALIDATION.md#public-031--2026-10-04-kst) records a prior publication. References to 0.1.0 below describe the older release.
+This guide covers version 0.5.1 ([0.3.1 reliability fixes](../PARITY.md#031-reliability-fixes)). Version 0.3.0 introduced multi-host SSH, sender context and return routes, update checks and the optional `sp` shorthand ([0.3.0 changes](../PARITY.md#030-remote-usability-and-maintenance)). [Public 0.3.1 release evidence](../VALIDATION.md#public-031--2026-10-04-kst) records a prior publication. References to 0.1.0 below describe the older release.
@@ -81,7 +81,7 @@ Side Session remains experimental. Relay/MCP are outside this comparison.
**Find and message running Claude Code and Codex sessions, locally or over SSH.**
This TypeScript client runs on Node.js without Python.
-This guide describes **version 0.5.0**. The npm package and CLI command are both **`session-peer`**. This
+This guide describes **version 0.5.1**. The npm package and CLI command are both **`session-peer`**. This
project does not provide a Relay server or hosted service.
## Quick start
@@ -89,12 +89,12 @@ project does not provide a Relay server or hosted service.
Use Node **22.13+ within 22.x or 24.x**. Check which `session-peer` your PATH
selects if the Python CLI is already installed; both packages use that command.
-**Version 0.5.0 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](#build-from-source).
+**Version 0.5.1 installation:** check registry availability and review the chosen release before running the npm install/update examples. If that version is unavailable, use the [reviewed source build](#build-from-source).
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
-session-peer --version # session-peer 0.5.0 (typescript)
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
+session-peer --version # session-peer 0.5.1 (typescript)
session-peer list --agent claude --json
session-peer send --to CLAUDE_PID --message 'Please review the API contract.' --dry-run --json
```
@@ -161,7 +161,7 @@ submits nothing. JSON adds sanitized `codexHomeResolution` and, when supplied by
native queue output, `queueId`; neither confirms consumption. See the
[selection contract](../PARITY.md#source-codex-home-selection--17--020).
Published **0.1.0** still requires an explicit live home and has no inactive opt-in.
-On SSH, both ends need the same TypeScript client version (0.5.0).
+On SSH, both ends need the same TypeScript client version (0.5.1).
## Requirements
@@ -185,11 +185,11 @@ npm run build
node dist/cli.js --version
npm pack --ignore-scripts
# Optional global install: first check which session-peer your PATH selects.
-npm install --global --ignore-scripts ./session-peer-0.5.0.tgz
+npm install --global --ignore-scripts ./session-peer-0.5.1.tgz
session-peer --version
```
-Expected for version 0.5.0: `session-peer 0.5.0 (typescript)`. Keep the `./...tgz` path to
+Expected for version 0.5.1: `session-peer 0.5.1 (typescript)`. Keep the `./...tgz` path to
select the locally built artifact. Check the checkout version before using
these commands for a later release.
@@ -209,7 +209,7 @@ On Windows PowerShell, inspect competing commands with
npm ci --ignore-scripts
npm run build
npm pack --ignore-scripts
-npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.0.tgz
+npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.1.tgz
& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version
# Later: npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer
```
@@ -474,7 +474,7 @@ separate approval. Licensed under [MIT](../LICENSE).
## Package release
-Public **0.3.2** was verified on 2026-10-07 KST ([immutable checkpoint](../VALIDATION.md#public-032--2026-10-07-kst)). Check current 0.5.0 availability and npm tags with the registry queries below; dated preparation evidence does not establish a later publication outcome.
+Public **0.3.2** was verified on 2026-10-07 KST ([immutable checkpoint](../VALIDATION.md#public-032--2026-10-07-kst)). Check current 0.5.1 availability and npm tags with the registry queries below; dated preparation evidence does not establish a later publication outcome.
Version 0.2.1 was published and verified on 2026-09-29 KST and 0.2.0 on
2026-09-28 KST; see the [0.2.1 public release record](../VALIDATION.md#public-021--2026-09-29-kst).
@@ -482,7 +482,7 @@ Later releases add their own dated records. Check the exact version and current
tags before installing:
```sh
-npm view session-peer@0.5.0 version dist.integrity
+npm view session-peer@0.5.1 version dist.integrity
npm view session-peer dist-tags
```
@@ -534,7 +534,7 @@ and reports `current`, `latest`, `channel` (`latest` by default, or `preview`),
installation's owner is positively identified from the running CLI's path:
an npm global prefix whose own `session-peer` launcher points at this package
(default, Homebrew, nvm, nvm-windows and fnm prefixes), for example
-`npm install --global --ignore-scripts session-peer@0.5.0`; a pnpm, Yarn or Bun
+`npm install --global --ignore-scripts session-peer@0.5.1`; a pnpm, Yarn or Bun
global store whose manifest declares `session-peer`; Volta; or the npx cache.
Project installs (`npm_project`, `pnpm_project`), source checkouts (`source`)
and anything else (`unknown`) get `updateCommand: null` and a `guidance`
@@ -635,4 +635,4 @@ Native observation supports exact Codex 0.160.1/0.162.1 on macOS arm64 with an a
[Observation](api.md#unreleased-local-delivery-observation--72)
-Native delivery observation is unsupported on Linux and Windows. The 0.5.0 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported. The supported POSIX ledger can record an explicitly verified receipt as `operator_confirmed`; this is not automatic ACK collection. The 0.4.0 scope is qualified macOS observation and explicit operator confirmation. Linux, Windows and receipt-bootstrap extensions are tracked separately in [#130](https://github.com/abruption/session-peer-ts/issues/130), [#131](https://github.com/abruption/session-peer-ts/issues/131) and [#132](https://github.com/abruption/session-peer-ts/issues/132).
+Native delivery observation is unsupported on Linux and Windows. The 0.5.1 source records a non-selectable Linux arm64 Codex 0.162.1 candidate (pinned upstream `aarch64-unknown-linux-musl` executable, schema and one injection test); it does not enable Linux observation. Linux native observation remains unsupported. The supported POSIX ledger can record an explicitly verified receipt as `operator_confirmed`; this is not automatic ACK collection. The 0.4.0 scope is qualified macOS observation and explicit operator confirmation. Linux, Windows and receipt-bootstrap extensions are tracked separately in [#130](https://github.com/abruption/session-peer-ts/issues/130), [#131](https://github.com/abruption/session-peer-ts/issues/131) and [#132](https://github.com/abruption/session-peer-ts/issues/132).
diff --git a/docs/guide.zh-CN.md b/docs/guide.zh-CN.md
index 8d190e1..3203203 100644
--- a/docs/guide.zh-CN.md
+++ b/docs/guide.zh-CN.md
@@ -4,11 +4,11 @@
[English](guide.md) | [한국어](guide.ko.md) | [日本語](guide.ja.md) | [简体中文](guide.zh-CN.md)
-**文档和源码版本:0.5.0。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](../VALIDATION.md),发布流程见 [RELEASING.md](../RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.6 与 0.5.0 会拒绝彼此的 SSH 预检。
+**文档和源码版本:0.5.1。** 安装前,请审核所选发布版本和注册表元数据。按日期记录的准备与验证结果见 [VALIDATION.md](../VALIDATION.md),发布流程见 [RELEASING.md](../RELEASING.md)。SSH 两端必须使用相同的 TypeScript 客户端版本;0.3.6 与 0.5.1 会拒绝彼此的 SSH 预检。
-**0.4.0 handoff 范围:** 可选 native 观测仅限 macOS arm64 上精确 Codex 0.160.1/0.162.1 版本;明确的 `operator_confirmed` 收件确认使用受支持的 POSIX ledger。Linux、Windows 的 native 观测和自动 ACK 收集仍不支持。 0.5.0 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。
+**0.4.0 handoff 范围:** 可选 native 观测仅限 macOS arm64 上精确 Codex 0.160.1/0.162.1 版本;明确的 `operator_confirmed` 收件确认使用受支持的 POSIX ledger。Linux、Windows 的 native 观测和自动 ACK 收集仍不支持。 0.5.1 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。
-这是 0.5.0 详细指南([0.3.1 可靠性修复](../PARITY.md#031-reliability-fixes))。0.3.0 新增了多主机 SSH、发送者信息与回复路由、更新检查以及可选的 `sp` 简写([0.3.0 变更](../PARITY.md#030-remote-usability-and-maintenance))。此前的发布记录见 [0.3.1 公开发布证据](../VALIDATION.md#public-031--2026-10-04-kst);下文 0.1.0 的说明用于旧版本对比。
+这是 0.5.1 详细指南([0.3.1 可靠性修复](../PARITY.md#031-reliability-fixes))。0.3.0 新增了多主机 SSH、发送者信息与回复路由、更新检查以及可选的 `sp` 简写([0.3.0 变更](../PARITY.md#030-remote-usability-and-maintenance))。此前的发布记录见 [0.3.1 公开发布证据](../VALIDATION.md#public-031--2026-10-04-kst);下文 0.1.0 的说明用于旧版本对比。
@@ -44,7 +44,7 @@
向正在运行的 **Claude Code 和 Codex 会话**发送消息,支持本机和跨机器 SSH。这是运行于 Node.js 的 TypeScript 客户端,不需要 Python。
-**这是 0.5.0 使用指南。**包名为 `session-peer`,CLI 命令为 **`session-peer`**。本项目不提供 Relay 服务器或托管服务。
+**这是 0.5.1 使用指南。**包名为 `session-peer`,CLI 命令为 **`session-peer`**。本项目不提供 Relay 服务器或托管服务。
## 功能与边界
@@ -61,7 +61,7 @@
构建后,`node dist/cli.js list --json` 合并列出 Claude/Codex,`list --agent codex --json` 查找已知 home。公开 npm **0.1.0** 仍要求显式 agent,以及 Codex 列表所需的 home;0.2.0 也支持显式命令格式。
-范围仅限默认 `~/.codex`、`CODEX_HOME`、macOS Orca 下的直接账户 home,以及 JSON 数组 `SESSION_PEER_CODEX_HOMES`。`--codex-home` 固定 Codex 列表并绕过无关配置错误;`--agent claude` 完全跳过 Codex 探索。同一 home 的别名合并,不同 home 的相同 UUID 保留。发送时使用每行的 `codexHome`。可选 home 缺失不是错误;显式 home 缺失或无效会保留成功读取的行,并返回退出码 1。列表不会选择 writer 或提交消息。[列表契约](../PARITY.md#source-unified-listing-contract--16--020)说明排序、诊断、`--all` 和 SSH 行为。0.2.0的 Codex 发送在省略 `--codex-home` 时选择唯一且稳定的 live writer;显式 home 仍会检查所有已知竞争 home。非活动队列提交需要已保存的线程、所有候选均确认非活动,以及 `--codex-home HOME --allow-inactive-codex-home`,不会执行 wake/resume。Dry-run 不提交。JSON 新增诊断 `codexHomeResolution`,且仅在原生输出提供时包含 `queueId`;两者均不代表消费确认。请参阅[选择契约](../PARITY.md#source-codex-home-selection--17--020)。公开 **0.1.0** 仍需要显式 live home,且没有非活动许可选项。SSH 两端需要相同的 TypeScript 客户端版本(0.5.0)。
+范围仅限默认 `~/.codex`、`CODEX_HOME`、macOS Orca 下的直接账户 home,以及 JSON 数组 `SESSION_PEER_CODEX_HOMES`。`--codex-home` 固定 Codex 列表并绕过无关配置错误;`--agent claude` 完全跳过 Codex 探索。同一 home 的别名合并,不同 home 的相同 UUID 保留。发送时使用每行的 `codexHome`。可选 home 缺失不是错误;显式 home 缺失或无效会保留成功读取的行,并返回退出码 1。列表不会选择 writer 或提交消息。[列表契约](../PARITY.md#source-unified-listing-contract--16--020)说明排序、诊断、`--all` 和 SSH 行为。0.2.0的 Codex 发送在省略 `--codex-home` 时选择唯一且稳定的 live writer;显式 home 仍会检查所有已知竞争 home。非活动队列提交需要已保存的线程、所有候选均确认非活动,以及 `--codex-home HOME --allow-inactive-codex-home`,不会执行 wake/resume。Dry-run 不提交。JSON 新增诊断 `codexHomeResolution`,且仅在原生输出提供时包含 `queueId`;两者均不代表消费确认。请参阅[选择契约](../PARITY.md#source-codex-home-selection--17--020)。公开 **0.1.0** 仍需要显式 live home,且没有非活动许可选项。SSH 两端需要相同的 TypeScript 客户端版本(0.5.1)。
### 0.2.0 CLI 易用性
@@ -83,15 +83,15 @@ macOS、Linux 或 Windows native;Node **22.x 中的 22.13 及以上,或 24.x
使用 Node 22.x 的 22.13 及以上或 24.x,并确认要安装的确切版本是否已发布。Python CLI 也使用同一个命令名;若已有安装,请先检查 PATH。
-**安装 0.5.0:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](#从源码构建)。
+**安装 0.5.1:** 运行 npm 安装或更新示例前,请确认注册表可用性并审核所选发布版本。如该版本不可用,请使用[已审核的源码构建](#从源码构建)。
```sh
-npm view session-peer@0.5.0 version dist.integrity
-npm install --global --ignore-scripts session-peer@0.5.0
+npm view session-peer@0.5.1 version dist.integrity
+npm install --global --ignore-scripts session-peer@0.5.1
session-peer --version
```
-预期输出:`session-peer 0.5.0 (typescript)`。
+预期输出:`session-peer 0.5.1 (typescript)`。
1. 用 `session-peer list --agent claude --json` 查找目标并选择准确 PID。
2. 用 `session-peer send --to CLAUDE_PID --message 'Please reply after checking.' --dry-run --json` 验证,不提交。
@@ -110,7 +110,7 @@ npm run build
node dist/cli.js --version
npm pack --ignore-scripts
# 可选:全局安装前先检查 PATH 选择的现有命令
-npm install --global --ignore-scripts ./session-peer-0.5.0.tgz
+npm install --global --ignore-scripts ./session-peer-0.5.1.tgz
session-peer --version
```
@@ -120,7 +120,7 @@ session-peer --version
安装前后用 `type -a session-peer` 和 `command -v session-peer` 检查实际执行项。其他实现也可能提供同名命令;请选择 PATH 上的一种,或显式运行 `node /absolute/path/dist/cli.js`。不要用 `--force` 覆盖其他管理器的文件。本包不会自动安装、删除或调整 Python 包、技能或服务。卸载使用 `npm uninstall --global session-peer`,随后再次检查 PATH。
-在 Windows PowerShell 中用 `Get-Command session-peer -All` 检查已有命令。为了不替换 Python CLI,可先执行 `npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts`,再用 `npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.0.tgz` 安装到隔离目录。以 `& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` 验证,并可用 `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` 卸载。
+在 Windows PowerShell 中用 `Get-Command session-peer -All` 检查已有命令。为了不替换 Python CLI,可先执行 `npm ci --ignore-scripts`、`npm run build`、`npm pack --ignore-scripts`,再用 `npm install --prefix "$env:TEMP\session-peer-ts-source" --ignore-scripts .\session-peer-0.5.1.tgz` 安装到隔离目录。以 `& "$env:TEMP\session-peer-ts-source\node_modules\.bin\session-peer.cmd" --version` 验证,并可用 `npm uninstall --prefix "$env:TEMP\session-peer-ts-source" session-peer` 卸载。
## 使用
@@ -211,14 +211,14 @@ Python 只用作开发时的比较基准(v1.1.0,`8ca77cf31254c8287a2593fe484
## npm 版本
-公开 **0.3.2** 于 2026-10-07 KST 完成验证([不可变公开记录](../VALIDATION.md#public-032--2026-10-07-kst))。请用下方注册表查询确认 0.5.0 的当前可用性和 npm 标签;带日期的准备证据不能证明后续发布结果。
+公开 **0.3.2** 于 2026-10-07 KST 完成验证([不可变公开记录](../VALIDATION.md#public-032--2026-10-07-kst))。请用下方注册表查询确认 0.5.1 的当前可用性和 npm 标签;带日期的准备证据不能证明后续发布结果。
0.2.1 已于 2026-09-29 KST、0.2.0 已于 2026-09-28 KST 公开并验证。请参阅
[0.2.1 公开记录](../VALIDATION.md#public-021--2026-09-29-kst)(之后的版本各自追加带日期的记录),
安装前核对准确版本与当前标签。
```sh
-npm view session-peer@0.5.0 version dist.integrity
+npm view session-peer@0.5.1 version dist.integrity
npm view session-peer dist-tags
```
@@ -257,7 +257,7 @@ dist-tag(3 秒超时,不重试),并报告 `current`、`latest`、`channe
`ahead`)、`managedBy`、`updateCommand` 和 `guidance`。只有根据当前 CLI 路径能确切识别
安装的管理方时才给出命令:其自身 `session-peer` 启动器指向本包的 npm 全局 prefix
(默认、Homebrew、nvm、nvm-windows、fnm,例如
-`npm install --global --ignore-scripts session-peer@0.5.0`),清单中声明了
+`npm install --global --ignore-scripts session-peer@0.5.1`),清单中声明了
`session-peer` 的 pnpm、Yarn、Bun 全局存储,Volta,或 npx 缓存。项目安装
(`npm_project`、`pnpm_project`)、源码检出(`source`)以及其他情况(`unknown`)只返回
`updateCommand: null` 和一句 `guidance`,因此不会给出可能修改无关当前目录的命令。它只报告
@@ -339,4 +339,4 @@ native 观测仅支持 macOS arm64 上的精确 Codex 0.160.1/0.162.1 和已有
[Observation](api.md#unreleased-local-delivery-observation--72)
-Linux、Windows 的 native 注入观察尚不支持。 0.5.0 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。支持的 POSIX ledger 可将操作人员明确确认的收件事实记录为 `operator_confirmed`,但这不是自动 ACK 收集。0.4.0 范围是已验证的 macOS 观察和操作人员的明确确认。Linux、Windows 观察及 receipt bootstrap 分别作为 [#130](https://github.com/abruption/session-peer-ts/issues/130)、[#131](https://github.com/abruption/session-peer-ts/issues/131)、[#132](https://github.com/abruption/session-peer-ts/issues/132) 的后续工作。
+Linux、Windows 的 native 注入观察尚不支持。 0.5.1 源码记录了不可选用的 Linux arm64 Codex 0.162.1 候选(固定的上游 `aarch64-unknown-linux-musl` 可执行文件、schema 及一次注入测试),但不启用 Linux 观测;Linux native 观测仍不支持。支持的 POSIX ledger 可将操作人员明确确认的收件事实记录为 `operator_confirmed`,但这不是自动 ACK 收集。0.4.0 范围是已验证的 macOS 观察和操作人员的明确确认。Linux、Windows 观察及 receipt bootstrap 分别作为 [#130](https://github.com/abruption/session-peer-ts/issues/130)、[#131](https://github.com/abruption/session-peer-ts/issues/131)、[#132](https://github.com/abruption/session-peer-ts/issues/132) 的后续工作。
diff --git a/docs/shorthand.md b/docs/shorthand.md
index 5ba29bf..d25cc83 100644
--- a/docs/shorthand.md
+++ b/docs/shorthand.md
@@ -1,6 +1,6 @@
# Optional `sp` shorthand
-This reference describes source/package version 0.5.0. Check registry availability before using a version-pinned npm installation; before publication use a reviewed source artifact. The shorthand forwards to the explicitly selected CLI and does not install or upgrade it.
+This reference describes source/package version 0.5.1. Check registry availability before using a version-pinned npm installation; before publication use a reviewed source artifact. The shorthand forwards to the explicitly selected CLI and does not install or upgrade it.
[README](../README.md) · [User guide](guide.md)
diff --git a/package-lock.json b/package-lock.json
index 176e0dc..678057f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "session-peer",
- "version": "0.5.0",
+ "version": "0.5.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "session-peer",
- "version": "0.5.0",
+ "version": "0.5.1",
"license": "MIT",
"os": [
"darwin",
@@ -15,7 +15,7 @@
],
"dependencies": {
"fs-ext-extra-prebuilt": "2.2.14",
- "smol-toml": "1.9.0"
+ "smol-toml": "1.9.1"
},
"bin": {
"session-peer": "dist/cli.js"
@@ -58,9 +58,9 @@
"license": "MIT"
},
"node_modules/smol-toml": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz",
- "integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==",
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.1.tgz",
+ "integrity": "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">= 18"
diff --git a/package.json b/package.json
index eecc421..ff97596 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "session-peer",
- "version": "0.5.0",
+ "version": "0.5.1",
"private": false,
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -50,6 +50,6 @@
},
"dependencies": {
"fs-ext-extra-prebuilt": "2.2.14",
- "smol-toml": "1.9.0"
+ "smol-toml": "1.9.1"
}
}
diff --git a/scripts/release.mjs b/scripts/release.mjs
index 7266ce8..e66dd91 100644
--- a/scripts/release.mjs
+++ b/scripts/release.mjs
@@ -8,9 +8,9 @@ import { fileURLToPath } from 'node:url';
export const repository = 'abruption/session-peer-ts';
export const registry = 'https://registry.npmjs.org';
-const stableVersion = '0.5.0';
-const previousStable = '0.4.0';
-const previousStableIntegrity = 'sha512-mMNJ118atoflhpFTkf2+DtO7MRf7FQnPK3lKWbq/vd8Xvb5DsjUtAgydIBOcd3Xj9BCYGqM50aFhzUr4dDGHSw==';
+const stableVersion = '0.5.1';
+const previousStable = '0.5.0';
+const previousStableIntegrity = 'sha512-lLc7SFRUydY5TjcX8aE18kgfC88Wi4Mx/OXcbLlk0W9yH+JYLkxGwmZIiC7S1bczFgXzsP3xSe8DqiD7YplpDw==';
const verifiedPreview = '0.1.0-preview.1';
const verifiedPreviewIntegrity = 'sha512-h4SMvrQ/LWA9osd4EHIs9rSTqv1u+S3MXQAmn+yG/gZ9+7NwYMutq+Oa5K/0ggIq61Wfwdh11Cv+5YdEffiNMA==';
export const packageFiles = ['UNICODE-LICENSE.txt', 'dist/casefold.js', 'dist/casefold.d.ts', 'dist/help.js', 'dist/help.d.ts', 'dist/output.js', 'dist/output.d.ts', 'CONTRIBUTING.md', 'LICENSE', 'PARITY.md', 'README.ja.md', 'README.ko.md',
@@ -30,7 +30,7 @@ export function validatePackage(pkg) {
assert.deepEqual(pkg.exports, { '.': { types: './dist/index.d.ts', import: './dist/index.js' } });
assert.equal(pkg.private, false);
assert.ok(pkg.version === stableVersion || /^\d+\.\d+\.\d+-preview\.\d+$/.test(pkg.version),
- 'only reviewed preview versions and 0.5.0 stable are supported');
+ 'only reviewed preview versions and 0.5.1 stable are supported');
assert.deepEqual(pkg.bin, { 'session-peer': 'dist/cli.js' });
assert.equal(pkg.repository.url, `git+https://github.com/${repository}.git`);
assert.deepEqual(pkg.publishConfig, { registry: `${registry}/`, access: 'public',
diff --git a/src/protocol.ts b/src/protocol.ts
index e98d811..b1f4893 100644
--- a/src/protocol.ts
+++ b/src/protocol.ts
@@ -1,7 +1,7 @@
import { isIPv6 } from 'node:net';
import { Refusal } from './discovery.js';
import { uuid } from './writer.js';
-export const VERSION = '0.5.0';
+export const VERSION = '0.5.1';
export const VERSION_LINE = `session-peer ${VERSION} (typescript)`;
// Return the OpenSSH destination: [user@]name, or an IPv6 literal with any
// brackets removed (`ssh` does not strip `[...]` outside ssh:// URIs). Zone IDs
diff --git a/src/skill-metadata.ts b/src/skill-metadata.ts
index fd23725..6cc3442 100644
--- a/src/skill-metadata.ts
+++ b/src/skill-metadata.ts
@@ -11,15 +11,16 @@ const verdict = (compatible: boolean): SkillCheck => ({ status: compatible ? 'co
code: compatible ? 'skill_contract_compatible' : 'skill_contract_mismatch', verification: 'metadata_only' });
export const SKILL_METADATA_LIMIT = 65536;
const keys = ['version', 'runtime-implementation', 'runtime-min-version', 'runtime-full-version', 'runtime-capability-policy'] as const;
-// Preserve each published runtime/profile result. The 0.5.0 release candidate
-// carries forward only the four exact profiles accepted by public 0.4.0.
+// Preserve each published runtime/profile result. The 0.5.1 release candidate
+// carries forward only the four exact profiles accepted by public 0.5.0.
// Metadata compatibility does not authenticate content or publish/install a skill.
// Every later runtime/profile requires separate review; no version ranges.
const matrix: Record = {
'0.3.2': ['0.1.0'], '0.3.3': ['0.1.0', '0.2.0'], '0.3.4': ['0.1.0', '0.2.0'],
'0.3.5': ['0.1.0', '0.2.0', '0.2.1'], '0.3.6': ['0.1.0', '0.2.0', '0.2.1'],
'0.4.0': ['0.1.0', '0.2.0', '0.2.1', '0.3.0'],
- '0.5.0': ['0.1.0', '0.2.0', '0.2.1', '0.3.0']
+ '0.5.0': ['0.1.0', '0.2.0', '0.2.1', '0.3.0'],
+ '0.5.1': ['0.1.0', '0.2.0', '0.2.1', '0.3.0']
};
const profiles: Record = {
'0.1.0': ['0.1.0', 'typescript', '0.1.0', '0.1.0', 'probe-help'],
diff --git a/test/diagnostics.test.ts b/test/diagnostics.test.ts
index f53ad1b..915a635 100644
--- a/test/diagnostics.test.ts
+++ b/test/diagnostics.test.ts
@@ -227,3 +227,28 @@ test('doctor distinguishes nested sqlite_home keys from an escaped root override
assert.equal(result.sendAuthorized, false);
}
});
+
+
+test('doctor rejects repeated exponent signs anywhere in Codex configuration', async t => {
+ const f = fixture(t); f.db();
+ const config = join(f.home, 'config.toml');
+ // Unrelated numeric settings are still part of the complete TOML document.
+ // A rejected document cannot establish that default SQLite storage is in use.
+ for (const number of ['3e+20', '3e-20']) {
+ writeFileSync(config, `numeric_setting = ${number}\n[profiles.p]\nweight = ${number}\n`);
+ const result = await diagnoseCodex(f.home, process.execPath) as any;
+ assert.equal(result.homes[0].status, 'available', number);
+ assert.equal(result.homes[0].code, 'state_db_readable', number);
+ assert.equal(result.ready, true); assert.equal(result.tool.executed, false);
+ }
+ for (const number of ['3e++20', '3e--20']) {
+ for (const text of [`numeric_setting = ${number}\n`, `[profiles.p]\nweight = ${number}\n`]) {
+ writeFileSync(config, text);
+ const result = await diagnoseCodex(f.home, process.execPath) as any;
+ assert.equal(result.homes[0].status, 'unknown', text);
+ assert.equal(result.homes[0].code, 'codex_config_unreadable', text);
+ assert.equal(result.ready, false); assert.equal(result.tool.executed, false);
+ assert.equal(result.sendAuthorized, false);
+ }
+ }
+});
diff --git a/test/integration-multihost-updates.test.ts b/test/integration-multihost-updates.test.ts
index e50252e..9374c9a 100644
--- a/test/integration-multihost-updates.test.ts
+++ b/test/integration-multihost-updates.test.ts
@@ -21,7 +21,7 @@ async function fixture(t: TestContext) {
const requests: string[] = [];
const server = createServer((request, response) => {
requests.push(request.url ?? '');
- response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ latest: '0.5.1' }));
+ response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify({ latest: '0.5.2' }));
});
server.listen(0, '127.0.0.1'); await once(server, 'listening');
t.after(async () => {
@@ -38,7 +38,7 @@ const args=process.argv.slice(2);
if(args.includes('-G')){console.log('hostname x\\nuser config-user\\nport 22');process.exit(0);}
const dest=args[args.indexOf('--')+1];
if((process.env.FAKE_DOWN||'').split(',').includes(dest)){console.error('ssh: connect to host: Connection refused');process.exit(255);}
-if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}
+if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}
const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:fs.readFileSync(0,'utf8'),encoding:'utf8',
env:{...process.env,SESSION_PEER_CACHE_DIR:${JSON.stringify(receiver)},SESSION_PEER_UPDATE_NOTICE:'1'}});
// FAKE_INJECT: a receiver response carrying a forged client-only field.
@@ -58,7 +58,7 @@ process.stdout.write(out);process.exit(r.status);`, { mode: 0o700 });
};
const seed = (age = 0) => {
mkdirSync(cache, { recursive: true, mode: 0o700 });
- writeFileSync(file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.5.1', checkedAt: Date.now() - age }), { mode: 0o600 });
+ writeFileSync(file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.5.2', checkedAt: Date.now() - age }), { mode: 0o600 });
};
const settled = async (count: number) => {
for (let i = 0; i < 200 && !(requests.length >= count && existsSync(file) && !existsSync(lock)); i++) await delay(50);
@@ -86,7 +86,7 @@ test('repeated --host keeps the request-order array exactly; the notice is clien
// One destination stays flat and gets the additive client field.
const single = await f.call(['list', '--agent', 'claude', '--host', 'alpha', '--json']);
const flat = JSON.parse(single.stdout);
- assert.equal(Array.isArray(flat), false); assert.equal(flat.host, 'alpha'); assert.equal(flat.clientUpdate.latest, '0.5.1');
+ assert.equal(Array.isArray(flat), false); assert.equal(flat.host, 'alpha'); assert.equal(flat.clientUpdate.latest, '0.5.2');
await delay(500);
assert.deepEqual(f.requests, [], 'fresh cache: no refresh'); assert.equal(existsSync(f.receiver), false);
});
@@ -98,7 +98,7 @@ test('each invocation schedules at most one refresh regardless of host count; re
for (const item of JSON.parse(first.stdout)) assert.equal('clientUpdate' in item, false);
await f.settled(1);
assert.equal(f.requests.length, 1, 'one refresh for three hosts');
- assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.1');
+ assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.2');
f.seed(25 * 3600_000);
await f.call(f.args(['--output-format', 'text']));
await f.settled(2);
@@ -118,7 +118,7 @@ test('failure policy: per-host results (including failed ones) may carry the adv
const on = await f.call(['list', '--agent', 'claude', '--host', 'alpha', '--json'], down);
const offValue = JSON.parse(off.stdout), { clientUpdate, ...onValue } = JSON.parse(on.stdout);
assert.equal(offValue.ok, false); assert.equal(offValue.error, 'ssh_unreachable');
- assert.equal(on.code, off.code); assert.deepEqual(onValue, offValue); assert.equal(clientUpdate.latest, '0.5.1');
+ assert.equal(on.code, off.code); assert.deepEqual(onValue, offValue); assert.equal(clientUpdate.latest, '0.5.2');
// Mixed array: element shape and exit code unchanged; text gets one stderr line for the invocation.
const mixedOff = await f.call(f.args(['--json', '--no-update-notice']), down), mixedOn = await f.call(f.args(['--json']), down);
assert.equal(mixedOn.code, mixedOff.code); assert.deepEqual(JSON.parse(mixedOn.stdout), JSON.parse(mixedOff.stdout));
@@ -152,7 +152,7 @@ test('clientUpdate is client-local: a remote-supplied value is dropped', posix,
assert.equal(localRun.code, 0);
const { clientUpdate: own, ...local } = JSON.parse(localRun.stdout);
accepted(local, 'alpha');
- assert.equal(own.source, 'npm_registry_cache'); assert.equal(own.latest, '0.5.1'); assert.notEqual(own.command, 'fixture-only');
+ assert.equal(own.source, 'npm_registry_cache'); assert.equal(own.latest, '0.5.2'); assert.notEqual(own.command, 'fixture-only');
const text = await f.call(f.args(['--output-format', 'text', '--no-update-notice']), inject);
assert.equal(text.code, 0); assert.doesNotMatch(text.stdout, /fixture-only|Error:/); assert.equal(text.stderr, '');
assert.deepEqual(text.stdout.match(/^Host: .+$/gm), hosts.map(item => `Host: ${item}`));
diff --git a/test/package-smoke.ts b/test/package-smoke.ts
index af05a27..c3f41a0 100644
--- a/test/package-smoke.ts
+++ b/test/package-smoke.ts
@@ -14,7 +14,7 @@ try {
const metadata = JSON.parse(readFileSync('package.json', 'utf8'));
assert.equal(metadata.private, false);
assert.deepEqual(metadata.publishConfig, { registry: 'https://registry.npmjs.org/', access: 'public', tag: 'latest' });
- assert.deepEqual(metadata.dependencies, { 'fs-ext-extra-prebuilt': '2.2.14', 'smol-toml': '1.9.0' });
+ assert.deepEqual(metadata.dependencies, { 'fs-ext-extra-prebuilt': '2.2.14', 'smol-toml': '1.9.1' });
for (const hook of ['preinstall', 'install', 'postinstall', 'prepare', 'prepack']) {
assert.equal(metadata.scripts[hook], undefined);
}
diff --git a/test/release.test.ts b/test/release.test.ts
index 9aaf2d9..b2f8cba 100644
--- a/test/release.test.ts
+++ b/test/release.test.ts
@@ -49,13 +49,13 @@ test('release gate rejects moved main, absent/failed/pending CI and missing envi
assert.throws(() => validateGitHubGate(sha, main, runs, { name: 'npm', protection_rules: [{ type: 'required_reviewers', reviewers: [] }] }));
});
-test('0.5.0 stable staging requires verified 0.4.0 and unchanged preview/tag baselines', () => {
+test('0.5.1 stable staging requires verified 0.5.0 and unchanged preview/tag baselines', () => {
const previewDist = { integrity: 'sha512-h4SMvrQ/LWA9osd4EHIs9rSTqv1u+S3MXQAmn+yG/gZ9+7NwYMutq+Oa5K/0ggIq61Wfwdh11Cv+5YdEffiNMA==',
attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.1.0-preview.1' } };
- const stableDist = { integrity: 'sha512-mMNJ118atoflhpFTkf2+DtO7MRf7FQnPK3lKWbq/vd8Xvb5DsjUtAgydIBOcd3Xj9BCYGqM50aFhzUr4dDGHSw==',
- attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.4.0' } };
- const current = { name: pkg.name, versions: { '0.4.0': { dist: stableDist }, '0.1.0-preview.1': { dist: previewDist } },
- 'dist-tags': { latest: '0.4.0', preview: '0.1.0-preview.1' } };
+ const stableDist = { integrity: 'sha512-lLc7SFRUydY5TjcX8aE18kgfC88Wi4Mx/OXcbLlk0W9yH+JYLkxGwmZIiC7S1bczFgXzsP3xSe8DqiD7YplpDw==',
+ attestations: { url: 'https://registry.npmjs.org/-/npm/v1/attestations/session-peer@0.5.0' } };
+ const current = { name: pkg.name, versions: { '0.5.0': { dist: stableDist }, '0.1.0-preview.1': { dist: previewDist } },
+ 'dist-tags': { latest: '0.5.0', preview: '0.1.0-preview.1' } };
validateRegistryState(pkg, 'stable-stage', current);
assert.throws(() => validateRegistryState(pkg, 'stable-stage', null));
assert.throws(() => validateRegistryState(pkg, 'trusted-stage', current));
@@ -71,10 +71,10 @@ test('0.5.0 stable staging requires verified 0.4.0 and unchanged preview/tag bas
versions: { ...current.versions, '0.1.0-preview.1': { dist: { ...previewDist, attestations: undefined } } } }));
const preview = { ...pkg, version: '0.1.0-preview.2', publishConfig: { ...pkg.publishConfig, tag: 'preview' } };
assert.throws(() => validateRegistryState(preview, 'trusted-stage', { ...current, 'dist-tags': { latest: '0.1.0-preview.0' } }), /latest_points_to_prerelease/);
- assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.4.0': { dist: { ...stableDist, integrity: 'tampered' } } } }), /verified_stable_changed/);
- assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.4.0': { dist: { ...stableDist, attestations: undefined } } } }));
+ assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.5.0': { dist: { ...stableDist, integrity: 'tampered' } } } }), /verified_stable_changed/);
+ assert.throws(() => validateRegistryState(pkg, 'stable-stage', { ...current, versions: { ...current.versions, '0.5.0': { dist: { ...stableDist, attestations: undefined } } } }));
validateRegistryState(preview, 'trusted-stage', { ...current,
- 'dist-tags': { latest: '0.4.0', preview: '0.1.0-preview.1' } });
+ 'dist-tags': { latest: '0.5.0', preview: '0.1.0-preview.1' } });
});
test('release artifact binds version, allowlist, commit, SHA-256 and registry integrity', () => {
diff --git a/test/replies.test.ts b/test/replies.test.ts
index 3046f6f..bedc2f4 100644
--- a/test/replies.test.ts
+++ b/test/replies.test.ts
@@ -141,7 +141,7 @@ if(args.at(-1)==='exit 0'){const mode=process.env.FAKE_PROBE;
if(mode==='timeout'){console.error('ssh: connect to host origin port 22: Operation timed out SECRET-SENTINEL');process.exit(255);}
console.error('SECRET-SENTINEL');process.exit(1);}
if((process.env.FAKE_DOWN||'').split(',').includes(dest)){console.error('Connection refused');process.exit(255);}
-if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}
+if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}
const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:{...process.env,SESSION_PEER_TAILSCALE:'off'}});
process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
writeFileSync(join(path, 'tailscale'), `#!${process.execPath}
diff --git a/test/reply-agent-binding.test.ts b/test/reply-agent-binding.test.ts
index 5201985..31162fd 100644
--- a/test/reply-agent-binding.test.ts
+++ b/test/reply-agent-binding.test.ts
@@ -45,7 +45,7 @@ function fixture(t: TestContext) {
const fs=require('node:fs'),{spawnSync}=require('node:child_process');
fs.appendFileSync(${JSON.stringify(sshLog)},'1');
if(process.argv.includes('-G')){console.log('user fixture-user');process.exit(0);}
-if(process.argv.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}
+if(process.argv.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}
const input=fs.readFileSync(0,'utf8');
const result=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});
process.stdout.write(result.stdout);process.exit(result.status);`, { mode: 0o700 });
diff --git a/test/skill-guidance.test.ts b/test/skill-guidance.test.ts
index a0fdf21..750103b 100644
--- a/test/skill-guidance.test.ts
+++ b/test/skill-guidance.test.ts
@@ -41,9 +41,10 @@ test('published skill 0.2.1 complete bytes match the recorded release and finite
assert.deepEqual(validateSkillMetadata(text, '0.3.6'), expected);
assert.deepEqual(validateSkillMetadata(text, '0.4.0'), expected);
assert.deepEqual(validateSkillMetadata(text, '0.5.0'), expected);
+ assert.deepEqual(validateSkillMetadata(text, '0.5.1'), expected);
const path = fileURLToPath(url);
assert.deepEqual(inspectSkill(path), { ...expected, path });
- for (const runtime of ['0.3.2', '0.3.3', '0.3.4', '0.3.7', '0.4.1', '0.5.1', '0.6.0']) {
+ for (const runtime of ['0.3.2', '0.3.3', '0.3.4', '0.3.7', '0.4.1', '0.5.2', '0.6.0']) {
assert.deepEqual(validateSkillMetadata(text, runtime), {
status: 'incompatible', code: 'skill_contract_mismatch', verification: 'metadata_only'
});
diff --git a/test/skill-metadata.test.ts b/test/skill-metadata.test.ts
index 2c49e8a..1ecd45f 100644
--- a/test/skill-metadata.test.ts
+++ b/test/skill-metadata.test.ts
@@ -29,7 +29,7 @@ test('finite runtime/profile matrix preserves published 0.3.2 and 0.3.3 profiles
const legacy = fixtureText(), successor = fixtureText({ ...fields, version: '0.2.0', 'runtime-full-version': '0.3.2' });
assert.deepEqual(validateSkillMetadata(legacy, '0.3.2'), compatible);
assert.deepEqual(validateSkillMetadata(successor, '0.3.2'), incompatible);
- assert.equal(VERSION, '0.5.0');
+ assert.equal(VERSION, '0.5.1');
for (const text of [legacy, successor]) {
assert.deepEqual(validateSkillMetadata(text, '0.3.3'), compatible);
assert.deepEqual(validateSkillMetadata(text), compatible);
@@ -43,7 +43,7 @@ test('finite runtime/profile matrix preserves published 0.3.2 and 0.3.3 profiles
test('0.3.4 patch row permits only the two exact published profiles', () => {
const successor = { ...fields, version: '0.2.0', 'runtime-full-version': '0.3.2' };
- assert.equal(VERSION, '0.5.0');
+ assert.equal(VERSION, '0.5.1');
for (const values of [fields, successor]) {
assert.deepEqual(validateSkillMetadata(fixtureText(values), '0.3.4'), compatible);
for (const key of Object.keys(values)) {
@@ -63,7 +63,7 @@ test('0.3.6 release candidate carries forward only the finite 0.3.5 profiles', t
const { file } = fixture(t);
const successor = { ...fields, version: '0.2.1', 'runtime-full-version': '0.3.2' };
const text = fixtureText(successor);
- assert.equal(VERSION, '0.5.0');
+ assert.equal(VERSION, '0.5.1');
for (const values of [fields, { ...fields, version: '0.2.0', 'runtime-full-version': '0.3.2' }, successor]) {
const profile = fixtureText(values);
assert.deepEqual(validateSkillMetadata(profile, '0.3.5'), compatible);
@@ -80,12 +80,12 @@ test('0.3.6 release candidate carries forward only the finite 0.3.5 profiles', t
assert.deepEqual(validateSkillMetadata(text), compatible);
assert.deepEqual(validateSkillMetadata(text, undefined), compatible);
writeFileSync(file, text);
- // The candidate doctor uses compiled 0.5.0; explicit historical rows remain unchanged.
+ // The candidate doctor uses compiled 0.5.1; explicit historical rows remain unchanged.
assert.deepEqual(inspectSkill(file), { path: file, ...compatible });
for (const runtime of [null, {}, 0, '', '0.3.5-preview.1', '00.3.5', '0.3.6-preview.1', '00.3.6']) {
assert.deepEqual(validateSkillMetadata(text, runtime), missing);
}
- for (const runtime of ['0.3.5', '0.3.6', '0.4.0', '0.5.0']) {
+ for (const runtime of ['0.3.5', '0.3.6', '0.4.0', '0.5.0', '0.5.1']) {
for (const key of Object.keys(successor)) {
assert.deepEqual(validateSkillMetadata(fixtureText({ ...successor, [key]: 'unsupported' }), runtime), incompatible, key);
const omitted = { ...successor } as Record; delete omitted[key];
@@ -385,7 +385,7 @@ test('complete companion 0.2.1 candidate bytes match the exact reviewed fixture
assert.equal(createHash('sha256').update(bytes).digest('hex'),
'96d5a0d049f8a4d193703af3583b198a91079d44ae373de34e94a54e2296a4d6');
const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
- assert.equal(VERSION, '0.5.0');
+ assert.equal(VERSION, '0.5.1');
assert.deepEqual(validateSkillMetadata(text), compatible);
assert.deepEqual(validateSkillMetadata(text, undefined), compatible);
assert.deepEqual(validateSkillMetadata(text, '0.3.5'), compatible);
@@ -399,18 +399,19 @@ test('complete companion 0.2.1 candidate bytes match the exact reviewed fixture
});
-test('0.5.0 finite row preserves the four exact 0.4.0 profiles', t => {
+test('0.5.1 finite row preserves the four exact 0.4.0 and 0.5.0 profiles', t => {
const {file}=fixture(t);
- assert.equal(VERSION,'0.5.0');
+ assert.equal(VERSION,'0.5.1');
for(const values of [fields,{...fields,version:'0.2.0','runtime-full-version':'0.3.2'}, {...fields,version:'0.2.1','runtime-full-version':'0.3.2'},{...fields,version:'0.3.0','runtime-full-version':'0.4.0'}]){
const text=fixtureText(values);
assert.deepEqual(validateSkillMetadata(text,'0.4.0'),compatible);
assert.deepEqual(validateSkillMetadata(text,'0.5.0'),compatible);
+ assert.deepEqual(validateSkillMetadata(text,'0.5.1'),compatible);
assert.deepEqual(validateSkillMetadata(text),compatible);
assert.deepEqual(validateSkillMetadata(text,undefined),compatible);
writeFileSync(file,text);assert.deepEqual(inspectSkill(file),{path:file,...compatible});
- for(const runtime of ['0.3.7','0.4.1','0.5.1','0.6.0'])assert.deepEqual(validateSkillMetadata(text,runtime),incompatible);
- for(const runtime of [null,{},0,'','0.4.0-preview.1','00.4.0','0.5.0-preview.1','00.5.0'])assert.deepEqual(validateSkillMetadata(text,runtime),missing);
+ for(const runtime of ['0.3.7','0.4.1','0.5.2','0.6.0'])assert.deepEqual(validateSkillMetadata(text,runtime),incompatible);
+ for(const runtime of [null,{},0,'','0.4.0-preview.1','00.4.0','0.5.0-preview.1','00.5.0','0.5.1-preview.1','00.5.1'])assert.deepEqual(validateSkillMetadata(text,runtime),missing);
for(const key of Object.keys(values)){
assert.deepEqual(validateSkillMetadata(fixtureText({...values,[key]:'unsupported'})),incompatible,key);
const omitted={...values} as Record;delete omitted[key];assert.deepEqual(validateSkillMetadata(fixtureText(omitted)),missing,key);
@@ -433,17 +434,18 @@ test('0.5.0 finite row preserves the four exact 0.4.0 profiles', t => {
// Companion PR37 b8ff909 is an immutable review checkpoint, not an install pin.
-test('complete 0.3.0 companion candidate bytes match exact review and the 0.4.0 and 0.5.0 guards', t=>{
+test('complete 0.3.0 companion candidate bytes match exact review and the 0.4.0, 0.5.0 and 0.5.1 guards', t=>{
const bytes=readFileSync(new URL('./fixtures/ts-skill-0.3.0-candidate.md',import.meta.url));
assert.equal(bytes.length,20162);assert.ok(bytes.length<=SKILL_METADATA_LIMIT);
assert.equal(createHash('sha256').update(bytes).digest('hex'),'1ae3690cb5d03777bf448cf06496fed27e4a9329deb03741d6d956a3b13c5ed4');
- const text=new TextDecoder('utf-8',{fatal:true}).decode(bytes);assert.equal(VERSION,'0.5.0');
+ const text=new TextDecoder('utf-8',{fatal:true}).decode(bytes);assert.equal(VERSION,'0.5.1');
assert.deepEqual(validateSkillMetadata(text),compatible);
assert.deepEqual(validateSkillMetadata(text,undefined),compatible);
assert.deepEqual(validateSkillMetadata(text,'0.4.0'),compatible);
assert.deepEqual(validateSkillMetadata(text,'0.5.0'),compatible);
+ assert.deepEqual(validateSkillMetadata(text,'0.5.1'),compatible);
const {file}=fixture(t);writeFileSync(file,bytes);assert.deepEqual(inspectSkill(file),{path:file,...compatible});
- for(const runtime of ['0.3.2','0.3.3','0.3.4','0.3.5','0.3.6','0.3.7','0.4.1','0.5.1','0.6.0'])assert.deepEqual(validateSkillMetadata(text,runtime),incompatible);
- for(const runtime of [null,{},0,'','0.4.0-preview.1','00.4.0','0.5.0-preview.1','00.5.0'])assert.deepEqual(validateSkillMetadata(text,runtime),missing);
+ for(const runtime of ['0.3.2','0.3.3','0.3.4','0.3.5','0.3.6','0.3.7','0.4.1','0.5.2','0.6.0'])assert.deepEqual(validateSkillMetadata(text,runtime),incompatible);
+ for(const runtime of [null,{},0,'','0.4.0-preview.1','00.4.0','0.5.0-preview.1','00.5.0','0.5.1-preview.1','00.5.1'])assert.deepEqual(validateSkillMetadata(text,runtime),missing);
// Only bounded metadata is interpreted; candidate body instructions stay inert.
});
diff --git a/test/ssh-hosts.test.ts b/test/ssh-hosts.test.ts
index 2429fd2..e804781 100644
--- a/test/ssh-hosts.test.ts
+++ b/test/ssh-hosts.test.ts
@@ -32,7 +32,7 @@ fs.appendFileSync(${JSON.stringify(log)},JSON.stringify({args,input})+'\\n');
const dest=args[args.indexOf('--')+1],list=k=>(process.env[k]||'').split(',');
if(args.includes('-G')){if(process.env.FAKE_G==='fail'||list('FAKE_G_FAIL').includes(dest))process.exit(255);console.log('hostname x\\nuser config-user\\nport 22');process.exit(0);}
if(list('FAKE_DOWN').includes(dest)){console.error('ssh: connect to host: Connection refused SECRET-SENTINEL');process.exit(255);}
-if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}
+if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}
if(list('FAKE_LOSS').includes(dest))process.exit(255);
const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});
if(list('FAKE_MALFORMED_TARGET').includes(dest)){const value=JSON.parse(r.stdout);value.target.agent={toString:null};r.stdout=JSON.stringify(value);}
@@ -339,7 +339,7 @@ const proxy=args.find(a=>a.startsWith('ProxyCommand='));
if(proxy){const cmd=proxy.slice(13).replace(/%(.)/g,(m,c)=>c==='%'?'%':c==='h'?name:c==='p'?port:c==='n'?dest:c==='r'?'config-user':m);
if(spawnSync('/bin/sh',['-c','exec '+cmd],{stdio:'inherit'}).status!==0)process.exit(255);}
fs.appendFileSync(${JSON.stringify(log)},JSON.stringify({hop:false,args,input})+'\\n');
-if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}
+if(args.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}
const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});
process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
const env = { PATH: bin + delimiter + f.env.PATH };
diff --git a/test/transport.test.ts b/test/transport.test.ts
index f2021aa..c80abcc 100644
--- a/test/transport.test.ts
+++ b/test/transport.test.ts
@@ -171,20 +171,20 @@ test('SSH preflight reports allowlisted causes without leaking stderr or sending
test('SSH request framing: no message in remote command; response loss never retries', async t => {
const { path, env } = setup(t), messages = await inbox(t, path);
const ssh = join(path, 'ssh');
- writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process'); const a=process.argv.slice(2);if(a.at(-1).endsWith('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}if(a.at(-1).includes('SECRET-SENTINEL'))process.exit(99);const input=require('node:fs').readFileSync(0,'utf8');const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
+ writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process'); const a=process.argv.slice(2);if(a.at(-1).endsWith('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}if(a.at(-1).includes('SECRET-SENTINEL'))process.exit(99);const input=require('node:fs').readFileSync(0,'utf8');const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input,encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
const result = await invoke(['send', '--host', 'fixture', '--to', String(process.pid), '--message', 'SECRET-SENTINEL', '--no-from'], { ...env, PATH: path + delimiter + env.PATH });
assert.equal(result.status, 'posted'); assert.equal(messages.length, 1);
// Valid JSON mixed with banner/trailing output is not a verified response.
for (const command of ['send', 'list']) for (const prefix of [true, false]) {
const response = JSON.stringify({ schemaVersion: 1, command, host: 'fixture', ok: true, status: 'posted', submitted: true, consumptionConfirmed: false });
const stdout = prefix ? 'untrusted banner\n' + response : response + '\ntrailing junk';
- writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.5.0 (typescript)');else console.log(${JSON.stringify(stdout)});`, { mode: 0o700 });
+ writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.5.1 (typescript)');else console.log(${JSON.stringify(stdout)});`, { mode: 0o700 });
const unverified = await invoke([command, '--host', 'fixture', ...(command === 'send' ? ['--to', 'fixture', '--message', 'not-provable'] : [])], { ...env, PATH: path + delimiter + env.PATH });
assert.equal(unverified.status, command === 'send' ? 'unknown' : 'refused');
assert.equal(unverified.submitted, command === 'send' ? null : false);
assert.equal(unverified.retryAllowed, false);
}
- writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.5.0 (typescript)');else process.exit(255);`, { mode: 0o700 });
+ writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).endsWith('--version'))console.log('session-peer 0.5.1 (typescript)');else process.exit(255);`, { mode: 0o700 });
const lost = await invoke(['send', '--host', 'fixture', '--to', 'fixture', '--message', 'lost'], { ...env, PATH: path + delimiter + env.PATH });
assert.equal(lost.status, 'unknown'); assert.equal(lost.retryAllowed, false); assert.equal(messages.length, 1);
});
@@ -333,7 +333,7 @@ test('a saved inactive home needs opt-in and still rejects relocated or unreadab
test('SSH preflight accepts a verified version despite noisy stderr and propagates remote exit codes', async t => {
const { path, env } = setup(t), messages = await inbox(t, path);
const ssh = join(path, 'ssh');
- writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process');if(process.argv.at(-1).endsWith('--version')){console.error('curl: (7) Failed to connect: Connection refused');console.log('session-peer 0.5.0 (typescript)');process.exit(0);}const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:require('node:fs').readFileSync(0,'utf8'),encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
+ writeFileSync(ssh, `#!${process.execPath}\n${sshConfigUser}const {spawnSync}=require('node:child_process');if(process.argv.at(-1).endsWith('--version')){console.error('curl: (7) Failed to connect: Connection refused');console.log('session-peer 0.5.1 (typescript)');process.exit(0);}const r=spawnSync(${JSON.stringify(process.execPath)},[${JSON.stringify(cli)},'--stdio-request'],{input:require('node:fs').readFileSync(0,'utf8'),encoding:'utf8',env:process.env});process.stdout.write(r.stdout);process.exit(r.status);`, { mode: 0o700 });
const remoteEnv = { ...env, PATH: path + delimiter + env.PATH };
// #55 + #49/#51 over SSH: a flag-like body travels as --message= and posts literally.
const sent = await invoke(['send', '--host', 'fixture', '--to', String(process.pid), '--message=--dry-run', '--no-from'], remoteEnv);
@@ -374,6 +374,41 @@ test('ordinary SSH results cannot opt themselves into handoff or claim unsolicit
// Fixture values are data, never part of the executable JavaScript source.
const config={log,handoff:{...h,targetGeneration:'\');throw new Error("fixture data executed");//\u2028\u2029'}};
writeFileSync(fake+'.json',JSON.stringify(config),{mode:0o600});
- writeFileSync(fake,`#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).includes('--version')){console.log('session-peer 0.5.0 (typescript)');process.exit(0);}const fs=require('node:fs');const config=JSON.parse(fs.readFileSync(__filename+'.json','utf8'));let input='';process.stdin.on('data',p=>input+=p);process.stdin.on('end',()=>{fs.appendFileSync(config.log,'1');console.log(JSON.stringify({schemaVersion:1,command:'send',host:'fixture',ok:true,status:'posted',submitted:true,consumptionConfirmed:false,dryRun:false,target:{agent:'claude',pid:123,name:null},remoteMarker:'kept',handoff:config.handoff,handoffQuery:{status:'unknown'},handoffWarning:'fixture-only'}));});`,{mode:0o700});
+ writeFileSync(fake,`#!${process.execPath}\n${sshConfigUser}if(process.argv.at(-1).includes('--version')){console.log('session-peer 0.5.1 (typescript)');process.exit(0);}const fs=require('node:fs');const config=JSON.parse(fs.readFileSync(__filename+'.json','utf8'));let input='';process.stdin.on('data',p=>input+=p);process.stdin.on('end',()=>{fs.appendFileSync(config.log,'1');console.log(JSON.stringify({schemaVersion:1,command:'send',host:'fixture',ok:true,status:'posted',submitted:true,consumptionConfirmed:false,dryRun:false,target:{agent:'claude',pid:123,name:null},remoteMarker:'kept',handoff:config.handoff,handoffQuery:{status:'unknown'},handoffWarning:'fixture-only'}));});`,{mode:0o700});
const r=await invoke(['send','--host','fixture','--to','123','--message','fixture','--no-from','--no-reply-to'],{...env,PATH:path+delimiter+(env.PATH??'')});assert.equal(r.code,0);assert.equal(r.status,'posted');assert.equal(r.submitted,true);assert.equal(r.target.name,null);assert.equal(r.remoteMarker,'kept');assert.equal('handoff' in r,false);assert.equal('handoffQuery' in r,false);assert.equal('handoffWarning' in r,false);assert.equal(readFileSync(log,'utf8'),'1');
});
+
+
+test('malformed exponent configuration refuses Codex dry-run and send before the fixture queue', async t => {
+ const { path, env } = setup(t), home = join(path, 'home'); database(home); await holder(t, path, home);
+ const queue = join(path, 'queue'), count = join(path, 'count'), config = join(home, 'config.toml');
+ writeFileSync(count, '');
+ writeFileSync(queue, `#!${process.execPath}\nrequire('node:fs').appendFileSync(${JSON.stringify(count)},'1'); console.log('Queued message toml-051 for thread '+process.argv[4]+'.');`, { mode: 0o700 });
+ const args = ['send', '--to', `codex:${id}`, '--codex-home', home, '--codex-bin', queue, '--message', 'fixture', '--no-from', '--no-reply-to'];
+ // Valid single exponent signs remain acceptable; dry-run never invokes queue.
+ for (const number of ['3e+20', '3e-20']) {
+ writeFileSync(config, `numeric_setting = ${number}\n[profiles.p]\nweight = ${number}\n`);
+ const result = await invoke([...args, '--dry-run'], env);
+ assert.equal(result.status, 'validated', number); assert.equal(result.submitted, false);
+ assert.equal(readFileSync(count, 'utf8'), '');
+ }
+ for (const number of ['3e++20', '3e--20']) {
+ for (const text of [`numeric_setting = ${number}\n`, `[profiles.p]\nweight = ${number}\n`]) {
+ writeFileSync(config, text);
+ for (const extra of [['--dry-run'], []]) {
+ const result = await invoke([...args, ...extra], env);
+ assert.equal(result.error, 'codex_config_unreadable', text);
+ assert.equal(result.status, 'refused'); assert.equal(result.submitted, false);
+ assert.equal(result.code, 1); assert.equal(readFileSync(count, 'utf8'), '');
+ }
+ }
+ }
+ // This parser correction does not add support for an explicit root override.
+ writeFileSync(config, 'numeric_setting = 3e+20\nsqlite_home = "/fixture/relocated"\n');
+ for (const extra of [['--dry-run'], []]) {
+ const result = await invoke([...args, ...extra], env);
+ assert.equal(result.error, 'unsupported_codex_sqlite_home');
+ assert.equal(result.status, 'refused'); assert.equal(result.submitted, false);
+ assert.equal(readFileSync(count, 'utf8'), '');
+ }
+});
diff --git a/test/updates.test.ts b/test/updates.test.ts
index d541f03..6eaaa46 100644
--- a/test/updates.test.ts
+++ b/test/updates.test.ts
@@ -16,7 +16,7 @@ import { acquireLock, cacheDirectory, checkUpdate, compareVersions, manager, ref
const cli = resolve('dist/cli.js');
const SENTINEL = 'REGISTRY-BODY-SENTINEL';
type Reply = { status?: number; body?: string; wait?: number };
-async function fixture(t: TestContext, reply: Reply = { body: JSON.stringify({ latest: '0.5.1', preview: '0.6.0-preview.1' }) }) {
+async function fixture(t: TestContext, reply: Reply = { body: JSON.stringify({ latest: '0.5.2', preview: '0.6.0-preview.1' }) }) {
const root = mkdtempSync(join(process.env.TASK_TEMP ?? tmpdir(), 'codex-updates-'));
mkdirSync(join(root, '.claude/sessions'), { recursive: true });
const requests: string[] = [];
@@ -82,11 +82,11 @@ test('package-manager guidance requires a positively identified owner; uncertain
read: path => { if (!(path in files)) throw new Error('ENOENT'); return files[path]!; },
real: path => { if (path in links) return links[path]!; if (path in files) return path; throw new Error('ENOENT'); }
});
- const declared = JSON.stringify({ dependencies: { 'session-peer': '0.5.0' } });
+ const declared = JSON.stringify({ dependencies: { 'session-peer': '0.5.1' } });
const posixGlobal = (prefix: string) => probe({ [`${prefix}/lib/node_modules/session-peer/dist/cli.js`]: '' },
{ [`${prefix}/bin/session-peer`]: `${prefix}/lib/node_modules/session-peer/dist/cli.js` });
const windowsGlobal = (prefix: string) => probe({ [`${prefix}/session-peer.cmd`]: '@ECHO off\r\nnode "%dp0%\\node_modules\\session-peer\\dist\\cli.js" %*\r\n' });
- const cmd = { npm: 'npm install --global --ignore-scripts session-peer@0.5.1' };
+ const cmd = { npm: 'npm install --global --ignore-scripts session-peer@0.5.2' };
const cases: [string, NodeJS.Platform, Probe, string, string | null][] = [
['/usr/local/lib/node_modules/session-peer/dist/updates.js', 'linux', posixGlobal('/usr/local'), 'npm', cmd.npm],
['/home/u/.nvm/versions/node/v24.16.0/lib/node_modules/session-peer/dist/updates.js', 'darwin', posixGlobal('/home/u/.nvm/versions/node/v24.16.0'), 'npm', cmd.npm],
@@ -100,27 +100,27 @@ test('package-manager guidance requires a positively identified owner; uncertain
['/home/u/.nvm/versions/node/v24.16.0/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null],
['/home/u/.local/lib/node_modules/session-peer/dist/updates.js', 'linux',
probe({ '/home/u/.local/lib/node_modules/session-peer/dist/cli.js': '' }, { '/home/u/.local/bin/session-peer': '/home/u/.local/share/pipx/venvs/session-peer/bin/session-peer' }), 'unknown', null],
- ['/home/u/.volta/tools/image/packages/session-peer/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'volta', 'volta install session-peer@0.5.1'],
- ['C:/Users/u/AppData/Local/Volta/tools/image/packages/session-peer/node_modules/session-peer/dist/updates.js', 'win32', probe({}), 'volta', 'volta install session-peer@0.5.1'],
+ ['/home/u/.volta/tools/image/packages/session-peer/lib/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'volta', 'volta install session-peer@0.5.2'],
+ ['C:/Users/u/AppData/Local/Volta/tools/image/packages/session-peer/node_modules/session-peer/dist/updates.js', 'win32', probe({}), 'volta', 'volta install session-peer@0.5.2'],
['C:\\work\\app\\node_modules\\session-peer\\dist\\updates.js', 'win32', probe({ 'C:/work/app/package.json': declared }), 'npm_project', null],
['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'npm_project', null],
['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': JSON.stringify({ dependencies: { other: '1' } }) }), 'unknown', null],
['/work/app/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null],
- ['/home/u/.local/share/pnpm/global/5/node_modules/.pnpm/session-peer@0.5.0/node_modules/session-peer/dist/updates.js', 'linux',
- probe({ '/home/u/.local/share/pnpm/global/5/package.json': declared }), 'pnpm', 'pnpm add --global --ignore-scripts session-peer@0.5.1'],
- ['/work/app/node_modules/.pnpm/session-peer@0.5.0/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'pnpm_project', null],
- ['/work/app/node_modules/.pnpm/session-peer@0.5.0/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null],
- ['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/home/u/.config/yarn/global/package.json': declared }), 'yarn', 'yarn global add --ignore-scripts session-peer@0.5.1'],
+ ['/home/u/.local/share/pnpm/global/5/node_modules/.pnpm/session-peer@0.5.1/node_modules/session-peer/dist/updates.js', 'linux',
+ probe({ '/home/u/.local/share/pnpm/global/5/package.json': declared }), 'pnpm', 'pnpm add --global --ignore-scripts session-peer@0.5.2'],
+ ['/work/app/node_modules/.pnpm/session-peer@0.5.1/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/work/app/package.json': declared }), 'pnpm_project', null],
+ ['/work/app/node_modules/.pnpm/session-peer@0.5.1/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null],
+ ['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({ '/home/u/.config/yarn/global/package.json': declared }), 'yarn', 'yarn global add --ignore-scripts session-peer@0.5.2'],
['/home/u/.config/yarn/global/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'unknown', null],
- ['/home/u/.bun/install/global/node_modules/session-peer/dist/updates.js', 'darwin', probe({ '/home/u/.bun/install/global/package.json': declared }), 'bun', 'bun add --global --ignore-scripts session-peer@0.5.1'],
- ['/home/u/.npm/_npx/abc123/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'npx', 'npx --yes --ignore-scripts session-peer@0.5.1 --version'],
+ ['/home/u/.bun/install/global/node_modules/session-peer/dist/updates.js', 'darwin', probe({ '/home/u/.bun/install/global/package.json': declared }), 'bun', 'bun add --global --ignore-scripts session-peer@0.5.2'],
+ ['/home/u/.npm/_npx/abc123/node_modules/session-peer/dist/updates.js', 'linux', probe({}), 'npx', 'npx --yes --ignore-scripts session-peer@0.5.2 --version'],
['/src/session-peer-ts/dist/updates.js', 'linux', probe({ '/src/session-peer-ts/package.json': JSON.stringify({ name: 'session-peer' }) }), 'source', null],
['/src/other/dist/updates.js', 'linux', probe({}), 'unknown', null],
['/opt/unrelated/node_modules/other/updates.js', 'linux', probe({}), 'unknown', null],
];
for (const [path, platform, files, owner, command] of cases) {
assert.equal(manager(path, platform, files), owner, `${platform} ${path}`);
- assert.equal(upgradeCommand(owner as Manager, '0.5.1'), command, path);
+ assert.equal(upgradeCommand(owner as Manager, '0.5.2'), command, path);
if (command === null) assert.ok(upgradeGuidance(owner as Manager).length > 0);
}
});
@@ -157,7 +157,7 @@ test('update --check reports npm dist-tags, writes a private atomic cache and le
assert.deepEqual(f.requests, ['/-/package/session-peer/dist-tags']);
const v = r.value;
assert.equal(v.command, 'update'); assert.equal(v.ok, true); assert.equal(v.package, 'session-peer');
- assert.equal(v.current, '0.5.0'); assert.equal(v.latest, '0.5.1'); assert.equal(v.channel, 'latest'); assert.equal(v.distTag, 'latest');
+ assert.equal(v.current, '0.5.1'); assert.equal(v.latest, '0.5.2'); assert.equal(v.channel, 'latest'); assert.equal(v.distTag, 'latest');
assert.equal(v.source, 'npm_registry'); assert.equal(v.status, 'update_available'); assert.equal(v.outdated, true); assert.equal(v.updated, false);
assert.equal(v.referenceVersion, '1.1.0'); assert.equal(v.cache, 'written');
// The test runs from a checkout; installed-path guidance is covered above and in package-smoke.
@@ -168,31 +168,31 @@ test('update --check reports npm dist-tags, writes a private atomic cache and le
assert.deepEqual([readFileSync(skill), readFileSync(python)], before);
const cached = JSON.parse(readFileSync(f.file, 'utf8'));
assert.deepEqual(Object.keys(cached).sort(), ['channel', 'checkedAt', 'latest', 'package', 'schemaVersion', 'source']);
- assert.equal(cached.latest, '0.5.1');
+ assert.equal(cached.latest, '0.5.2');
assert.deepEqual(readdirSync(f.cache), ['npm-update.json']);
if (process.platform !== 'win32') {
assert.equal(statSync(f.cache).mode & 0o777, 0o700); assert.equal(statSync(f.file).mode & 0o777, 0o600);
}
const text = await f.call(['update', '--check', '--output-format', 'text']);
- assert.equal(text.code, 0); assert.match(text.stdout, /0\.5\.0 \(typescript\): 0\.5\.1 available on npm dist-tag latest/);
+ assert.equal(text.code, 0); assert.match(text.stdout, /0\.5\.1 \(typescript\): 0\.5\.2 available on npm dist-tag latest/);
assert.match(text.stdout, /source checkout/); assert.equal(text.stderr, '');
- f.state.reply = { body: JSON.stringify({ latest: '0.5.0' }) };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.1' }) };
const current = await f.call(['update', '--check', '--json']);
assert.equal(current.value.status, 'up_to_date'); assert.equal(current.value.outdated, false); assert.equal(current.value.updateCommand, null);
});
test('preview channel accepts prereleases, compares separately and does not replace the stable cache', async t => {
const f = await fixture(t);
- f.seed('0.5.0');
+ f.seed('0.5.1');
const before = readFileSync(f.file, 'utf8');
const ahead = await f.call(['update', '--check', '--channel', 'preview', '--json']);
assert.equal(ahead.code, 0); assert.equal(ahead.value.latest, '0.6.0-preview.1'); assert.equal(ahead.value.channel, 'preview');
assert.equal(ahead.value.status, 'update_available'); assert.equal(ahead.value.outdated, true); assert.equal(ahead.value.cache, 'not_applicable');
- f.state.reply = { body: JSON.stringify({ latest: '0.5.0', preview: '0.1.0-preview.1' }) };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.1', preview: '0.1.0-preview.1' }) };
const older = await f.call(['update', '--check', '--channel=preview', '--json']);
assert.equal(older.value.status, 'ahead'); assert.equal(older.value.outdated, false); assert.equal(older.value.updateCommand, null);
assert.equal(readFileSync(f.file, 'utf8'), before);
- f.state.reply = { body: JSON.stringify({ latest: '0.5.0' }) };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.1' }) };
const missing = await f.call(['update', '--check', '--channel', 'preview', '--json']);
assert.equal(missing.code, 1); assert.equal(missing.value.error, 'dist_tag_missing');
});
@@ -205,7 +205,7 @@ test('invalid, failed and slow registry responses refuse without printing bodies
[{ body: JSON.stringify({ latest: 9, note: SENTINEL }) }, 'registry_response_invalid'],
[{ body: `[${JSON.stringify(SENTINEL)}]` }, 'registry_response_invalid'],
[{ body: `${SENTINEL} not json` }, 'registry_response_invalid'],
- [{ body: JSON.stringify({ latest: '0.5.1', padding: SENTINEL.repeat(4000) }) }, 'registry_response_invalid'],
+ [{ body: JSON.stringify({ latest: '0.5.2', padding: SENTINEL.repeat(4000) }) }, 'registry_response_invalid'],
[{ status: 500, body: SENTINEL }, 'registry_http_error'],
[{ status: 302, body: SENTINEL }, 'registry_unreachable'],
];
@@ -217,7 +217,7 @@ test('invalid, failed and slow registry responses refuse without printing bodies
const text = await f.call(['update', '--check', '--output-format=text']);
assert.match(text.stdout, new RegExp(`Error: ${error}`)); assert.equal(text.stdout.includes(SENTINEL), false);
}
- f.state.reply = { body: JSON.stringify({ latest: '0.5.1' }), wait: 8000 };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.2' }), wait: 8000 };
const started = Date.now();
const slow = await f.call(['update', '--check', '--json']);
assert.equal(slow.value.error, 'registry_timeout'); assert.ok(Date.now() - started < 7000, 'bounded timeout');
@@ -264,7 +264,7 @@ test('update without --check refuses with ownership guidance and no network; opt
test('cached notices are off by default, opt-in additive, stderr-only in text and opt-out wins', async t => {
const f = await fixture(t);
- f.seed('0.5.1');
+ f.seed('0.5.2');
const plain = await f.call(['list', '--json']);
assert.equal(plain.code, 0); assert.equal('clientUpdate' in plain.value, false); assert.equal(plain.stderr, '');
const on = { SESSION_PEER_UPDATE_NOTICE: '1' };
@@ -273,14 +273,14 @@ test('cached notices are off by default, opt-in additive, stderr-only in text an
const { clientUpdate, ...rest } = json.value;
assert.deepEqual(rest, plain.value);
assert.deepEqual(Object.keys(clientUpdate).sort(), ['channel', 'checkedAt', 'command', 'current', 'guidance', 'latest', 'managedBy', 'schemaVersion', 'source', 'status']);
- assert.equal(clientUpdate.status, 'available'); assert.equal(clientUpdate.current, '0.5.0'); assert.equal(clientUpdate.latest, '0.5.1');
+ assert.equal(clientUpdate.status, 'available'); assert.equal(clientUpdate.current, '0.5.1'); assert.equal(clientUpdate.latest, '0.5.2');
assert.equal(clientUpdate.source, 'npm_registry_cache'); assert.match(clientUpdate.checkedAt, /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ$/);
const textOff = await f.call(['list', '--output-format', 'text']);
const textOn = await f.call(['list', '--output-format', 'text'], { SESSION_PEER_UPDATE_NOTICE: 'yes' });
assert.equal(textOn.stdout, textOff.stdout); assert.equal(textOn.code, textOff.code);
- assert.match(textOn.stderr, /^Update available: session-peer 0\.5\.0 -> 0\.5\.1 \(npm dist-tag latest\)\. This is a source checkout; update it with git and rebuild\.\n$/);
+ assert.match(textOn.stderr, /^Update available: session-peer 0\.5\.1 -> 0\.5\.2 \(npm dist-tag latest\)\. This is a source checkout; update it with git and rebuild\.\n$/);
const doctor = await f.call(['doctor', '--json'], on);
- assert.equal(doctor.value.clientUpdate.latest, '0.5.1'); assert.equal(doctor.value.capabilities.updateCheck, true); assert.equal(doctor.value.capabilities.selfUpdate, false);
+ assert.equal(doctor.value.clientUpdate.latest, '0.5.2'); assert.equal(doctor.value.capabilities.updateCheck, true); assert.equal(doctor.value.capabilities.selfUpdate, false);
const refusal = await f.call(['send', '--to', 'codex:bad', '--json', 'hi'], on);
assert.equal(refusal.code, 2); assert.equal('clientUpdate' in refusal.value, false);
for (const [args, extra] of [[['list', '--json', '--no-update-notice'], on], [['list', '--json'], { ...on, SESSION_PEER_NO_UPDATE_NOTICE: 'TRUE' }],
@@ -288,7 +288,7 @@ test('cached notices are off by default, opt-in additive, stderr-only in text an
const r = await f.call(args, extra);
assert.equal(r.code, 0); assert.equal(r.stderr, ''); assert.equal(r.value?.clientUpdate, undefined);
}
- f.seed('0.5.0');
+ f.seed('0.5.1');
assert.equal('clientUpdate' in (await f.call(['list', '--json'], on)).value, false);
// Python's own update.json in the same directory is a different release stream.
writeFileSync(join(f.cache, 'update.json'), JSON.stringify({ schemaVersion: 1, latest: '9.9.9', checkedAt: Math.floor(Date.now() / 1000) }));
@@ -304,26 +304,26 @@ test('missing, expired and invalid caches refresh once in the background without
assert.deepEqual(first.value, baseline.value); assert.equal(first.code, baseline.code); assert.equal(first.stderr, '');
await f.settled(1);
assert.equal(f.requests.length, 1);
- assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.1');
+ assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.2');
if (process.platform !== 'win32') assert.equal(statSync(f.file).mode & 0o777, 0o600);
- assert.equal((await f.call(['list', '--json'], on)).value.clientUpdate.latest, '0.5.1');
+ assert.equal((await f.call(['list', '--json'], on)).value.clientUpdate.latest, '0.5.2');
assert.equal(f.requests.length, 1, 'fresh cache does not refresh');
- f.seed('0.5.1', 25 * 3600_000);
+ f.seed('0.5.2', 25 * 3600_000);
assert.equal('clientUpdate' in (await f.call(['list', '--json'], on)).value, false, 'expired cache is not shown');
await f.settled(2); assert.equal(f.requests.length, 2);
for (const content of ['not json', JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.0-rc.1', checkedAt: Date.now() }),
- JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.5.1', checkedAt: Date.now() + 3600_000 }), 'x'.repeat(5000)]) {
+ JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.5.2', checkedAt: Date.now() + 3600_000 }), 'x'.repeat(5000)]) {
const count: number = f.requests.length;
writeFileSync(f.file, content);
const r = await f.call(['list', '--json'], on);
assert.equal(r.code, 0); assert.equal('clientUpdate' in r.value, false);
await f.settled(count + 1); assert.equal(f.requests.length, count + 1);
- assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.1');
+ assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.2');
}
});
test('concurrent invocations perform one bounded refresh; locks and failures back off', async t => {
- const f = await fixture(t, { body: JSON.stringify({ latest: '0.5.1' }), wait: 1000 });
+ const f = await fixture(t, { body: JSON.stringify({ latest: '0.5.2' }), wait: 1000 });
const on = { SESSION_PEER_UPDATE_NOTICE: '1' };
const results = await Promise.all(Array.from({ length: 8 }, () => f.call(['list', '--json'], on)));
for (const r of results) { assert.equal(r.code, 0); assert.equal('clientUpdate' in r.value, false); }
@@ -341,7 +341,7 @@ test('concurrent invocations perform one bounded refresh; locks and failures bac
await f.settled(2); assert.equal(f.requests.length, 2);
// Offline/failed refresh records a backoff without a version and never alters output.
f.state.reply = { status: 503, body: SENTINEL };
- f.seed('0.5.1', 25 * 3600_000);
+ f.seed('0.5.2', 25 * 3600_000);
const offline = await f.call(['list', '--json'], on);
assert.equal(offline.code, 0); assert.equal(offline.stdout.includes(SENTINEL), false);
await f.settled(3); assert.equal(f.requests.length, 3);
@@ -360,7 +360,7 @@ test('remote wire requests never read, refresh or report client notices', async
const on = { SESSION_PEER_UPDATE_NOTICE: '1' };
const wire = await f.call(['--stdio-request'], on, JSON.stringify({ schemaVersion: 1, args: ['list', '--json'] }));
assert.equal(wire.code, 0); assert.equal('clientUpdate' in wire.value, false);
- f.seed('0.5.1');
+ f.seed('0.5.2');
const seeded = await f.call(['--stdio-request'], on, JSON.stringify({ schemaVersion: 1, args: ['doctor', '--json'] }));
assert.equal('clientUpdate' in seeded.value, false);
await delay(1000);
@@ -371,7 +371,7 @@ test('remote wire requests never read, refresh or report client notices', async
test('over SSH the local client adds its own notice; the receiver never reads or refreshes', { skip: process.platform === 'win32' && 'POSIX fake ssh' }, async t => {
const f = await fixture(t);
- f.seed('0.5.1');
+ f.seed('0.5.2');
const bin = join(f.root, 'bin'), receiver = join(f.root, 'receiver-cache');
mkdirSync(bin);
writeFileSync(join(bin, 'ssh'), `#!${process.execPath}\nconst {spawnSync}=require('node:child_process');const flag=process.argv.at(-1).endsWith('--version')?'--version':'--stdio-request';` +
@@ -380,7 +380,7 @@ test('over SSH the local client adds its own notice; the receiver never reads or
const on = { SESSION_PEER_UPDATE_NOTICE: '1', PATH: `${bin}${delimiter}${process.env.PATH}` };
const json = await f.call(['list', '--host', 'audit', '--json'], on);
assert.equal(json.code, 0, json.stdout); assert.equal(json.value.host, 'audit');
- assert.equal(json.value.clientUpdate.latest, '0.5.1'); assert.equal(json.stderr, '');
+ assert.equal(json.value.clientUpdate.latest, '0.5.2'); assert.equal(json.stderr, '');
const text = await f.call(['list', '--host', 'audit', '--output-format', 'text'], on);
assert.equal(text.code, 0); assert.equal(text.stderr.match(/Update available/g)?.length, 1);
const off = await f.call(['list', '--host', 'audit', '--json', '--no-update-notice'], on);
@@ -430,9 +430,9 @@ test('explicit and background cache writes are serialized by the same lock and n
const locked = await f.call(['update', '--check', '--json']);
assert.equal(locked.code, 0); assert.equal(locked.value.latest, '0.3.9'); assert.equal(locked.value.cache, 'skipped_locked');
assert.equal(existsSync(f.file), false);
- f.state.reply = { body: JSON.stringify({ latest: '0.5.1' }) };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.2' }) };
await refreshCache({ ...f.env, SESSION_PEER_UPDATE_LOCK_TOKEN: background });
- assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.1'); assert.equal(existsSync(f.lock), false);
+ assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.5.2'); assert.equal(existsSync(f.lock), false);
f.state.reply = { body: JSON.stringify({ latest: '0.3.9' }) };
const written = await f.call(['update', '--check', '--json']);
assert.equal(written.value.cache, 'written'); assert.equal(JSON.parse(readFileSync(f.file, 'utf8')).latest, '0.3.9');
@@ -440,7 +440,7 @@ test('explicit and background cache writes are serialized by the same lock and n
writeFileSync(f.file, JSON.stringify({ schemaVersion: 1, package: 'session-peer', source: 'npm_registry', channel: 'latest', latest: '0.3.9', checkedAt: Date.now() + 60_000 }));
const newer = readFileSync(f.file, 'utf8');
const late = acquireLock(env)!;
- f.state.reply = { body: JSON.stringify({ latest: '0.5.1' }) };
+ f.state.reply = { body: JSON.stringify({ latest: '0.5.2' }) };
await refreshCache({ ...f.env, SESSION_PEER_UPDATE_LOCK_TOKEN: late });
assert.equal(readFileSync(f.file, 'utf8'), newer); assert.equal(existsSync(f.lock), false);
const skipped = await f.call(['update', '--check', '--json']);
@@ -465,7 +465,7 @@ test('concurrent lock acquisition across processes yields one owner, and none fo
});
test('lock I/O failures: a failed token write removes only its own new lock; an unverifiable lock maps to cache failed', async t => {
- const f = await fixture(t, { body: JSON.stringify({ latest: '0.5.1' }) });
+ const f = await fixture(t, { body: JSON.stringify({ latest: '0.5.2' }) });
const env = { ...f.env };
t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); });
const token = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
@@ -487,7 +487,7 @@ test('lock I/O failures: a failed token write removes only its own new lock; an
syncBuiltinESMExports();
const result = await checkUpdate('latest', env);
t.mock.restoreAll(); syncBuiltinESMExports();
- assert.equal(result.cache, 'failed'); assert.equal(result.latest, '0.5.1'); assert.equal(existsSync(f.file), false);
+ assert.equal(result.cache, 'failed'); assert.equal(result.latest, '0.5.2'); assert.equal(existsSync(f.file), false);
// Release failed too, so the lock is left for documented manual recovery.
assert.ok(existsSync(f.lock));
});