Version 0.4.0
is public from source dde3923efe116f87f38b1639b29b889d2b832e44.
The dated verification records identical
reviewed/Actions/staged/public bytes, signature/provenance checks and isolated
installation/removal. The release includes the approved limited macOS Codex
observation and explicit operator-confirmed receipt scope below; #130–#132
remain separate platform/collector extensions. Published skill 0.2.1 remains
the installation pin; runtime publication is not companion-skill publication.
Updated 2026-10-18 KST. TypeScript 0.4.0 uses published
Python 1.1.0, commit
8ca77cf,
as the ordinary list/discovery comparison baseline. JSON reports
referenceVersion: "1.1.0"; this identifies a comparison source, not complete
runtime parity. The source reports VERSION=0.4.0; publication evidence belongs
to the dated validation and release records. This source does not change
immutable public npm artifacts or their historical comparison markers.
- The exact-release
compatibility-v1.jsonand compatibility document are byte-identical to the historical Python 1.0.2 versions. The existingconformance.test.tsanddiscovery.test.tsselection passed 21 tests on macOS arm64 / Node 24.16.0 / Python 3.15.0 against an isolated export of the exact 1.1.0 source. This is ordinary list/discovery and synthetic refusal evidence, not full send/handoff/SSH interoperability or live ACK evidence. python-reference.pin.jsonrecords the exact ordinary source, comparison scope and selected source-file hashes; the tests check those hashes before using the oracle. CI and the publishing workflow use the same immutable source. This test-only manifest is not an npm runtime dependency or an installed-package authenticity check.- Python 1.1.0 source adds bounded Claude registry reads and fail-closed Codex SQLite-storage checks, along with revised SSH evidence handling and opt-in target-generation/handoff behavior. TS retains its own error codes, stricter selected-storage parser and distinct native result shapes; it does not copy Python output mechanically or weaken its existing guards. These differences were inspected statically; the 21 tests are not coverage of every new path.
- Python 1.1.0 has an experimental local Claude receipt-only ACK path. This does not establish automated ACK, SSH handoff or Windows handoff support in the scoped TS 0.4.0 implementation. Receipt bootstrap, original generation, body-free native observation, cleanup and platform qualification remain separate gates. Neither a native queue result nor turn completion establishes ACK or safe retry; no end-to-end consumption/exactly-once guarantee is introduced.
The frozen handoff design and its historical Python 1.0.3 design-review baseline remain separate from this ordinary runtime comparison. Exact frozen bytes and hashes are preserved. See the published design checkpoint and VALIDATION.md.
The retained tables and dated migration sections below describe their original Python 1.0.2/1.0.3 comparisons and TS 0.3.x source snapshots. Their historical pins and evidence are not rewritten as new-release conformance claims.
Updated 2026-10-14 KST. Python reference: 1.0.2,
47c2371.
This matrix describes the TS 0.3.6 source/package contract: the 0.2.0 unified listing, safe home selection,
read-only doctor, text/help/positional input, Unicode 14.0.0 name matching and
separately managed TS skill guidance (#16/#17/#18/#19/#25), the
0.2.1 reliability and hardening changes, and the
0.3.0 remote usability and maintenance
changes with their known limitations, plus the bounded ordinary SSH correction and finite skill guard. At the 2026-10-09 KST preparation checkpoint, public npm latest was 0.3.2 and preview was 0.1.0-preview.1. Historical 0.1.0
differences are called out explicitly. Public availability and tags must be
checked in the registry; this document does not assert a publication outcome.
SSH preflight enforces the exact same TS version banner. For builds from source
between releases, operators should also align reviewed commits; the wire does not
verify a source commit. referenceVersion: "1.0.2" identifies the comparison baseline,
not complete parity. See VALIDATION.md for dated evidence.
This source/package carries the published skill 0.2.1 guidance adopted in PR #146, merge source 602cfc09b31ce8aad23e1dd9382a2448a61143f9. All four guides use immutable skill source 0f494cabd05901c03161dc91792dcf89f8e96631; skill metadata/full-version and Python reference remain unchanged. TS 0.3.6 adds only its explicitly reviewed finite row with the same skill 0.1.0/0.2.0/0.2.1 acceptance as public 0.3.5. Historical rows stay unchanged and unreviewed future versions remain incompatible.
The patch updates package/banner/current examples and the stable release baseline to previous public 0.3.5. SSH still requires exact matching TS versions; 0.3.5 and 0.3.6 refuse each other. No native delivery behavior, platform qualification, automatic ACK/wait, retry, installation manager, skill publication or 0.4 experiment is added. Public availability must be checked in the registry; this source description is not publication evidence. Dated preparation and publication checks belong in VALIDATION.md and release notes.
The four guides now pin the independently published TS skill 0.2.1 release to immutable source 0f494cabd05901c03161dc91792dcf89f8e96631. Annotated tag object 4e7df7d8f99863e86b477eacce958033d7280246 peels to that source. The complete SKILL.md is 13,996 raw UTF-8 bytes, SHA-256 8c3520f95e0d6807303be96bfa06df69887ddaf76be05f1905505d0a956820c3. The tag, source and file identities were independently rechecked after publication; the reviewed PR head dc303b513d9da862f2ce21d648e168fdd847ae95 and published source have identical SKILL bytes.
Public npm TS 0.3.5, source a2057940ba19056fcaaa5551a7820f7414bd3bfa, accepts the exact profile: skill 0.2.1, implementation typescript, minimum 0.1.0, documented feature reference 0.3.2, policy probe-help. It retains legacy skill 0.1.0 and 0.2.0 acceptance. Public 0.3.2/0.3.3/0.3.4 reject skill 0.2.1; future runtime/profile combinations are not inferred from version ranges. The published 0.3.5 tarball's validator accepted these exact complete bytes with compiled default, explicit undefined, explicit 0.3.5 and outer inspectSkill; supplied historical/future identities were rejected. These helper checks are not executions of historical CLIs.
The published bytes are retained as an offline guidance fixture, distinct from the earlier candidate fixture. compatible / skill_contract_compatible / metadata_only is a bounded metadata result, not content authentication, send permission, delivery or ACK. Runtime and skill installation/update remain separate and manager-owned; no postinstall, global installation or implicit update was performed. Python reference 1.0.2 and Python skill pins remain unchanged; unshipped 0.4 handoff/observer/ACK and experiments are excluded. Earlier candidate, pending-publication and 0.2.0 pin records below are dated history, not the current installation guidance.
Historical preparation record; the current published skill and installation pin are recorded in the 2026-10-14 checkpoint.
This preparation is based on public 0.3.4 source afed4a5a8945012faf1b23d52cffecb70470a5b3 plus unmerged Draft #140, checkpoint b917769047db41dedcb4aa1367a1b89915beb000. Candidate compiled VERSION=0.3.5 preserves exact legacy skill 0.1.0 and published skill 0.2.0 profiles and proposes exactly skill 0.2.1 / minimum 0.1.0 / full 0.3.2 / typescript / probe-help. Future versions remain incompatible; metadata-only compatibility is not instruction authenticity, CLI permission, delivery or ACK evidence.
Companion agreement on the exact profile and complete final skill bytes is pending. The initial inactive-session dry-run was refused without submission. After user activation, a stable-writer dry-run passed and one review request was queued under correlation STS-035-SKILL-PROFILE-REVIEW-20261013-A; consumption, review and agreement remain unconfirmed. #139 remains open for that agreement and separate rollout. The four guides retain published skill source 8482cf644507c682915e35c85aa3384c53864906 and SHA-256 00c6fc343fdbeb8f6dc1b1ee1b730f04cef71bbfb08205a7be3559202f1d8804; no successor pin or skill publication is approved. Python reference 1.0.2 remains unchanged. Exact-version SSH requires both endpoints at 0.3.5 for candidate tests; public 0.3.4 refuses that banner. Draft #106–#109, #82 and #142 are excluded. Historical 0.3.4 preparation below remains dated evidence, not the current candidate description.
The earlier pending agreement statements above describe their initial preparation checkpoints. Companion Draft #35, source 9db5420b45568dd08fc43d406dbf9ad1952f7891, now provides the complete 13,930-byte skill 0.2.1 candidate, SHA-256 96d5a0d049f8a4d193703af3583b198a91079d44ae373de34e94a54e2296a4d6. Independent full-content/hash comparison and compiled guard/default/undefined/outer-inspection tests support candidate-specific design/content acceptance; see the exact review. This is a test fixture, not a published skill, approved installation pin or final joint publication freeze. Final skill wording/bytes after actual guard publication require another exact review and separate release approval. Published pin8482cf6 remains unchanged. Local scoped tests passed24/24; exact source/artifact/current-head CI and review status are recorded in PR #144. No instruction execution, live delivery/ACK, merge, staging or publication is claimed.
PR #134 corrects Windows-destination PowerShell forwarding of native exit codes for version preflight and wire requests. A missing native exit fails with 1; complete validated abnormal responses retain independent submission facts. Strict response limits, exact-version SSH, host-key checks and no automatic resend remain unchanged. This is not delivery or ACK evidence.
The four guides pin independently published TS skill 0.2.0 source 8482cf644507c682915e35c85aa3384c53864906. The 0.3.4 candidate accepts only the same two exact reviewed profiles as public 0.3.3; 0.3.5 and unreviewed versions remain incompatible. Python reference 1.0.2 is unchanged. Draft 0.4 handoff/app-server/observation and Side Session are excluded.
Implemented means the stated bounded behavior exists. Partial means a related command exists with the differences shown. Planned means no usable TS equivalent yet. Deliberately different identifies a maintained boundary, not a missing implementation. Issue links describe work, not available features.
| Surface | Python 1.0.2 | TS source (published differences noted) | Status / follow-up |
|---|---|---|---|
| Claude discovery and inbox submission | Local POSIX socket / Windows named pipe; PID or name; discovery rechecked before submission | Same transport families and recheck; Windows process start identity and authenticated pipe checks | Implemented; native fixtures are not live ACKs |
list selection |
Unified agent list; optional agent; bounded multiple Codex homes; partial discovery diagnostics | Source: combined Claude/Codex by default, optional agent/home filters, bounded multi-home inventory and partial diagnostics. Published 0.1.0 still requires an agent and explicit Codex home | Implemented in source for #16, 0.2.0; no Antigravity adapter |
| Codex active queue | Saved UUID, unique live writer, real lock/owner checks, pre-submit revalidation | Source: same protections, full UUID, implicit unique-live selection or explicit home; all known candidates checked | Implemented in source for #17, 0.2.0 |
| Implicit home / inactive queue | Select unique live home; explicit inactive opt-in under separate guards | Source: unique live selection; explicit saved inactive home with opt-in and verified inactive candidates. Public 0.1.0 has neither | Implemented in source for #17, 0.2.0; no wake/resume |
doctor |
Readiness diagnostics; return-route check is opt-in | Source: read-only local/SSH metadata, per-agent/home results, capabilities and bounded TS skill compatibility. Published 0.1.0 has no doctor | Implemented in source for #18, 0.2.0 base diagnostics; opt-in doctor --check-return-route added in 0.3.0 (#21) |
| CLI input, help and names | Human text or JSON; positional or named/stdin message; Unicode casefold matching | 0.2.0: explicit JSON/text format, positional/named/stdin body, command help, exact Unicode 14.0.0 casefold with PID for ambiguity | Implemented; #19, 0.2.0. Published 0.1.0 help still says “preview”; source help now describes unified list |
| From / Reply-To | Caller detection, automatic return-route metadata, structured URI resolution | 0.2.1 and earlier: From uses Codex environment UUID only; explicit --reply-address; local/SSH Reply-To URI parsing with conflict checks. 0.3.0: Claude/Codex sender detection, generated routes, --reply-to, same-user self normalization, Tailscale routing hint and doctor --check-return-route (details) |
Implemented for #21; no Antigravity identity, no executable Reply: line, 0.3.0 |
| SSH | Python source streamed to POSIX remote Python; multiple hosts/options/host metadata | Same-version installed TS CLI required; strict preflight; JSON stdin; explicit Windows PowerShell path. 0.3.0: ordered repeated --host, allowlisted --ssh-opt, typed single-hop --ssh-jump (POSIX clients), IPv6 literals and sshUser/sshUserSource (details) |
Partial; #20 (no arbitrary options or multi-hop; --ssh-jump refused on Windows clients; Tailscale routing hint in #21), #23 deployment/version design (proposed ADR), 0.3.0 |
| Updates | Cached advisory client/skill notices (on by default); GitHub release check; standalone self-update and remote push; package-manager guidance | 0.3.0: update --check reads npm dist-tags (latest/preview) with installation-manager guidance; no self-update or remote update; cached client notices are opt-in (SESSION_PEER_UPDATE_NOTICE=1), honor --no-update-notice/SESSION_PEER_NO_UPDATE_NOTICE; no skill notices. 0.2.1 only accepts --no-update-notice as a no-op |
Partial for #22, 0.3.0; update boundaries |
| Reusable execution API | Agent/transport capability adapters | Package root exposes pure protocol helpers only; no general typed execution API | Planned #24, Future — Agent integrations |
| Skill guidance/setup | Companion session-peer skill targets Python |
Separate session-peer-ts companion PR and exact-commit setup; baseline/version/help gates |
Legacy metadata profile from #25; guidance refresh/guard coordination in #118 |
| MCP | Optional extra, restricted destinations; separate wake permission | Unsupported | Planned #26, Future — Agent integrations |
| Codex wake | Explicit opt-in; platform/version gates; may run a model and alter history | Unsupported; queued does not wake or prove consumption | Planned #27, Future — Agent integrations |
| Antigravity | Experimental registered bridge generation/inbox contract | Unsupported | Planned #28, Future — Agent integrations |
| Paired device client | Optional crypto/device transport and receiver policy | Unsupported | Planned #29 design, #30–#32, Future — Paired devices and Relay |
| Relay server / hosted control service | Separate infrastructure and deployment | Not part of the TS CLI deliverable | Deliberately different scope; #29 must define client boundaries before implementation |
| Wait / general ACK / exactly-once consumption | No general CLI wait/ACK or exactly-once consumption guarantee | No general CLI wait/ACK or exactly-once consumption guarantee | Shared limit, not a TS parity gap; paired-device deduplication is not end-to-end consumption |
Historical 0.2.0 source anchors and the then-pinned Python oracle: TS CLI, writer, send, protocol; Python parser, commands, Codex.
| Setting / surface | TS source behavior / migration consequence |
|---|---|
CLAUDE_CONFIG_DIR, ANTHROPIC_CONFIG_DIR |
Claude config precedence: first nonempty value in that order, otherwise ~/.claude. No Codex home is required for Claude listing. |
CODEX_HOME, default ~/.codex, macOS Orca account homes |
Source: bounded automatic Codex listing and writer safety inventory. Codex send selects a unique stable live home, or validates the explicit home against all competitors. Published 0.1.0 requires it for Codex list too. No recursive disk scan. |
SESSION_PEER_CODEX_HOMES |
Source: JSON array of absolute or ~/ paths adds required listing homes and writer candidates. Invalid list configuration is rejected as a whole with other sources preserved; send keeps fail-closed inventory. Published 0.1.0 uses it only for writer checks. |
CODEX_THREAD_ID, CODEX_SESSION_ID |
Valid UUID enables default From metadata; --no-from disables it. 0.3.0: two different values, or Claude evidence at the same time, give no identity. |
CLAUDE_CODE_MESSAGING_SOCKET |
0.3.0: must match exactly one live registered Claude session to give From: claude:NAME (PID when the name is not a unique printable target). Antigravity caller detection is not implemented. |
SESSION_PEER_REPLY_HOST, CC_PEER_REPLY_HOST |
0.3.0: return host for generated Reply-To and doctor --check-return-route, after --reply-to and before the tailnet address. Invalid values are refused (invalid_reply_host). |
SESSION_PEER_TAILSCALE |
TS only, 0.3.0: off skips tailscale status --json, so every destination is ordinary SSH. |
SESSION_PEER_UPDATE_NOTICE, SESSION_PEER_NO_UPDATE_NOTICE, SESSION_PEER_CACHE_DIR, SESSION_PEER_UPDATE_REGISTRY |
0.3.0 (#22): opt-in cached notices, opt-out (wins), absolute cache directory override, and npm registry mirror for update checks (HTTPS, or loopback HTTP; no credentials). Python's XDG_CACHE_HOME update.json is not read; TS uses its own npm-update.json. |
| Reply/update/Relay settings from Python | No blanket compatibility. Only the flags and environment reads above are implemented. Do not infer a route or permission from an environment variable or From header. |
| Runtime and native dependency | TS needs Node 22.13+ within 22.x or Node 24.x, plus a matching native lock binary. Python is not a TS runtime fallback. |
| CLI ownership | Both distributions install session-peer; npm does not remove Python/uv/pipx/venv installations. Select one path deliberately and update it through its owning manager. |
| Skills | Install separately and explicitly; no package postinstall downloads a skill or invokes another package manager. |
node dist/cli.js list --json combines Claude and saved Codex rows. Use
--agent claude to skip all Codex inventory, or --agent codex to skip Claude.
--codex-home PATH pins only Codex discovery (also in a combined list) and
bypasses unrelated environment/Orca/configuration errors. It is inapplicable to
--agent claude. A configured empty string is invalid JSON: unset
SESSION_PEER_CODEX_HOMES or use [] to disable extra homes.
Automatic sources are visited in order: default ~/.codex, nonempty CODEX_HOME,
immediate macOS Orca account homes, then SESSION_PEER_CODEX_HOMES. Canonical
aliases merge source labels; UUIDs in distinct homes stay distinct. Every Codex
row includes codexHome and stateDb; identity is host + canonical home + UUID.
Rows sort by descending updatedAt, home and UUID using Unicode code-point
ordering. --all independently includes archived records in every home and
unreachable Claude records. Discovery success does not imply an active writer.
Per-home status is ok, absent or error; missing optional default/Orca DBs
are absent. Missing argument/environment/configured homes are errors, even
when they alias an optional source. Empty readable DBs are ok with zero rows.
Aggregate Codex status is not_installed only when no DB is readable and there
are no errors; that is exit 0. Any inventory/home/agent failure yields exit 1
while preserving other rows. Error codes are state_db_missing,
state_db_not_regular, permission_denied, state_db_read_failed,
home_resolution_failed, candidate_enumeration_failed, or
invalid_home_configuration. Errors use fixed text, never raw exception bodies.
Inventory errors appear in discovery.codex.errors; per-home errors in homes.
Top-level codexHome appears only for exactly one candidate and no inventory
errors; consumers should use each row's home.
The same listing and filters execute in the SSH destination's environment; nonzero partial responses retain rows and diagnostics. No recursive scan, inbox connection, writer selection or queue submission is part of listing. SQLite is read-only but its existing WAL bookkeeping caveat still applies. Listing availability never relaxes send's lock/owner/revalidation checks. The shared enumerators reject a set empty extra-home variable and require named homes for both list and send. Listing may return partial results; send refuses any unreadable inventory and never uses listing results as authorization. Pinned listing bypasses unrelated configuration, but pinned send checks all sources.
Source: discovery.ts, CLI, writer. Contracts: discovery.test.ts ports Python unified/multi-home cases; native Windows SSH/partial-envelope checks are in windows-contract.test.ts. The Python oracle runs in POSIX CI; deterministic fixtures and injected filesystem failures run on Windows too. Error injection is not a real Windows ACL test.
0.3.1 patch for #99: Claude list/send/sender discovery skips non-regular, symlinked or larger-than-1-MiB registry records while retaining healthy records. Descriptor checks, POSIX nonblocking open and bounded reads cover FIFO replacement races. This is not a deadline for network-filesystem I/O; doctor retains its separately bounded metadata contract. The patch is not in the immutable npm 0.3.0 archive.
Do not parse human messages or assume an optional key exists. Python's
schemaVersion: 1 and TS's schemaVersion: 1 are not identical field contracts.
TS normal envelopes have schemaVersion, host, command, ok, version,
referenceVersion; caught failures omit the two version fields. Exit codes are
0 (success), 1 (operation error), 2 (usage/refusal as classified by the command).
| Field / path | Python 1.0.2 | TS (source differences noted) |
|---|---|---|
list.sessions, discovery |
Unified/per-agent and per-home diagnostics; partial results can remain with ok:false |
Source: Claude rows then Codex rows; readable homes survive failed homes/agents with ok:false, exit 1. Published 0.1.0 has only selected-agent/single-home results |
send.status, submitted |
Submission/wake/transport-specific details; do not assume a uniform error shape | validated:false, posted:true (Claude), queued:true (Codex); caught refusal is refused:false, unknown is unknown:null |
consumptionConfirmed |
Optional and transport-specific; never infer a missing value as false or confirmation | Always false on send results, including refusal/unknown |
target, chars, dryRun, codexHome |
Target and home details depend on agent; Codex target includes id/name, with separate agent |
Success/dry-run target has agent plus id or pid/name; chars counts enveloped code points; codexHome only on Codex success/dry-run |
queueId |
Present if native queue stdout supplies an ID | Source #17: optional bounded native confirmation ID on successful queue only; absent in public 0.1.0 |
codexHomeResolution |
Resolution diagnostics on Codex success and some refusals | Source #17: sanitized resolution on success and failures after resolution begins; private owner fingerprints remain internal. Absent in public 0.1.0 |
remoteVersion |
Optional remote list metadata after version discovery (also update diagnostics); not universal on send |
Absent; exact TS version banner must pass preflight; mismatch is a refusal |
replyRoute, addressResolution |
Optional metadata when route advertised / Reply-To destination resolved | 0.2.1 and earlier: absent. 0.3.0: same names and values as Python for generated routes and URI destinations; an explicit --reply-address has no replyRoute |
wake |
Optional, opt-in; failed activation may still have submitted:true and ok:false |
Absent; unsupported option |
| SSH metadata | Requested/resolved host and SSH metadata; multi-host list may be an array | Verified remote response adds host/sshHost; caught local SSH failure for one host reports the local host. 0.3.0 adds sshUser/sshUserSource and returns an ordered array for repeated --host (see #20 section); with a resolved Tailscale peer, host is its MagicDNS name and sshHost the requested alias (#21) |
error, retryAllowed |
Error/detail fields vary by path; submission may already have happened | Fixed error codes, caught failure has retryAllowed:false; native stderr/message body are not copied into errors |
clientUpdate, skillUpdates |
Optional advisory notice metadata | 0.3.0 (#22): client-local. Added by the invoking client to a flat (single-result) list/send/doctor JSON result, including a single --host result that failed or is unknown, when notices are opted in and a fresh npm cache shows a newer stable version. Never inside a repeated --host array, never on top-level parse/local caught failures, never produced by a --stdio-request receiver; a remote-supplied clientUpdate is dropped. It never changes status, exit code, consumption or retry decisions. skillUpdates absent |
An absent submission field is not false; null is not false either.
Check ok, command, status, exit code and presence separately. TS unknown means
submission may have happened; never automatically resend. A Python wake error
with submitted:true must not be retried as a fresh send. A missing reply or target exit
alone does not establish consumption or failure. Observe ACK in the receiver
TUI separately. Further #20 changes must test success, refused, partial and
unknown output, including absent/null/false distinctions.
Output sources: TS CLI/send above; Python commands, Codex queue/wake, SSH errors.
This dated 0.3.x comparison excludes Relay/MCP and paired-device transports.
It retained the Python 1.0.2 oracle and referenceVersion at that checkpoint;
the current 0.4.0 candidate baseline is described above. A fixed-source
review on 2026-10-09 used published Python 1.0.3
0d252550
and TS 0.3.2 f335f073
for migration guidance, not byte/shape parity or a runtime-reference repin.
- Python's Claude adapter
returns
ok,target,charsanddryRun; ordinary Claude results can omitstatus,submittedandconsumptionConfirmed. TS emitsvalidatedwithsubmitted:falsefor dry-run andpostedwithsubmitted:trueafter inbox submission, always withconsumptionConfirmed:false. Claudetarget.namemay be null. Codex queue IDs are optional; none of these facts confirms ACK. - Missing Claude session directories yield an empty list and discovery
status:ok; optional missing Codex homes yieldabsent, and no readable DB with no errors yieldsnot_installed. These are discovery states, not proof that an agent executable or inbox is ready. Doctor completion is separate fromready, and neither grants send authorization. - Repeated SSH hosts produce ordered per-host results; an operational failure
during per-host dispatch makes exit 1. Command-wide refusals before dispatch
(for example duplicate hosts or an empty message) retain exit 2, including
when rendered as an array. A single host stays flat. Presence,
ok, status and exit must be interpreted separately. The shipped 0.3.2 SSH path can classify even complete stdout as unknown after exit 255 or timeout: #115 and #116 track the correction and strict response parsing. This document does not claim those fixes have shipped. Unknown never permits automatic resend or fallback. - TS supports manager-owned npm update checks, not Python's standalone
self-update or remote maintenance. Client notices are off by default; opt-out
wins over opt-in, but explicit
update --checkstill requests the registry. Runtime, remote installation and skill maintenance remain separate. - TS normalizes a self SSH Reply-To only for an explicit matching OS user and
a recognized exact local name, with no explicit SSH option. Conflicting
Claude/Codex sender evidence omits identity instead of choosing Claude first;
unusable Claude names fall back to PID. An explicit return-route check runs a
reverse SSH no-op only for a usable non-local destination. A self route instead
returns
verified,transport:local,self_route_normalizedwithout SSH; a missing host fails before a probe.verifiedtherefore means local normalization or one successful SSH no-op, not receipt authentication, general reverse access or agent consumption. - Stronger TS storage, registry and output guards are intentional; migration does not relax them to reproduce Python behavior. General native ACK/wait, end-to-end deduplication and Side Session support are not public ordinary-CLI features in either compared release. Historical wake tests for native Codex 0.154.0 do not qualify a current native CLI.
- The separately pinned TS companion uses published skill metadata
0.2.1. Its repository tag and the Python skill version are different version trains. Public TS 0.3.5 accepts this exact profile; public 0.3.2/0.3.3/0.3.4 reject it. The runtime-first, separately approved skill rollout in #118/#139 is recorded in the published checkpoint. A Draft content candidate is not an approved installation source; use the reviewed immutable release pin in the guides, not a moving branch.
| Boundary | Python 1.0.2 | TS (source differences noted) |
|---|---|---|
| Remote prerequisite | POSIX-compatible remote shell and usable Python; source delivered over stdin. Native Windows destination is not this SSH path | Installed same-version TS CLI + supported Node/native dependency. POSIX shell by default; explicit --remote-platform win32 uses PowerShell and a .cmd launcher. Provisioning and a separate compatibility contract are proposed, not implemented, in the remote deployment ADR (#23) |
| Message on SSH | Base64 body in quoted remote argv; source on stdin | Message in UTF-8 stdin JSON; PowerShell's encoded command contains launcher/flag, not the message |
| Remote body/command | 90,000-character remote message cap; fully quoted UTF-8 SSH command capped at 131,071 bytes after base64/argument expansion | Stdin reader capped at 4,100,000 bytes, including JSON framing/escaping. No unlimited stream; OS argv limits still apply when invoking native queue |
| General message | At most 1,000,000 characters; Codex at most 32 KiB UTF-8 | At most 1,000,000 Unicode code points; Codex at most 32,768 UTF-8 bytes; rejects blank/NUL |
| Envelope overhead | From/Reply-To and encoding may reduce usable body budget | Limits apply after From/Reply-To envelope creation; JSON escaping can exhaust wire budget sooner |
| Preflight and refusal | Source transport and path-specific diagnostics, not exact installed-TS preflight | Before message dispatch, version mismatch/auth/host trust/unreachable/timeout produce fixed refusal codes and submitted:false |
| After dispatch | Respect submission metadata; failed wake/transport does not prove nothing was submitted | Lost/malformed/unverified response or nonzero/timed-out native queue after spawn is unknown with submitted:null, no automatic retry |
Sources: Python limits, SSH construction; TS CLI/send above. UTF-8 byte limits are not character counts. Local native Windows support, WSL/POSIX SSH and TS native Windows SSH are separate paths.
- Inspect command ownership. Run
type -a session-peer(POSIX) orGet-Command session-peer -All(PowerShell), thensession-peer --version. Record the Python interpreter/venv or owning uv/pipx/package manager before changing PATH. Do not delete another manager's files or overwrite its shim. - Install the chosen version explicitly after checking registry availability. The public 0.3.2 record pins its source and artifact verified on 2026-10-07 KST. If 0.3.3 is unavailable in the registry, use a reviewed source build.
Check
npm view session-peer@0.3.3 version dist.integrity, then usenpm install --global --ignore-scripts session-peer@0.3.3. Verify the resolved command/version again. An isolated npm prefix and explicit launcher path allow side-by-side evaluation. Install any desired skill separately using the exact reviewed commit, explicit agent and scope in the guide. The original companionsession-peerskill targets Python; use distinctsession-peer-tsguidance. Check implementation and supported help; 0.1.0 development builds historically shared their version string with the public baseline. - Make selection and output explicit. Replace bare
listwithsession-peer list --agent claude --json, orsession-peer list --agent codex --codex-home /absolute/home --json. Use a full Codex UUID; preserve the discovered home when selecting a row. Unicode names use exact Unicode 14.0.0 casefold; use PID for collisions. Supply one positional body,--message, or UTF-8 stdin. Keep--jsonfor machine consumers. 0.2.0 also supports combined listing and text output. - Validate before submission. Use
send ... --json --dry-run. Review the target/home, then remove--dry-runonly for an intended submission. Check posted/queued separately from receiver ACK; do not retry unknown outcomes. Do not test migration against unrelated live sessions. - Migrate each SSH endpoint deliberately. Install the exact same TS version
remotely; Python's same-named CLI does not pass TS preflight. Set an absolute
--remote-binwhere PATH is ambiguous; for Windows add--remote-platform win32and the.cmdpath. Retain existing SSH host trust and authentication; a control socket is not new authorization. Account for the size limits above. In TS 0.3.0, repeat--hostfor ordered per-host results; the single-host result stays flat. TS 0.2.1 and earlier lacked multi-host aggregation. - Keep unsupported workflows on an explicit Python path. Remote updates, MCP, wake, Antigravity and paired devices need their existing implementation until their issues are delivered. Doctor is available in 0.2.0; 0.1.0 lacked this command. Updating npm does not update Python, skills, external Relay infrastructure or a remote host automatically. To return to Python, select its recorded path or uninstall only the npm-owned installation.
Design-freeze checkpoint: jointly approved, frozen DESIGN v0.2; runtime was unimplemented and unshipped at that checkpoint. The current source implementation and its limited 0.4.0 scope are described below. All four issues #69, #70, #71 and #72 remain in the single 0.4.0 milestone. Milestone assignment is not delivery or publication evidence.
The upstream candidate is Python Draft PR #261,
commit 99d8ec3855d57da34a6fa789f3a375820a8fbe85:
document
and synthetic fixture.
Exact copies are retained in test/fixtures/handoff-v1.md and
test/fixtures/handoff-v1.json; handoff-v1.pin.json records their SHA-256 hashes,
review status and separate runtime/design references. These test-only files are
excluded from the npm package. The design-only checkpoint retained its then-current
51-file allowlist; the implementation adds its runtime modules explicitly.
The design review uses published Python 1.0.3 at
0d252550ab40c26d1ac4a19193df6ca2f84d830b as its compatibility baseline.
The original review retained the then-current Python 1.0.2 ordinary comparison
pin. The current candidate separately checks ordinary list/discovery against
published Python 1.1.0; that does not change these frozen design bytes or claim
identical Python/TS legacy outputs or implemented cross-runtime handoff parity.
An approved design commit pins the shared contract fixture independently of
runtime publication and the bounded ordinary comparison.
| Axis | Candidate meaning / TS boundary |
|---|---|
| Submission | Preserve native status, target, queueId, submitted and optional-field absence; an observer failure must not pass through a catch that erases known submission evidence. |
| Observation | Opt-in metadata-only output for an exact native message ID/home/original generation. SQLite may evaluate body-bearing cells internally; this is not a blanket no-body-access promise. Queue deletion or turn completion is not receipt ACK. |
| Receipt ACK | Receipt-only capability or explicitly recorded operator attestation; never generic reply parsing, a new model prompt, wake or permission to act. |
| Wait | Separate immutable wait operation/results; a late receipt can add current evidence without changing the original timeout result. |
| Retry | Native local/SSH replay remains forbidden. Sender fences do not implement receiver dedup or a universal exactly-once guarantee. |
| SSH | A fully validated, requested explicit-wait result may preserve queued/true with ok:false/exit1. Unrequested handoff data cannot enable this exception; target validation is still required. |
The candidate adds a nested handoff object only in opted-in mode. Its proposed
init/prepare/status/wait/confirm/ack commands, epoch IDs, private collector,
30-second per-destination budget and 5-second cleanup reserve were not CLI
capabilities at the design-freeze checkpoint. The source subset is described
in the scoped implementation sections below. Budgets 1–5 seconds are syntactically valid in the proposal
but refuse before effect. Source-streamed SSH without a compatible receipt handler
remains ACK-unsupported under this candidate. No transcript/body-returning observer
or fallback after a possible submission is authorized.
The amended exact candidate supersedes the first 605061 review snapshot. Its
normative document and synthetic vectors now address the seven prior findings:
- Duplicate receipt queries require original capability-hash authentication, including the same-committed-receipt expiry exception; wrong tokens are denied.
- Receipt classification and wait-order facts commit atomically. Proven committed ACKs survive clock restart; unprovable manual confirmation refuses without rewriting history.
- Receipt evidence binds the original generation. Null generation cannot mint authority; copied original capability data is explicitly only token-possession assurance.
- Sender/collector outputs and own persistence exclude secrets, while intended recipient native storage may retain effect-input authority outside CLI control.
- Missing/corrupt ledger or unknown ID returns a closed
handoffQueryerror, without a fabricated epoch or native result. Known tombstones retain original context. - Raw private input, compact public subtree and outer/fanout limits are defined separately; lexical constraints stay inside the new schema and native facts are preserved.
- Explicit-wait wire exceptions require the original request and validated native target/IDs/goal; returned metadata cannot opt a legacy request in or promote forged ACK.
The final candidate now aligns the three remaining fixture/profile rules:
- Four original-request profiles preserve Python Claude's absent native fields, TS Claude's nullable name/optional agent and Codex's queued/true/false result, optional queue ID, native UUID normalization and canonical home/context.
- A post-submission unsupported channel is a request-gated explicit-wait failure at exit 1, preserving known submission and any injection evidence.
- Interrupting a pending explicit wait records stopped/stopped_by_operator and returns complete structured output with ok:false/exit130. A successful status query of that record returns exit 0; terminal waits remain immutable.
The shared candidate has 39 synthetic request-aware wire vectors for positive,
forged, wrong-target/home/agent/profile, unsupported and interruption cases.
The exact candidate has TS design-only approval after independent review; no
additional normative change is requested for these reviewed findings.
Joint design freeze is recorded for the exact source/hash tuple above.
Python acceptance record
confirms both sides' design-only approval; TS acceptance was recorded at
63473fb80936b0085e08039997ac39f7158a58dd. handoff-v1.pin.json now has
jointFreezeApproved:true, runtimeImplemented:false and no open design-review
gates. The byte-identical source document and synthetic fixture retain their
historical proposal/draft labels; the external approval freezes DESIGN, not a
shipped or implemented runtime protocol. No regeneration or runtime/reference
repin occurred. Normative/schema/vector changes require a new exact joint review.
Design-only approval does not establish receipt/bootstrap/runtime acceptance or
authorize merge, implementation, live effects, operational changes or publication.
Python's exact-candidate documentation tests and CodeQL passed; its optional Control dependency audit and required release gate failed. That CI status is separate from this static design review. No bypass, dependency repair or claim of a fully passing upstream PR is made by this historical review. At that checkpoint, runtime producer/collector/bootstrap, platform cleanup, metadata-only observation and live acceptance were future gates; current implementation evidence appears below.
handoff-contract.test.ts checks exact pins, synthetic status and selected golden
safety invariants. It does not implement or test a collector, native receipt,
crash recovery, monotonic clock continuity, app-server or live SSH. Upstream's
separate test-only validator likewise supplies design/schema checks, not runtime
acceptance evidence. No issue is closed and no implementation is enabled here.
| Evidence class | What it establishes | What it does not establish |
|---|---|---|
| Baseline source review above | Implemented branches, flags, bounds and optional fields at pinned SHAs | Behavior of a different release or a live destination |
| POSIX CI fixtures | Read-only SQLite/reference discovery, real local fixture locks/sockets, CLI/SSH failure contracts, packaging on Node 22/24 | Live model consumption or all OS/architecture combinations |
| Windows CI native fixtures | x64 Node 22/24: real kernel locks, Restart Manager PID/SID/start evidence, ownership/revalidation races, queue fixture, actual PowerShell/.cmd with fake SSH endpoint, installed native binary/package checks | Real network SSH authentication, real Codex queue acceptance, Windows arm64, receiver ACK |
| Dedicated live observations | Only the recorded target/runtime/OS/arch and observed submission/ACK in VALIDATION.md | General qualification of all builds/agents; fixture pass is never ACK |
| Public registry checks | Exact package hashes, provenance/signature metadata and isolated installation for the recorded release | Live-agent delivery or consumption |
The implemented rows map to these repeatable checks:
- conformance.test.ts: Claude/Codex discovery against the pinned Python oracle, unsupported schema/row/command refusal, empty-PATH runtime.
- transport.test.ts: real POSIX inbox, guarded queue, wrong/competing writer, unknown native outcome, inert Reply-To, host boundaries, SSH preflight/framing/loss.
- windows.test.ts and windows-contract.test.ts: the bounded Windows native contracts described above.
- input-boundaries.test.ts: exact 1,000,000-code-point / 32,768-byte message bounds, envelope overhead, 4,100,000-byte escaped JSON wire bound and invalid UTF-8 refusal. Python SSH limits are source-inspected here; these TS tests do not exercise the Python transport.
- types.test.ts, package-smoke.ts, release.test.ts: pure typed helpers, package/native loading, artifact and publication gates. package-smoke also checks installed-path update guidance without network or file changes.
- updates.test.ts: #22 update checks and notices against a local fixture registry (see below).
Windows expansion belongs to #34; this matrix belongs to #33. Each feature PR must update its rows, sources, evidence date and affected README languages. Changes to JSON, env, defaults, runtime/remote requirements or packaging must include the corresponding acceptance tests. Keep planned features distinct from supported behavior and deliberate exclusions; milestone assignment is not a release commitment. Recheck the exact packaged copy during release review.
This is 0.2.0 behavior; it does not change the immutable npm 0.1.0 archive. send --to codex:UUID selects only a unique stable live writer across
bounded known homes. --codex-home pins the destination but still inventories
competitors. A unique live writer without a saved row yields
thread_not_yet_persisted; an unknown owner or multiple live writers refuses.
An unsaved live competitor is checked too. Listing does not authorize sending.
--allow-inactive-codex-home requires an explicit home (including a home in a
Reply-To URI), a saved thread there, and all candidates verified inactive.
Ordinary inactive sends retain error: inactive_writer with resolution reason
inactive_queue_requires_opt_in. No wake, resume, native DB write, wait or ACK
is added. Dry-run validates once and submits zero messages. Actual submission
re-enumerates and compares all candidates, saved-row presence, DB file identity,
lock-file identity and live owner evidence immediately before one native queue
call. Evidence changes refuse; changes after the final check remain an OS/native
queue race, not an exactly-once guarantee. Post-spawn failures remain unknown,
submitted: null, retryAllowed: false; a queue ID is not an ACK.
| Home/configuration | Source send behavior |
|---|---|
Unset SESSION_PEER_CODEX_HOMES, or [] |
No extra candidates |
| Empty/whitespace, malformed JSON, relative/non-string/NUL array entry | Reject entire configuration, even with explicit destination |
Absolute or ~/ array entries |
Canonicalized, deduplicated, required homes |
Explicit argument, nonempty CODEX_HOME, configured home missing DB |
home_inventory_unreadable; candidate state_db_missing |
| Optional default/Orca home missing DB | Allowed as absent, but requested UUID lock still checked |
| Unreadable DB, unresolved path, unknown writer | Refuse; never skip a competitor using partial results |
Migration from 0.1.0: unset an empty SESSION_PEER_CODEX_HOMES or set it to [];
remove stale CODEX_HOME instead of relying on its missing DB being ignored.
These guards are stricter than Python 1.0.2's selected-missing-DB exemption and
saved-match writer inspection: this source checks even unsaved competitors.
codexHomeResolution has schemaVersion: 1, status (selected, explicit,
ambiguous, unknown), canonical selected or null, fixed reason, and
candidates. Each candidate has codexHome, sources, savedThread
(boolean/null), writerLock (not_checked, held, free, absent, unknown),
optional activity, and fixed reason. Uninspected candidates remain explicit.
No raw subprocess stdout/stderr, PID start fingerprint, UID or command is exported.
Errors before home resolution (invalid options/message or missing executable)
need not include this field. SSH resolves on the destination and preserves these
fields on verified responses; response loss cannot supply trusted diagnostics.
queueId is included only when successful native stdout contains exactly one
line Queued message ID for thread UUID. for the requested UUID. ID must be
1–128 ASCII alphanumeric/./_/:/- characters, starting alphanumeric.
Missing, malformed or multiple confirmations omit the field without retrying or
changing successful queue status. Failed native processes never expose an ID.
Contracts: test/codex-homes.test.ts, test/transport.test.ts and
test/windows-contract.test.ts. These are fixtures, not real recipient ACKs.
0.3.1 patch for #100: numeric UID 0 is a valid caller identity for writer verification. Unknown or mismatched owners still refuse, and all lock stability/start-time checks and Windows SID validation are retained. This patch is not in the immutable npm 0.3.0 archive.
Historical base-doctor evidence date: 2026-09-28. Introduced in 0.2.0,
doctor --json [--agent claude|codex] supports local
inspection and the existing version-checked SSH/stdin JSON transport. Published
npm 0.1.0 does not contain this command. Both SSH endpoints must use the same
TS version; reviewed commit alignment for source builds remains the operator's responsibility, not a wire-level commit check.
ok:true, exit 0 and diagnosticCompleted:true mean the diagnostic command
completed, including when agents are unavailable. ready is a metadata
precondition summary for the selected agents, not authorization to send.
Per-agent results remain in agents; per-home Codex results retain source labels,
required/optional status and missing, permission, unsupported-schema or unknown
codes. Explicit doctor home inspection bypasses unrelated inventory, like list;
it does not replace send's stricter competitor checks. Codex executable paths
are inspected for accessibility and never executed, even with --codex-bin.
An accessible DB and executable do not verify a live writer (writerVerified:false).
Claude checks bounded numeric session records (regular files, maximum 64 KiB),
process identity and inbox metadata. POSIX checks the socket file; Windows checks
process identity and a named-pipe advertisement, not pipe existence or availability
for connection. verification states this distinction. Invalid records and
unknown/permission inspections are counted. There is no inbox connection, queue
submission, lock acquisition, chmod, configuration mutation, wake, login, or
credential read. Native process arguments, stderr and session names/bodies are
not emitted. Host/home and executable paths are intentionally reported.
capabilities describes implementation support: list/send/doctor:true,
wake/wait/ack/consumptionConfirmation:false, agents:[claude,codex],
transports:[local,ssh]; implementation:typescript distinguishes runtime identity.
These are implementation capabilities, not evidence of agent readiness or ACK.
skills inspects only ~/.agents/skills/session-peer-ts/SKILL.md and
<explicit home, CODEX_HOME, or ~/.codex>/skills/session-peer-ts/SKILL.md.
No skill installation or reference traversal occurs. The legacy #25 TS skill contract
requires metadata version/min/full 0.1.0, implementation typescript, and
capability policy probe-help; missing/malformed/incompatible metadata is
reported separately and does not change agent readiness. This conservative
metadata check does not certify instruction content. Generic Python
session-peer skills are not inferred compatible. Since 0.3.0, the separate
opt-in doctor --check-return-route can execute a bounded reverse SSH no-op
(current behavior); base doctor
does not run that probe. --check-reply-to is not an alias and is unsupported.
Neither mode observes a reply or authenticates an ACK.
Acceptance evidence: test/diagnostics.test.ts ports Python diagnostic cases for
missing homes/tools, unsupported schema, per-home results, permission/unknown
boundaries, invalid/stale session records, read-only inspection and unavailable
inbox; adds source capabilities, bounded skill metadata, stdio and POSIX SSH
fixtures. test/windows-contract.test.ts checks doctor through real encoded
PowerShell/.cmd fixture SSH, without queue dispatch. Fixture evidence is not a
live-agent or delivery/consumption test.
Version 0.2.0 adds full list/send/doctor help, text rendering and one positional
message. The matrix distinguishes this from historical 0.1.0. The explicit
output requirement is retained: --json or --output-format json|text; no default
format changes. No selector retains json_output_required for compatibility.
Conflicting selectors and all parse errors produce one JSON refusal before stdin
reads/discovery/send. Valid text requests render execution errors as text. Remote
wire requests require JSON even when the caller selects local text output.
Partial discovery preserves rows, diagnostics and nonzero exit status. Terminal
control characters in text labels are escaped; JSON is unchanged.
One positional body, named body or stdin is accepted; combining sources refuses
before reading stdin. -- terminates option parsing. Empty or whitespace input
is rejected before envelope construction. Existing input/transport budgets and
unknown outcome/no retry boundaries remain.
Claude exact name matching uses Unicode 14.0.0 CaseFolding.txt C/F mappings
(default non-Turkic full casefold), pinned independently of Node ICU and Python's
runtime Unicode version. No normalization or fuzzy comparison occurs. ß/ss,
Greek sigma and ligature collisions require PID selection. Unicode data source
and SHA-256 are recorded in src/casefold.ts; regenerate offline with
node scripts/generate-casefold.mjs /path/to/CaseFolding.txt (hash enforced). The package includes
UNICODE-LICENSE.txt. Python builds with newer Unicode databases can differ for
characters added after 14.0.0; this is not a blanket future-Unicode parity claim.
test/cli-usability.test.ts covers format/help/input/error boundaries, real local
name/PID selection with collisions, and text escaping on all CI platforms.
Evidence date: 2026-09-28. TS CLI baseline for this work is 8289da7; companion
candidate is 081cc3c1d16a394bd92824333f4bc61c36951799.
This paragraph records the 2026-09-28 source review, not current repository-tag
availability. Companion PR #14 was later merged; its separately pinned TS
metadata remains 0.1.0, and #118 owns any successor contract/pin. Published
npm 0.1.0 remains unchanged. There is no runtime setup subcommand: user-invoked
Skills CLI 1.7.0 is the reviewed setup path, with an exact commit, explicit agent,
and explicit project/user scope chosen before running the documented command.
- Ownership: resolve the PATH launcher, require
session-peer X.Y.Z (typescript), then capture that launcher's help. Python'sreferenceVersionis not a feature guarantee. Baseline examples retain explicit agent, Codex home and JSON flags. - Development capabilities:
doctor, implicit home selection and inactive opt-in require affirmative help support. When top-level help advertises per-command help, inspectsend --helpfor send flags anddoctor --helpfor the supported doctor command. Mere mention in an unsupported list does not enable a feature. Skill minimum/full runtime metadata mean baseline support, not support for every conditional option. No assumption of wake, Antigravity, Relay, MCP or general wait support is imported from Python. - Workflow: fresh list → preserve exact home/PID → dry-run → one authorized send.
Reply URIs remain data,
queuedis not ACK, andunknownnever permits retry. - Metadata contract: separate
session-peer-ts/SKILL.md, skill version0.1.0,runtime-implementation: "typescript",runtime-min-version: "0.1.0",runtime-full-version: "0.1.0",runtime-capability-policy: "probe-help". Missing or incompatible skills can be diagnosed independently of transport readiness; the runtime never executes instructions merely to inspect metadata. - Lifecycle: no npm hooks, downloads, automatic update or overwrite of the Python
skill. Pinned update repeats
addfor another reviewed commit after inspecting local edits.skills updatefollows a moving source and is not this workflow. - Acceptance evidence:
test/diagnostics.test.tsalready tests the exact companion metadata contract as compatible without executing or exposing the skill body; compatibility does not authorize sending.test/skill-guidance.test.tsexercises baseline JSON list, selected-home dry-run refusal and gated explicit inactive dry-run in disposable SQLite homes; no submission occurs. Companionscripts/test-ts-skill.mjsexercises real Skills CLI project/global install, replacement-update, list and removal for Codex/Claude Code while preserving a Python skill fixture. Those are installation/contract fixtures, not live-agent delivery or ACK evidence.
0.2.1 (#49–#57, #59, #64, #65) tightens existing 0.2.0 behavior and adds no new command, transport or JSON field. Callers may notice the stricter refusals below.
| Area | Source behavior |
|---|---|
| Codex body argv (#49) | Native queue receives --message=<text> as one argument, so bodies such as - item, --help or -x are queued as text. The SSH request forwards the body the same way. |
| Codex SQLite location (#50) | An inherited CODEX_SQLITE_HOME is removed from the native queue environment. A home whose config.toml sets sqlite_home is refused as unsupported_codex_sqlite_home (unreadable config: codex_config_unreadable) in dry-run and send, and doctor reports it as unsupported, because validation reads <home>/state_5.sqlite. A live writer started with its own CODEX_SQLITE_HOME cannot be observed. |
| Option values (#51) | A separate value that is exactly an option name (for example --message --dry-run) is invalid_option (exit 2), as in Python argparse. Use --message=<text>, stdin or -- <text> for such text. |
| SSH exit code (#52) | A verified remote response keeps its 0/1/2 exit code locally. |
Reply-To codexHome (#53) |
Must be a POSIX or Windows absolute path, matching Python; relative and ~ paths are invalid_reply_uri. |
| Text output (#54) | Also escapes U+061C, U+200B–U+200F, U+202A–U+202E, U+2060–U+2069 and U+FEFF. JSON is unchanged. |
| SSH preflight (#55) | Exit 0 with the exact version line passes even when login scripts write to stderr; stderr classification applies only to failed preflights. |
Windows --remote-bin (#56) |
Every PowerShell single-quote variant (', U+2018–U+201B) is doubled inside the encoded command. |
| Windows batch shims (#57) | A .cmd/.bat executable found first on PATH (or given explicitly) is reported as executable_unsupported by send and doctor, because Node cannot spawn them without a shell and a shell would interpret the message. Use the native codex.exe. |
| Envelope trimming (#59, #64) | Leading/trailing \n is trimmed by index scanning instead of /^\n+|\n+$/, which was quadratic on long interior newline runs (CodeQL js/polynomial-redos). Trimming semantics are unchanged. |
| Windows owner inspection (#65) | The compiling Add-Type owner inspection waits up to 20000 ms; the compile-free creation-time probe keeps 8000 ms. Timeouts still refuse. |
0.3.1 source includes the following patch updates; publication is a separate approved release operation. The dated public 0.3.0 evidence remains historical.
| Area | Patch contract |
|---|---|
| SSH results (#93, #97) | Malformed successful send responses affect only that host; other outcomes are preserved. Unnamed Claude PID targets accept name:null. Unknown outcomes are never retried automatically. |
| Codex storage (#94) | Parse TOML root keys, including escaped keys, with TOML semantics. Nested sqlite_home is not a root override; malformed configuration is refused. An inactive opt-in does not bypass the root storage guard. |
| Reply-To (#98) | Reject decoded Claude session values with reserved codex: or session-peer: prefixes; wire receivers reject unresolved nested URI transports before SSH or queue execution. |
| Claude discovery (#99) | Read bounded regular registry files, skip unsafe/oversized entries and preserve healthy sessions. POSIX FIFO/nonblocking and descriptor race tests are platform-specific evidence. |
| Writer identity (#100) | UID 0 is a valid verified identity; missing or malformed identity information remains refused. |
| Documentation (#90) | Includes the migration wording correction absent from the immutable public 0.3.0 archive. |
SSH still requires identical TS versions on both ends: 0.3.0 and 0.3.1 refuse each other at preflight. Coordinate endpoint upgrades. The 0.3.0 known limitations below remain: Windows-client jump routing, the Windows deployment cell and newer receipt/recovery rules, and update notice validation gaps are not resolved by this patch.
0.3.0 (#20, #21, #22, #74, #78/#79, #87; design #23) adds remote and
maintenance features to 0.2.1. Existing commands, exit codes and the delivery
boundaries (posted/queued are submission, never consumption or ACK) are
unchanged. SSH still requires the same version on both ends, so 0.2.1 and 0.3.0
endpoints refuse each other at preflight; upgrade them together.
| Area | 0.3.0 behavior |
|---|---|
| Multi-host SSH (#20) | Ordered repeated --host for list/send/doctor (array output for two or more), all hosts validated before any ssh starts, duplicate_ssh_host, IPv6 literals and sshUser/sshUserSource. See details |
| Connection options (#20) | --ssh-opt is an allowlist (-p, -l, -i, -o Port/User/IdentityFile/IdentitiesOnly, -4, -6); everything else is refused. Typed single-hop --ssh-jump USER@HOST[:PORT] with a fixed hardened ProxyCommand, POSIX clients only |
| Sender context and return routes (#21) | Verified Claude/Codex sender identity (From:), generated structured Reply-To URIs, --reply-to, a Tailscale routing hint and opt-in doctor --check-return-route. No executable Reply: line. See details |
| Update checks (#22) | update --check reads npm dist-tags once (3 s, no retries) and reports manager-specific guidance; nothing is installed. Cached client notices are opt-in (SESSION_PEER_UPDATE_NOTICE=1) and opt-out wins. See details |
sp shorthand (#74) |
Opt-in, shell-scoped Bash/zsh and PowerShell activation files; npm registers only session-peer. See details |
| Bounded timeouts (#87) | On POSIX, external commands run in their own process group, and a timeout, output overflow or termination signal kills the whole group (including a jump ProxyCommand). On Windows only the direct child is killed; the call still returns at its deadline |
| Windows process probe (#78, #79) | The compile-free creation-time probe waits up to 15000 ms (was 8000 ms) after a CI cold-start timeout; the compiling owner inspection keeps 20000 ms. Timeouts still exclude the process |
| Remote deployment (#23) | Design only: a proposed ADR for Python-free deployment and version compatibility. No deployment code ships; the exact-version preflight is unchanged |
- #20 — Partial.
--ssh-jumpis refused on Windows clients (ssh_jump_unsupported_platform) until Win32-OpenSSHProxyCommandbehavior is verified. No multi-hop and no arbitrary ssh options. - #23 — Partial. The remote deployment ADR is a proposal. Its POSIX cell was exercised by hand (record); that dated Linux run did not exercise the later receipt/recovery rules. The 2026-10-07 Windows record adds bounded published-0.3.1 qualification on Windows 10 x64, an administrator account, PowerShell 5.1 and Node 24.16.0: offline provisioning, wrapper/native locking, operator doctor and nonexistent-target dry-run, plus 31 local storage fixtures. Node 22 has banner/native-lock smoke only, not the full matrix. Non-admin, other architectures, F6 runtime-identity enforcement and the Linux current-rule gaps remain open. No 0.3.2 live ACK or deploy helper is established.
- Update notices (#22). The Windows refresh, refresh-failure and
forged-
clientUpdate(injection) cells are unverified; Windows CI covers only the array/flat shape and one text stderr line. Those cases have POSIX fixture evidence only. - Fixture and CI evidence is not a live-agent ACK.
0.3.1 patch for #98: a Claude Reply-To URI whose decoded
session starts with reserved lowercase codex: or session-peer: is invalid_reply_uri before
local selection or SSH dispatch. Ordinary Claude names and Codex UUID routes
keep their existing contract; this patch is not in the immutable npm 0.3.0 archive.
Wire receivers require an already resolved target; an unresolved Reply-To URI
in --stdio-request is nested_transport_forbidden before queue or SSH calls.
#21 behavior added in
0.3.0; 0.2.1 and earlier do not have it. Compared with Python
1.0.2 replies.py/diagnostics.py:
| Area | Python 1.0.2 | TS source |
|---|---|---|
| Claude sender | PID parsed from CLAUDE_CODE_MESSAGING_SOCKET, matched in the registry; name or PID |
Same evidence. The match must be exactly one live row (exact socket match when the path has no PID). A name that is not unique by casefold, is all digits, contains : or a control character falls back to the PID |
| Codex sender | CODEX_THREAD_ID, else CODEX_SESSION_ID; codexHome from CODEX_HOME |
Same, plus codexHome only for an absolute CODEX_HOME. Two different IDs give no identity |
| Nested agents | Claude wins over Codex variables | Both kinds of evidence give no identity (ambiguous), per #21 |
| From line | From: agent:id @ user@host with a local-hostname fallback |
From: agent:id only. The return host appears only in the structured Reply-To |
| Reply-To | URI plus executable Reply: python3 … send … line |
URI only; no executable line. Generated only with a sender; local sends without a configured host get transport=local |
| Host precedence | --reply-to, SESSION_PEER_REPLY_HOST, CC_PEER_REPLY_HOST, detected tailnet address; user added when missing |
Same order. Detection reads only tailscale status --json (MagicDNS name, else 100.64.0.0/10 IPv4); no tailscale ip/ip addr/ifconfig scraping. --reply-to conflicts with --reply-address/--no-reply-to |
| Self normalization | User (if any) equals this user and host is local | The user is required and must equal this user, and the host must be an exact local form (localhost, canonical 127.x.y.z, ::1, ::ffff:127.x.y.z, the host name or Tailscale self); non-canonical numerics and IPv4-compatible IPv6 (RFC 4291 section 2.5.5.1) never normalize to local. This strict predicate is separate from the fail-closed return-host refusal. Applies only when the route came from the URI alone (no --host/SSH options, including --ssh-jump) |
| Tailscale routing | Online peer: alias kept, HostName/HostKeyAlias added; offline/ambiguous refused; unknown unchanged; MagicDNS off means no lookup |
Alias kept and HostName added only for Online === true with MagicDNS on. HostKeyAlias is added only when the same ssh -G used for sshUser read the config and found none; a configured alias is kept and an unread config is never overridden. Online === false is refused even with MagicDNS off; any other Online value is ordinary SSH. Status is queried at most once per command with a 3 s bound; host is the MagicDNS name and sshHost the requested alias. SESSION_PEER_TAILSCALE=off disables it. The macOS app CLI is used only when no tailscale is on PATH |
| Return-route probe | doctor --check-return-route [--reply-to]; fixed true command; detail may contain stderr |
Same options plus -T and ControlPath=none. Fixed exit 0 (a no-op in POSIX shells, cmd.exe and PowerShell; only POSIX return hosts have fixture coverage). The 8 s deadline covers the final ssh only; the Tailscale (3 s) and ssh -G (5 s) lookups are bounded separately. Same returnRoute statuses and reasons plus return_host_is_receiver; never includes stderr. With --host, a return host that might name a machine (fail closed, no DNS: localhost names, canonical 127.0.0.0/8 and 0.0.0.0/8, ::1, ::, ::ffff:-mapped forms, IPv4-compatible IPv6 and every non-canonical numeric such as 127.1, 0177.0.0.1, 2130706433 or 4294967296, whose meaning differs between resolvers) is invalid_return_route before SSH, on the receiver a known self name (exact OS host name or Tailscale self name/address, for any user; other DNS/LAN aliases are not detected) or an unprovable numeric form fails as return_host_is_receiver instead of normalizing to local, and the receiver gets only the return host through the JSON request. A remote send refuses a loopback or non-canonical numeric reply host (invalid_reply_host). Like every ssh this CLI starts, the probe and its ssh -G lookup use the probing machine's trusted ssh_config; see the #20 trust boundary |
Evidence: POSIX fake ssh/tailscale fixtures in test/replies.test.ts
(sender detection and ambiguity, name fallback, precedence and conflicts,
same-user/wrong-user self routes, offline/unknown/ambiguous peers, generated
and malformed URIs, probe argv and auth/host-key/timeout/other outcomes,
remote probe forwarding). A test preload sets SESSION_PEER_TAILSCALE=off and
clears CLAUDE_CODE_MESSAGING_SOCKET, CODEX_THREAD_ID, CODEX_SESSION_ID and the reply-host variables for every test; fixtures that need an identity set it explicitly. No real tailnet, SSH destination
or agent session was used.
#20 behavior added in
0.3.0; 0.2.1 and earlier do not have it. Compared with Python
1.0.2 --host/--ssh-opt:
| Area | Python 1.0.2 | TS source |
|---|---|---|
Repeated --host |
Ordered per-host loop; one host stays flat, several return a JSON array; any failure exits 1 | Same shape and order for list, send and doctor. Every element keeps schemaVersion, ok, host (as requested) and command. Any failed host makes the overall exit 1; a single host keeps its own 0/1/2 exit code |
| Malformed successful send response | No delivery may be inferred from an invalid response | A spawned send response with a missing or command-invalid target is unknown for that host (submitted:null, no retry); other host results remain intact. Target validation follows the requested agent and accepts the Python Claude shape without target.agent. Text output falls back to JSON for values that cannot be string-coerced, so formatting cannot turn an accepted send into a false command-wide refusal |
| Validation before dispatch | Host/option checks run inside the loop, so an earlier host may already be contacted. argparse syntax errors print usage to stderr (exit 2) without JSON; other pre-dispatch errors are attributed to each --host |
Every host, --ssh-opt, message and remote option is validated before any ssh process starts (including ssh -G). TS always answers in JSON. Every --host VALUE/--host=VALUE before -- is collected before any other check, so with two or more of them every pre-dispatch refusal, including syntax errors such as an unknown option or a missing value, is an array with one submitted:false element per host; with fewer it is one flat object |
| Duplicate destinations | Not checked | duplicate_ssh_host for a repeated destination string (user, case-insensitive host name, canonical IPv6). Syntactic only: aliases or addresses of the same physical host are not detected |
| Attempts | One attempt per host | One preflight and at most one request per destination. Refused/unknown hosts are reported in place; later hosts still get their single attempt. No failover, resend or retry after unknown |
| Timeouts | Python subprocess timeouts on the direct child | POSIX: each ssh (and every other external command) starts detached, in a new session and process group without a controlling terminal, registered as soon as its PID exists; cleanup covers that group only (a descendant that calls setsid() leaves it and is out of scope; no PID scanning); a timeout, an output overflow or SIGINT/SIGTERM/SIGHUP to the CLI kills the whole group, including a jump ProxyCommand that holds the pipes, and the call completes after the exit plus a 100 ms grace instead of waiting for the pipes. Windows: only the direct child is killed, descendants may linger, and the call still returns at the deadline. Preflight timeout stays a refusal and request timeout stays unknown without retry (#87) |
--ssh-opt |
Arbitrary ssh arguments minus a substring blocklist (proxycommand, localcommand, permitlocalcommand) |
Allowlist only: -p PORT, -l USER, -i IDENTITY_FILE, -o Port=/User=/IdentityFile=/IdentitiesOnly=yes|no, -4, -6, as separate or attached tokens. Anything else is unsupported_ssh_option; a bad, duplicate or dash-leading value is invalid_ssh_option. Identity paths refuse % and $ (OpenSSH token/environment expansion). Options are re-emitted canonically after the fixed BatchMode=yes, StrictHostKeyChecking=yes and ConnectTimeout=10, which OpenSSH therefore keeps |
| Jump hosts | ProxyJump/-J allowed through --ssh-opt; the hop gets no command-line BatchMode/StrictHostKeyChecking |
Done for POSIX clients: typed --ssh-jump USER@HOST[:PORT] (one hop, explicit user, shell- and %-inert characters only). The CLI builds a fixed ProxyCommand: the shell-quoted, %-escaped ssh path with BatchMode=yes, StrictHostKeyChecking=yes, UpdateHostKeys=no, ConnectTimeout=10, ConnectionAttempts=1, ProxyCommand=none, ProxyJump=none, ControlPath=none, ForwardAgent=no, ClearAllForwardings=yes, PermitLocalCommand=no, -W '[%h]:%p'. It is placed with the fixed options, together with outer ControlMaster=no, ControlPath=none and ProxyUseFdpass=no, so it overrides a ProxyJump/ProxyCommand and any control master in ssh_config for the target (checked with ssh -G -F <fixture>). -J/ProxyJump/ProxyCommand through --ssh-opt stay refused. Refused with --ssh-control-path and on Windows clients (ssh_jump_unsupported_platform) until Win32-OpenSSH ProxyCommand behavior is verified. No multi-hop, and no Tailscale lookup for the hop (v1). Result field sshJump. --ssh-opt values (including -4/-6) apply to the target only, not the hop. Shell scope: the generated ProxyCommand was verified with sh, bash and zsh; other login shells are unverified. Unverified boundaries: Win32-OpenSSH clients. Python parity: Python accepts arbitrary -J hops; TS offers a stricter, typed single hop |
| User | sshUser/sshUserSource: explicit from USER@HOST, else ssh -G (ssh_config_or_local_default), else unknown |
Same fields and sources; -l USER also counts as explicit. -l together with USER@HOST is conflicting_ssh_user, because -l silently wins. Without an explicit user, one local ssh -G runs per host (5 s limit). It does not connect, but per ssh(1)/ssh_config(5) it evaluates the user's ssh configuration, including Match exec commands, as Python's lookup does. A failed or unexpected lookup gives unknown for that host only |
| Trust boundary | Blocklisted command-line options; user ssh config trusted | The allowlist governs only options this CLI puts on the ssh command line. The user's and system ssh_config are trusted user configuration: ProxyCommand, ProxyJump, Match exec, Include and similar settings there apply to every ssh this CLI starts, as for the user's own ssh |
| IPv6 | Passed through to ssh |
2001:db8::1, [2001:db8::1] and user@[2001:db8::1] are accepted; brackets are removed for ssh, sshHost shows the destination used. Zone IDs (%) are refused |
| Control socket / Windows | n/a | --ssh-control-path requires exactly one --host. --remote-platform win32 and --remote-bin apply to every host with the same encoded PowerShell command |
| Text output | Per-host human blocks | Per-host blocks prefixed by Host: <host> |
Evidence: POSIX fake-ssh fixtures in test/ssh-hosts.test.ts (ordering,
argv, zero invocations on invalid input including syntax errors, flat versus
array refusals, partial results, per-host ssh -G failure, IPv6, POSIX/Windows
remote commands, body absent from argv) and a two-host case in the Windows
native fixture. --ssh-jump fixtures use a fake ssh (installed under a path
with spaces, a quote and %) that expands %h/%p/%% and runs the
ProxyCommand through /bin/sh -c exec, recording the exact hop argv;
injection attempts start no ssh. Before implementation, local
ssh -G -F <fixture config> (OpenSSH 10.3p1, no connection) confirmed that a
command-line ProxyCommand overrides the target's configured
ProxyJump/ProxyCommand without error, and that the hop's command-line
ProxyCommand=none/ProxyJump=none override the jump host's configured
proxies; ssh_config(5) does not list ProxyCommand among the keywords with
${ENV} expansion. The unknown case is a classification fixture: the fake exits
255 on the request without running the remote CLI, so it shows that unknown
is never retried, not that a delivered message's response was lost. No real
SSH destination was contacted.
Evidence date: 2026-10-02. Added in 0.3.0; 0.2.1 and earlier have no update
command. session-peer update --check --json [--channel latest|preview] makes one
GET to <registry>/-/package/session-peer/dist-tags (default
https://registry.npmjs.org/, 3-second timeout covering the body, 64 KiB body
limit, redirects refused, no retries). latest must be a stable npm semver;
preview may be a prerelease. Versions follow SemVer 2.0 precedence without a
v prefix or build metadata. Python 1.0.2's GitHub tags are a different stream
and are never compared; referenceVersion is unrelated to this check.
| Field | Meaning |
|---|---|
current, latest, channel, distTag |
Running TS version, the dist-tag's version, and the tag name |
source |
npm_registry |
status, outdated |
update_available (outdated:true), up_to_date or ahead |
updated |
Always false; nothing is installed |
managedBy, updateCommand, guidance |
Owner from the resolved CLI path, positively identified only: npm (global prefix whose bin/session-peer symlink or session-peer.cmd launcher targets this package: default, Homebrew, nvm, nvm-windows, fnm), pnpm/yarn/bun (global store whose package.json declares session-peer), volta, npx. Command only for those owners and only when outdated. npm_project/pnpm_project (project package.json declares the dependency), source (checkout) and unknown (anything else, such as an unconfirmed nvm-windows prefix) always get null plus a guidance sentence |
cache |
written, skipped_locked, skipped_stale_lock, skipped_newer, failed, or not_applicable (preview channel) |
skills, skillsManagedBy |
Same local TS skill metadata check as doctor; separate |
Failures use the standard caught-failure envelope with registry_timeout,
registry_unreachable, registry_http_error, registry_response_invalid,
dist_tag_missing (exit 1), or invalid_update_registry,
unsupported_update_channel (exit 2). Response bodies are never emitted.
update without --check is self_update_unsupported (exit 2) with updated:false,
managedBy, updateCommand (dist-tag spec, same owner rules), guidance and checkCommand; it makes no
request. --host, other list/send options and --stdio-request (error
remote_update_unsupported) are refused, so a remote destination never performs
update checks. doctor capabilities add updateCheck:true, selfUpdate:false.
Notices are deliberately opt-in (Python's are on by default): agents and
scripts call this CLI frequently, and an unrequested registry call or extra
stderr line would change its no-network default. With
SESSION_PEER_UPDATE_NOTICE=1 (1/true/yes/on), completed local
list/send/doctor results read only the local cache. A fresh (24 h) cache
whose stable latest is newer adds clientUpdate (schemaVersion, status,
current, latest, channel, checkedAt, source: "npm_registry_cache",
managedBy, command, guidance) to JSON, or one stderr line for text; stdout text is
unchanged. A missing, invalid, future-dated or expired cache spawns one detached
refresh and returns immediately. A failed refresh stores latest:null, which
suppresses attempts for 1 hour. Opt-out (--no-update-notice,
SESSION_PEER_NO_UPDATE_NOTICE) suppresses these background notices and
refreshes and wins over opt-in. An explicit update --check is an intended
request: it always contacts the registry and, on latest, writes the cache under
the same lock rules (reported as cache).
Cache and network failures never change results or exit codes.
Scope: notices belong to the invoking client. With --host, the client adds
clientUpdate (or its stderr line) to its own top-level output, including the
verified result obtained over SSH; the --stdio-request receiver never reads,
refreshes or produces a notice. The notice is computed once per invocation.
Multi-host and failures (shipped 0.3.0 integration with #20/#21):
- Repeated
--hostJSON array: request order and every element's exact shape are kept; noclientUpdatein elements and no wrapper object. - Flat result: one destination (local or a single
--host) gets the additive field, also when that per-host result failed (preflight refusal, remoteok:false) or is unknown. Top-level parse errors and local caught failures (the top-level catch path incli.ts) never get it. - Text output: one stderr line per invocation, for flat or array output and with failed, mixed or successful hosts; stdout is unchanged.
- The advisory never changes status, exit code,
submitted,consumptionConfirmedorretryAllowed. clientUpdateis reserved for the client:remote()deletes anyclientUpdatein a verified receiver response before it is shown.- Refresh count: the notice is computed at a single call site after dispatch, once per invocation. The fixture evidence is indirect: registry request counts with the single-flight cache lock (one request for three hosts, none from receivers). It does not instrument the call itself.
Evidence: POSIX test/integration-multihost-updates.test.ts (fake ssh).
Windows: test/windows-contract.test.ts checks one opted-in repeated --host
array (no element field), one text stderr line and a flat single-host field
through the encoded PowerShell fixture; it is not evidence for the refresh count
or the failure/forged-field cases, which are POSIX-only.
Single flight, fail closed (changed after review). Earlier revisions of this PR reclaimed stale locks and used a takeover mutex; review found legal filesystem interleavings that let a newer cache record be overwritten or another generation's lock or mutex be deleted. That code is removed:
- One lock,
npm-update.lock, created withO_EXCLand holding a random token. The notice path creates it before spawning the detached refresh, which inherits the token.update --checkcreates it around its own cache write. - Every cache write (background or explicit) happens only while holding the
lock, and only when the existing record is older than the writer's start
(
checkedAt); an explicit check that finds the lock held still reports its result withcache: "skipped_locked". A refresh without the current token does nothing. - No code path takes over, renames or deletes a lock, temporary file or other
resource it did not create, and no mutex exists. The shared cache record
npm-update.jsonis the one file that is replaced by design, by atomic rename and only under the lock rules above. An existing lock of any age makes the background refresh skip. The holder deletes its own lock after re-reading its token.
Guaranteed, with no automatic recovery: at most one lock holder at a time (O_EXCL), so
at most one background refresh and at most one cache writer at once; no writer
replaces a record newer than its own start; no invocation deletes or moves a
foreign lock, temporary file or other resource. Cost: a lock can be left behind
when its holder crashes (the detached child runs for at most about 3 seconds)
or when releasing it fails, for example because its token cannot be read back.
A failed token write right after creation is undone only for that same inode.
A left-behind lock blocks background refreshes until a user deletes
npm-update.lock by hand while no session-peer process runs. update --check reports cache: "skipped_stale_lock" once the lock is older
than 60 s; it never removes it. Manual deletion during a running refresh is the
only way to break the guarantee (the holder could then remove a lock created
after the deletion).
Cache: npm-update.json (0600, atomic temp-file rename) in a 0700 directory
owned by the user: SESSION_PEER_CACHE_DIR (absolute), else
$XDG_CACHE_HOME/session-peer on macOS/Linux, ~/Library/Caches/session-peer,
~/.cache/session-peer, or %LOCALAPPDATA%\session-peer\Cache. It holds only
package name, channel, version and timestamp. Windows relies on the per-user
profile ACL instead of POSIX modes. No checks or notices modify npm-owned files,
Python installations, remote hosts or skills.
Acceptance evidence: test/updates.test.ts (POSIX and Windows CI) covers SemVer
ordering, stable/prerelease tags, invalid/oversized/HTTP/redirect/timeout/offline
registry responses without body leakage, manager guidance per install layout,
including nvm, nvm-windows, fnm, Volta and ambiguous prefixes (unknown, no command),
cache paths and modes, opt-in default off, flag/env opt-out, JSON additivity,
text stderr, offline/invalid/expired/future caches, concurrent single-flight
refresh, fail-closed live and stale locks (no refresh until manual removal),
review regressions: an explicit check while a background holder runs reports
skipped_locked and writes nothing, then the holder publishes; a holder whose
start predates a newer record publishes nothing; a stale lock is never taken
over, so its holder's release cannot remove a successor; a stale lock and a
leftover mutex keep the same inode and content after notices and an explicit
check; a non-holder token neither releases nor writes; multi-process
acquisition yields one owner (none for a stale lock), failure backoff, Python cache isolation, TS skill metadata,
wire isolation, and a POSIX fake-SSH list --host where only the client adds
the notice. test/package-smoke.ts checks the installed
package's guidance and unchanged files. All use a local fixture registry; no
test contacts npm.
0.3.0 ships opt-in Bash/zsh and PowerShell aliases for sp. 0.2.1 and earlier
have no activation assets. npm still registers only canonical session-peer.
Sourcing in the caller's shell/scope refuses existing sp executables, aliases and
functions; no install hook, profile/PATH edits or runtime dispatcher is added.
The alias resolves the same canonical command with the same arguments, stdin,
output, exit codes and delivery boundaries. Automation/SSH/help/version retain
canonical identity. See activation and lifecycle.
Python/TS co-installation is excluded; replacing an implementation respects its original manager. This TS change does not implement additional Python features. Package fixtures cover name equivalence, collisions, an in-place version update and removal on every supported shell. They do not establish live delivery or ACK.
The public 0.3.2 record pins publication source f335f07352c842f2f6ceb12bd2ca6274b52f69f7 and the artifact. Runtime behavior differs from 0.3.1 only in the version banner and exact-version SSH gate. Its dated preparation record describes the pre-publication checkpoint, not current availability. Windows destination qualification used published 0.3.1, with the limits above. No 0.4 handoff/app-server/observation runtime is included; Drafts and the joint design freeze are not shipped capabilities.
The 0.3.3 correction for #116 and #115 validates bounded raw stdout with fatal UTF-8, one complete JSON object, decoded-key uniqueness, depth 64 and 65,536 nodes. Stdout has a 1 MiB byte cap; diagnostic stderr has an independent 4,096-byte retained budget. Overflow cannot turn a retained prefix into success.
Complete request-matched native facts survive a later SSH 255 or collection timeout. Native ok/status/submitted/target/queueId stay intact, while local exit 1 and client-owned sshTransport report transport failure. A peer-supplied sshTransport is discarded. Missing, partial, polluted, malformed or mismatched send responses remain outcome_unknown/submitted:null/retryAllowed:false. No retry, fallback or failover occurs, and submission is not ACK. Normal verified exit 0/1/2 behavior, nullable Claude names, optional queue IDs, per-host ordering and text/JSON output remain supported. Explicit Codex home/inactive selection context is checked; canonical remote aliases remain a destination trust boundary.
See the response contract and fixture limits. This 0.3.3 correction is not part of the immutable public 0.3.2 artifact, does not change the Python 1.0.2 oracle, and adds no 0.4.0 handoff/observer/ACK feature.
Historical 0.2.0 evidence; current 0.2.1 guidance is recorded in the 2026-10-14 checkpoint.
Evidence recheck: 2026-10-12 KST. Public TS runtime 0.3.3 at c0ed30273b9e9c0049df86ae3c38486a94289527 accepts the legacy skill 0.1.0 profile and the exact published skill 0.2.0 profile (typescript, minimum 0.1.0, full 0.3.2, probe-help). Public 0.3.2 remains legacy-only. The companion release session-peer-ts-v0.2.0 was pinned by the four guides at that checkpoint to source 8482cf644507c682915e35c85aa3384c53864906. The annotated tag object de7d619aebdfb4a8fe86893dc70728f646032e8d peels to that source; its 12,835-byte SKILL.md has SHA-256 00c6fc343fdbeb8f6dc1b1ee1b730f04cef71bbfb08205a7be3559202f1d8804. Those tag/file identities were independently rechecked for this follow-up.
compatible / skill_contract_compatible / metadata_only is a bounded metadata verdict, not a content signature, send permission or delivery/ACK result. Minimum is the baseline CLI compatibility floor; full is the documented published feature coverage reference. Optional operations still need actual version/help and platform/native prerequisites. Runtime and skill management remain separate, without postinstall. The Python 1.0.2 reference and Python skill are unchanged.
The immutable public 0.3.4 source/package uses compiled VERSION=0.3.4 and accepts these same two exact profiles through its default inspector; its explicit helper rejects unreviewed runtimes such as 0.3.5. TS 0.3.5 source/package adds the exact skill 0.2.1 tuple reviewed in the dated candidate-content record above. This source support is not proof of registry publication or an approved successor skill installation pin; final skill publication and pin adoption remain separate. At the earlier PR #134 checkpoint, compiled VERSION was 0.3.3 and the 0.3.4 row was tested through explicit validator arguments. That checkpoint is historical. Source support neither broadens compatibility to arbitrary future versions nor establishes runtime or skill publication. See the finite matrix and retained dated review. The dated candidate checkpoint below remains historical.
Version 0.3.3 uses compiled VERSION=0.3.3 on the production doctor inspection path. Its proposed finite matrix accepts the legacy profile (skill/minimum/full 0.1.0) and the exact proposed successor (skill 0.2.0, minimum 0.1.0, full 0.3.2, typescript, probe-help, root name session-peer-ts). Unsupported well-formed tuples are incompatible; malformed or conflicting metadata is unknown. Supported and incompatible tuple verdicts report verification:metadata_only; missing, permission and unknown results omit it. Whole-file reading is bounded at 65,536 bytes with a single overflow-detection byte, fatal UTF-8 and regular-file/change checks. This does not authenticate instruction content or authorize delivery.
See the bounded grammar, matrix and reader evidence. Companion Draft #20 design acceptance at 699a325 records the 2026-10-09 static design review of exact #126 ba18bb806a6ba503381259b4255211af3adc0336 and release Draft #129 bfb3d6f1256ab076a76660e08a7bf1ad071a6c6c; their reviewed design SHA-256 values are recorded in the linked guard design. No tests were rerun in that review. At this dated checkpoint, runtime and successor skill were unpublished. The earlier queued coordination request remains historical, without independently established consumption or approval. Design/fixture acceptance does not authorize runtime merge/release, final skill metadata/publication/installation/pin changes or claim public compatibility; the immutable npm 0.3.2 checker stays legacy-only. The guard runtime must actually publish before separate successor skill finalization/tag/publication/pin changes. At that checkpoint the install pin remained 081cc3c1d16a394bd92824333f4bc61c36951799; managers own upgrades, and no postinstall is introduced.
At the 2026-10-09 KST preparation checkpoint, #128 integrated open PRs #125/#127 and Draft #126 for review; that preparation did not bypass their human merge gate or approve staging/publication. 0.3.2 and 0.3.3 refuse each other's SSH preflight. Windows jump-host #20, provisioning #23, the Python 1.0.2 oracle, and unshipped 0.4/native ACK boundaries remain unchanged.
Python 1.1.0 is now public at
8ca77cf31254c8287a2593fe484bb30a7dba2727. Its
handoff contract
SHA-256 is 46a953010c49214bd8f969412f26713384fbe09a0ba78ebf0fef75da40d0d889,
byte-identical to the jointly reviewed design. The published fixture SHA-256 is
963b94e78f510c2535e405813558275d512a5bfe6bbd99ffdf0994b4d2e6e441,
also byte-identical to the frozen shared fixture. Both identities were rechecked
against the exact release source through local Git without replace objects and
the GitHub contents API.
The exact reviewed TS mirror remains unchanged; publication is not permission
to regenerate shared vectors or declare runtime conformance.
The handoff pin manifest records this source-only comparison separately from the
historical Python 1.0.3 design baseline and the historical Python 1.0.2 ordinary
oracle. The current candidate's ordinary source pin is recorded separately in
python-reference.pin.json.
The Python release's experimental local Claude receipt path does not establish
automated ACK support in the TS implementation. The pure-validator comparisons
below do not establish native execution parity.
Receipt bootstrap and additional native platforms are deferred to #130–#132;
original binding/generation guards remain mandatory in the included scope. This design-identity record alone does not repin a runtime
reference or establish runtime conformance.
The frozen #69 design pin is unchanged. The private POSIX ledger and
strict protocol helpers now implement prepare, at-most-once sender fencing,
status, bounded wait records and explicit receipt/operator-confirmation
validation. CLI details
describe the opt-in 0.4.0 scope; earlier 0.3.x packages lack these commands. Automated ACK
bootstrap, Windows private ACL support and original-sender SSH collector
coordination are deferred extensions (#131/#132), not prerequisites of the
user-approved limited 0.4.0 scope. Fixture passes are not live receipt/injection
qualification. Default ordinary output stays unchanged. The ordinary conformance oracle is
published Python 1.1.0; experimental receipt implementation parity is not claimed.
handoff-cross-runtime.test.ts also invokes the pinned published Python pure
validators/projection helpers and the TS validators on 45 data cases: all seven
shared states, schema/privacy/ACK/retry negatives, unsupported observation and
agent-specific native field absence. Standalone and canonical module hashes are
pinned. No native CLI, ledger/collector, model or network is invoked by that
comparison; equal validator decisions are not end-to-end handoff parity.
The journal encrypts its private message-binding digest with a separate
mode-0600 ledger.key; missing/changed/untrusted keys and plaintext journals
are refused without automatic key recovery or history migration. This is an
at-rest journal boundary, not protection against a reader of both private
files. Duplicate committed operator confirmation returns the original receipt
without rewriting its wait classification or journal.
The opt-in internal client is qualified only for queue mechanics on Codex 0.160.1 and 0.162.1 / macOS arm64, using separate isolated network-denied synthetic homes. One native queue/add, original client message ID persistence and no loaded/resumed thread were observed. This is not live delivery, general owner-route or receipt ACK qualification. Legacy Codex queue remains the default CLI path.
The client bounds frames, RPCs, traffic, owner verification, monotonic deadlines and owned process-group cleanup. An authorized caller supplies a durable before-effect fence; at most one queue RPC follows it. After a possible write, disconnect, timeout, schema error or remote refusal never enables fallback or resend. initialize opts out the exact profile notification set (83 for 0.160.1, 84 for 0.162.1); queue/add's own input echo is validated but only IDs are returned. queue/list, history, full item/turn notifications, resume/start and model turns are excluded. The separate synthetic metadata protocol is explicitly fixture only. Other platforms/versions and native app-server observation remain unsupported.
Generated schema hashes and the fixed official source commit are recorded in
test/fixtures/app-server-schema-evidence.json; tests use hermetic streams and
owned Node children. These mechanics do not establish Python runtime delivery parity
or implement automatic ACK.
Scoped runtime details cover bounded one-shot/explicit-wait observation, native CID queueing, original writer generation checks, per-operation terminal waits and positive native fact preservation. Generated schema/native queue mechanics are distinct from synthetic metadata/wait integration and live consumption evidence. The combined CLI path is restricted to Codex 0.160.1 or 0.162.1/macOS arm64; POSIX SQL fixtures alone do not qualify other native platforms. No rollout/transcript API is queried, no ACK is inferred and no post-attempt fallback/resend occurs.
Dedicated 0.162.1 Codex and Claude Code 2.1.296 test sessions supplied additional
local evidence; native client ID injection is separate from plain model response
or ACK. Retained qualified intents support independent waits with no
new native submission. Observation is read-only against native state; each wait
persists its private operation and terminal record in the sender ledger. Each process owns its wait cutoff; foreign pending and
old terminal waits are preserved. A separate voluntary matching reply was
explicitly confirmed before any wait with operator_confirmed; ACK status and
a later acknowledged wait succeeded. Confirmation after an earlier independent
wait correctly refused receipt_order_unprovable, without replay. This is manual
operator attestation, not an automated token-possession collector.
POSIX SSH opt-in now prepares an unattempted intent at an explicitly initialized destination ledger, pins resolved target/home/generation/epoch/CID, then sends once. Strict request-aware native facts survive complete failed-transport responses; unsolicited/malformed handoff cannot create ACK. Source deadlines are not renewed; destination remaining-duration watchdogs reap owned groups. Transit delay cannot be synchronized across independent monotonic clocks. Destination-handler/recording-socket fixtures use fake SSH. Separate real KR/US Linux checks established ordinary native queue acceptance and a US manual operator receipt; they did not qualify native client-ID observation on Linux. See SSH details.
Open capabilities include original-sender SSH collector coordination, Windows ACL/native observation and automated receipt producer/collector bootstrap. Other platform/version tuples and arbitrary blocking-filesystem deadlines are not qualified by these tests. #69/#70/#71/#72 remain together in the scoped 0.4.0 milestone. The ordinary comparison is pinned to Python 1.1.0; version 0.4.0 does not claim broader runtime parity.
Storage binding is checked on the owned queue transport before its effect: env/CLI pins plus exact opened state/queue paths. Managed-policy redirection or missing direct-native PID proof refuses with zero queue RPC. An additional network-denied initialization-only fixture verified config/CLI/env precedence with zero queued rows and no model/thread effect. Bootstrap policy files are not promised unchanged by the native tool; no managed requirement is bypassed.
The internal queue client also pins Codex 0.162.1/source
092d3acd6bec3e3a14bdc7e7a2810ab628ab759d on macOS arm64. The source/schema
fixture records the exact notification opt-out set (84 methods, compared with
83 in 0.160.1). An unloaded, isolated thread received one queue request with its
exact supplied client ID and native queue receipt; a separate owned-descriptor
fixture refused the durable effect guard and left zero queue rows. No version
range, Linux/Windows qualification, live delivery or ACK follows from these
queue-only fixtures. Initial harness failures are recorded separately and were
never retried with an already-attempted intent.
On 2026-10-11 the user approved keeping #69/#70/#71/#72 together in 0.4.0, with the following explicit qualification boundaries. This is source-candidate scope, not a publication claim or blanket Python runtime parity.
| Capability | Included candidate scope | Deferred extension |
|---|---|---|
| Native client-ID injection observation | Exact Codex 0.160.1/0.162.1 on macOS arm64; warm original metadata store and unchanged writer generation | Linux #130, Windows #131; no inherited profile support |
| Explicit receipt ACK | Operator attestation on supported POSIX ledgers, with original binding/order validated; operator_confirmed is not model authentication |
Voluntary receipt producer, authenticated collector/bootstrap and original-sender SSH collector #132 |
| Windows handoff storage | Required capability reports unsupported; ordinary published Windows send/SSH are separate | Private DACL/reparse storage and owned cleanup qualification #131 |
| SSH handoff | Explicit same-version destination preparation/ledger, strict binding and one send; unsupported goals refuse before effect | Real remote native client-ID observation remains unqualified; actual Linux queues/manual ACK do not establish it |
An automatic receipt collector, if later implemented, still records only a receiver's explicit receipt. Visible replies, arbitrary message text, queue deletions and turn completion never authorize ACK promotion. The frozen design bytes and historical approval metadata remain unchanged. SQLite projections may evaluate body-bearing cells internally; bodies/errors do not cross into JS or ordinary output. Required unsupported goals refuse before effect; after any possible submission no fallback/resend is permitted.
The four development PRs were merged in the authorized order #106→#107→#108→#109. Scoped evidence and separate release acceptance are tracked in their issues and #150. Platform/collector follow-ups are not completion conditions for this limited release. Companion skill candidate guidance and version/profile rollout is tracked in companion #31 and Draft #37. The 0.4.0 finite row preserves the three reviewed published profiles and accepts the exact proposed skill 0.3.0/min0.1.0/full0.4.0/probe-help tuple after design/content review of the complete Draft #37 fixture. That fixture is not a published skill or installation pin; final skill bytes require renewed exact review. A new runtime version is never automatically compatible by numerical ordering.