Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions experiments/codex_side_session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Native Codex Side Session experiment (#81)

**Dedicated single-producer tests only. General use remains blocked.** This
TypeScript prototype lives outside the installed `session-peer` CLI. The root
package's file allowlist excludes `experiments/`; root dependencies, CLI,
queues, SSH/Relay and publishing configuration are unchanged.

It connects over WebSocket to a private owned Unix endpoint of the **existing
owning** Codex app-server. It never launches a server, resumes/forks a thread,
falls back to a parent, reconnects or retries an unknown submission. Server
version is restricted to the tested 0.159.2 response. The experiment has its
own private npm manifest/lockfile (`ws` 8.22.0); it is not a new public package.

## Running the experiment

Tested with Node 24.16.0 on macOS arm64. Use a reviewed private endpoint for a
server/TUI dedicated to this experiment, configured read-only/on-request.
Do not point this prototype at a working session or an arbitrary Orca server.

```sh
npm ci --prefix experiments/codex_side_session --ignore-scripts
npm run typecheck --prefix experiments/codex_side_session
npm test --prefix experiments/codex_side_session

node --experimental-strip-types experiments/codex_side_session/side_session.ts \
--experimental-side-session --socket /private-test/app.sock list
node --experimental-strip-types experiments/codex_side_session/side_session.ts \
--experimental-side-session --socket /private-test/app.sock send \
--to codex:00000000-0000-4000-8000-000000000001 \
--exclusive-test-session --dry-run --message 'test input'
```

Those paths and UUIDs are examples. Establish the side's origin separately
through the actual native `/btw` TUI. The list command reports loaded ephemeral
threads as **unclassified**; ephemeral alone does not prove `/btw` provenance
or a parent relationship. The send command requires an exact UUID plus loaded,
ephemeral, idle and direct-input-capable checks. `--message -` reads bounded
UTF-8 stdin. Frames are limited to 1 MiB, input to 64 KiB, inventory to 32 pages
of 100 entries, and notifications to 100 per request. Approval/server requests
are refused, never answered; notifications are discarded without transcript
retention. Results omit provider error text and private endpoint paths.

On this Mac, set `TASK_TEMP` to a task-owned private directory on the current
project volume before tests; the fixtures otherwise use the OS temp directory.
The actual unit/transport tests are manual experiment checks, not integrated
into the root CLI test suite. Existing PR CI checks root/package compatibility.

## Unmet safety gates

1. **Idle preflight is not an atomic idle-only submission.** In the pinned
[native handler](https://github.com/openai/codex/blob/ff6aec96948b70d94983af2641a6b67c94faeff5/codex-rs/app-server/src/request_processors/turn_processor.rs#L652),
`turn/start` calls `start_or_steer_turn`. Another producer can win the race
after inspection. `--exclusive-test-session` is an explicit prerequisite
acknowledgement, not a lock against native clients. No concurrent-input
adversarial test was performed.
2. **Loaded does not mean UI-open.** The actual TS experiment closed the `/btw`
UI with Ctrl+C but the side remained loaded, ephemeral and idle. The same
dry-run still returned validated. No further input was sent. General support
requires cooperating-owner active registration/generation and suitable
native admission semantics.

`accepted` / `submitted:true` remains distinct from consumption and ACK. The
live test observed an exact ACK in its dedicated rendered TUI separately. This
adapter has no general ACK wait or automatic Reply-To return route. Linux,
Windows, SSH, shared server access and multi-client approval handling remain
unverified. A new server cannot access another server's in-memory side thread.

See [RESULTS.md](RESULTS.md) and the [Python reference experiment](https://github.com/abruption/session-peer/pull/246).
The issue stays open and the PR stays Draft while general-use gates are unmet.
107 changes: 107 additions & 0 deletions experiments/codex_side_session/RESULTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# TS Side Session experiment results — 2026-09-30

**Transport feasibility: PASS — one real side request and an exact side ACK.**
**General-use safety and complete #81 acceptance: NOT PASS.**

## Independent TS execution

- Repository base: `0eb5e1380ed29061da206eac835f8ad719be6694` (npm CLI 0.2.1).
- macOS arm64; Node 24.16.0; Codex CLI/server 0.159.2; experimental `ws` 8.22.0.
The dedicated TUI selected its default GPT-6.1-Sol/high; no model override.
- A fresh dedicated app-server exposed a private owned Unix endpoint; the native
TUI connected to that exact server with read-only/on-request policy. Existing
session policies were not changed. The TS adapter used real WebSocket-over-Unix,
with an explicit Unix connector that handles socket paths containing spaces.
- `initialize.userAgent` reported `session_peer_ts_side_experiment/0.159.2 ...`
instead of `codex-tui/0.159.2 ...`. The version gate accepts those two exact
tested prefixes for 0.159.2. CLI version and native TUI header independently
confirmed 0.159.2; the prototype does not accept arbitrary versions.
- A setup request yielded `ACK TS81-PARENT-READY-20260930` in the parent TUI.
Parent baseline was exactly one turn and idle.
- The real native `/btw` command displayed **Side from main thread**. The same
server's loaded inventory gained one ephemeral, idle, direct-input-capable
thread. No `thread/fork` substitute or artificial side target was used.
- Exact-target TS dry-run returned validated with `submitted:false`. The adapter
then called `turn/start` once, returning accepted / `submitted:true` /
`consumptionConfirmed:false`. No fallback, retry or policy/model override.
- The dedicated **side** rendered TUI showed both the request and the independently
generated response **`ACK TS81-SIDE-ROUNDTRIP-20260930`** (4 seconds of work).
This was read from the rendered screen, not inferred from the submission result.
- Parent before/after: one turn, idle, identical serialized-turn SHA-256
`62c56a46a3b9aa86c30d1c0610ddbd57327181c586b10e3b2b8c7f33dbae9a70`.
The side request and response did not enter the parent's transcript.

Only this dedicated test's parent was read with turns to compare hashes; no raw
transcript was exported. The adapter reads target metadata with `includeTurns:false`
and discards notifications. This does not implement general ACK waiting or reply
routing into an unrelated working conversation.

## Controls and findings

| Check | Evidence / result |
| --- | --- |
| Real native `/btw`, metadata, dry-run and one-shot side ACK | Live Mac PASS |
| Parent transcript unchanged | Live Mac hash/turn-count PASS |
| Persistent parent selected by adapter | Live refused: `ephemeral_target_required` before input |
| Native `thread/queue/add` against exact side ID | Live RPC rejection `-32600`; no queued fallback |
| UI dismissed with Ctrl+C | Live returned to the parent TUI |
| Dry-run after UI dismissal | **Still validated** while retained loaded/idle; no subsequent input |
| Unloaded/busy/mismatched/unknown-capability target | Contract test refusal, no turn/start |
| Transport timeout/protocol/internal error after submission attempt | Contract test unknown, no replay |
| Actual Unix WebSocket/symlink/private-directory handling | Fixture PASS |
| Unexpected server approval request | Fixture connection refused/closed; never answered |

The UI-lifetime finding independently reproduces the Python experiment's blocker.
The `turn/start` steering risk is grounded in pinned native handler source, not an
adversarial concurrency result. `ephemeral` is not a `/btw` discriminator. The
exclusive-test flag acknowledges the prerequisite but cannot enforce it. Owner
registration/generation plus safe native admission must be designed before any
normal opt-in support is claimed. Issue #81 remains open.

## Verification and cleanup

- Strict TypeScript checking and 20 scoped contract/real-Unix-WebSocket fixtures
passed locally. These fixtures do not count as live Linux/Windows evidence.
- Experimental dependencies installed with scripts disabled; npm audit found
zero vulnerabilities. No root dependency or lockfile change.
- Existing repository/package checks and PR CI are recorded on the Draft PR.
Root package file allowlist excludes all experimental code/manifests/results.
- The dedicated native TUI and server terminals were closed; Orca confirmed PTY
shutdown and retired those terminal surfaces. No matching endpoint server
process remained. No handles remained on its physical socket; only the test's
socket and rendezvous symlink were removed.
- The one newly created parent test record was deleted through its owning server
after checking exact ID/cwd, one turn and the unchanged hash. Unrelated records,
processes, worktrees, daemon files, credentials and operating settings were
preserved. The implementation worktree remains for Draft review.

Unverified: Linux/Windows live sessions, SSH, existing Orca private servers,
concurrent input/steering, multi-client approvals, general ACK wait and automatic
Reply-To. Canonical CLI, native queues, SSH/Relay, release settings and npm
publication are outside this prototype.

References: [App Server](https://learn.chatgpt.com/docs/app-server),
[Python experiment at its reviewed head](https://github.com/abruption/session-peer/blob/a0dea84396181426082e7467f113db111ab49b68/experiments/codex_side_session/RESULTS.md),
[pinned native turn handler](https://github.com/openai/codex/blob/ff6aec96948b70d94983af2641a6b67c94faeff5/codex-rs/app-server/src/request_processors/turn_processor.rs#L652),
[native ephemeral queue refusal](https://github.com/openai/codex/blob/ff6aec96948b70d94983af2641a6b67c94faeff5/codex-rs/app-server/src/request_processors/thread_queue_processor.rs#L259).

## Response-classification correction — 2026-10-14

- Matching RPC responses must contain exactly one of `result` and `error`.
A rejection is trusted only when its error is an object with a safe integer
code. Contradictory/malformed envelopes close the experiment connection;
after `turn/start` may have begun they remain `unknown` / `submitted:null` /
`retryAllowed:false`, not a definite refusal or acceptance.
- Added 12 actual WebSocket-over-Unix/CLI-envelope controls: result plus
`-32600`/`-32601`, null result plus error, absent outcome fields, malformed
error object and string/boolean/fractional/unsafe codes, clean `-32600`/
`-32601` refusals, and clean internal error. Each observes exactly one
`turn/start`, with no queue, fallback, replay, policy change or body in output.
- macOS arm64, Node 24.16.0: strict typecheck and all **32 scoped tests passed**.
With only the original adapter source restored, the three contradictory
result/error regressions failed (29 passed / 3 failed); the fixed source was
then restored. Dependencies installed with scripts disabled; audit: zero
vulnerabilities. This is a private fake owner, not a native/model test.
- The earlier 0.159.2 native observations above are historical and were not
repeated. Existing UI-lifetime/admission/concurrency/owner-generation and
platform/SSH limitations remain; this correction does not qualify support.
82 changes: 82 additions & 0 deletions experiments/codex_side_session/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions experiments/codex_side_session/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "session-peer-side-session-experiment",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"test": "node --experimental-strip-types --test test_side_session.ts",
"typecheck": "tsc --noEmit --strict --module NodeNext --target ES2022 --allowImportingTsExtensions --skipLibCheck side_session.ts test_side_session.ts"
},
"dependencies": { "ws": "8.22.0" },
"devDependencies": { "@types/node": "22.13.10", "@types/ws": "8.18.2", "typescript": "5.9.3" }
}
Loading
Loading