Skip to content

✨ Launch Codex sessions with <Session.Launch> (#755) - #764

Open
taras wants to merge 22 commits into
agent/issue-717-terminal-packagesfrom
agent/issue-755-codex-native-launch
Open

✨ Launch Codex sessions with <Session.Launch> (#755)#764
taras wants to merge 22 commits into
agent/issue-717-terminal-packagesfrom
agent/issue-755-codex-native-launch

Conversation

@taras

@taras taras commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Why

<Session.Launch> knew Codex's codex resume <id> command shape but refused to use it because XMD had not yet proved that ACP's provider-returned identity names the exact conversation the native Codex UI resumes.

This PR completes that proof and makes Codex available beside Claude at the root and inside a terminal-grid pane.

Closes #755. #770 is independent: it tracks Claude's provider-origin resume identity and does not block Codex's provider-native continuation.

What changes

Before:

  • <Agent name="codex"><Session.Launch>…</Session.Launch></Agent> refused because Codex was not advertised for native launch.
  • Codex had a known resume command, but no admitted provider-returned identity, materialization, continuation, or replay contract.

After:

  • Codex ACP/App Server supplies the canonical thread identity through the adapter's explicit _meta.agentSessionId assertion.
  • A newly created Codex conversation receives exactly one announced, fixed, tool-free codex-materialization.v1 turn. The authored instructions are not in that turn; they govern the first native user turn.
  • codex resume <canonical-id> opens the exact prepared conversation. Resume and replay never spend the materialization turn again.
  • Codex and Claude can launch concurrently in separate <Terminal> panes. Notifications, readiness, input, output, and teardown stay in the selected pane.
  • A later <Session> or <Prompt> performs a zero-turn live resume/load check and refuses before returning or prompting if the provider omits or changes the canonical identity.
  • Compatible Codex releases are admitted by protocol, command shape, capability, and host profile—not by an exact version or executable digest.

How it works

<Session.Launch>
  → observe and admit the live Codex command shape
  → publish session-route.v3 with immutable audit binding
  → create ACP conversation and receive provider identity
  → one fixed materialization turn for a new conversation
  → retain materialized checkpoint
  → release ACP ownership
  → open codex resume <exact identity> in the selected terminal

The V3 route is exactly {schema, route, provider, agent, sessionKey, executableBinding}. The route and launch journal retain two accounts of the original observation and must agree; neither is compared to a later executable as an authorization lock.

The materialization response and usage are retained only as filtered lifecycle evidence. They are not rendered as document output, do not contain the authored task, and cannot use tools or permissions. Unsafe or incomplete materialization refuses before the native process starts.

Provider-native continuation carries the retained canonical identity into the actual ACP resume/load. The live returned identity is checked before reconciliation, checkpointing, Session return, establishment publication, or prompt submission. Missing or different identity preserves the original conversation and refuses identity-unavailable.

Review guide

Start with: specs/decisions.md DEC-018 and packages/acp/src/native-launch.ts.

Then review:

  1. packages/acp/src/session-route.ts and packages/acp/src/provider.ts — V3 publication, materialization, continuation, replay, and ownership.
  2. packages/core/src/agent/launch-*.ts — the retained materialized phase and reconstruction from a completed prompt.
  3. packages/terminal/src/native-launcher.ts and pane-launcher.ts — provider-neutral notification routing.
  4. packages/acp/tests/native-reconnect.test.ts — real ACPX resume/load identity enforcement for Prompt and eager Session.
  5. packages/test-agent/tests/terminal-grid-native-launch.test.ts — TG22 mixed Claude/Codex pane behavior.
  6. The vendored adapter and ACPX provenance manifests, then the opt-in proof documents.

Look carefully at:

  • identity comes only from the provider response, never ACP sessionId, request echo, route key, transcript, or UUID shape;
  • no picker, --last, replacement conversation, widened cwd, or widened permissions appears;
  • completed replay performs no live observation, materialization, ACP work, or native launch;
  • cleanup failure withholds quiescence and leaves recovery ownership intact.

What must stay true

  • One canonical conversation — changed or missing live identity refuses before a Session or turn; Tier NR crosses the actual vendored ACPX subprocess boundary.
  • One materialization turn — only initial Codex construction spends it; core MR/MJ and ACP MZ rows cover prompt-first retention, replay reconstruction, interruption, and unsafe outcomes.
  • No release pin — a compatible changed executable continues the exact retained identity without rewriting the route or journal.
  • Pane-local launch — TG22 proves distinct Claude/Codex endpoints run concurrently, Codex materializes before readiness, Claude spends zero turns, and the root launcher is never entered.
  • Conservative teardown — refusal closes owned resources; unproved cleanup retains ownership and requires recovery.

How to verify it

Focused implementation evidence at Architect-reviewed signed commit fffc286b0b5333cfa972b8512519abcc036a4b9d:

deno task test packages/acp/tests/adapter-protocol.test.ts scripts/tests/adapter-vendor.test.ts
deno task test packages/acp/tests/native-launch.test.ts packages/acp/tests/session-route.test.ts packages/acp/tests/provider.test.ts packages/acp/tests/acceptance.test.ts
deno task test packages/core/tests/agent-session-launch.test.ts
deno task test packages/terminal/tests/native-launcher.test.ts packages/test-agent/tests/native-launch.test.ts packages/test-agent/tests/terminal-grid-native-launch.test.ts packages/terminal-tmux/tests/terminal-grid-tmux.test.ts
deno task test packages/acp/tests/codex-surfaces.test.ts packages/acp/tests/terminal-screen.test.ts
deno task test packages/acp/tests/native-reconnect.test.ts packages/acp/tests/native-launch.test.ts scripts/tests/acpx-vendor.test.ts
deno task check

All pass locally. The semantic implementation received Architect PASS at fffc286b0b5333cfa972b8512519abcc036a4b9d. Delivery head 97fda6aa7b5f85db747c066898fd3ef3c6d1dbeb adds the portable TG22 sort, includes terminal in the staged JSR consumer closure, normalizes Linux ps's no-terminal spelling at the POSIX boundary, and declares the extracted terminal packages only in the private root test host. It also keeps the GN cross-process journey beneath its unchanged deadline by invoking the production TestAgent worker from a focused subprocess entry instead of repeatedly cold-loading unrelated CLI assemblies; real ACPX, controller transport, journals, lifecycle, and public CLI dispatch evidence remain intact. Fresh Architect reviews passed both delivery repairs. deno task verify:clean also passes, proving setup, offline builds, release compile, resolution, and concurrent Deno/Node/Bun consumption leave repository-owned dependency state unchanged.

One Bun 1.4.0 package import failed once with EISDIR during overlapping local checks and passed unchanged alone and in independent review. It is tracked separately as flake #778; it does not weaken or close the package-boundary evidence.

The real-provider proofs are opt-in because they spend account model turns. They remain frozen evidence and were not rerun during implementation or architectural review:

  • packages/acp/src/CodexNativeLaunch.test.md
  • packages/acp/src/CodexZeroNativeTurnExit.test.md

For a manual mixed-pane check, use one document containing explicit Claude and Codex <Agent> scopes with uniquely named <Session.Launch> sessions. Run it twice from the same cwd: the first run must open both native UIs concurrently and announce exactly one Codex materialization; the second must resume both histories without another Codex materialization.

Scope

Included

  • Codex provider-returned native identity and advertisement
  • fixed one-turn materialization and durable replay
  • strict V3 construction routes
  • provider-native continuation for both eager Session and Prompt
  • protocol/shape/capability/host admission across compatible releases
  • terminal notification routing and mixed-grid evidence
  • vendored adapter/ACPX provenance and offline proof tooling

Intentionally unchanged

  • Claude native launch and client-native identity behavior
  • Node and Bun's existing native-launch host boundary
  • terminal-grid layout and tmux provider lifecycle
  • <Session.Launch> result capture (Return launch details from <Session.Launch> #753)
  • native transcript capture or reconstruction

New abstractions

  • provider-native-continuation is separate from native launch because proving that a native UI can resume a session does not prove ACP can later rejoin it.
  • session-route.v3 records the provider-returned construction route and immutable executable audit binding.
  • Terminal notification lets preparation announce a required pre-launch action in the same root or pane terminal that will receive the native UI.
  • Each has concrete production consumers and discriminating evidence.
  • No speculative functionality is included.

Generated or mechanical changes

  • The Codex adapter tarball and generated/snapshots.ts come from the reviewed feat/session-native-identity adapter commit recorded in its manifest and provenance.
  • The patched ACPX generated snapshot, declarations, manifest digest, and provenance carry live expected-identity confirmation through its existing vendored-snapshot mechanism; immutable upstream bytes remain unchanged.
  • Generic test fixtures that used codex as a placeholder agent are renamed so they do not accidentally exercise the now-real adapter.

Risks and limitations

  • The admitted real-proof host envelope is macOS arm64. Other host pairs refuse until independently proved.
  • Shape probing cannot prove every hidden behavior of a future CLI release; recovery is to remove the affected capability's proved profile, which fails closed without rewriting routes or journals.
  • The two opt-in real Codex proofs consume model turns and must be armed deliberately.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated and mechanical changes are identified with their source.
  • The description matches the reviewed diff and focused results.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 7 redundant comments. Inline suggestions to remove them below.

Comment thread packages/acp/src/native-launch.ts Outdated
codex: {
launcher: "codex",
// Codex creates the conversation through ACP and reports what it is called.
// XMD supplies nothing here and accepts only that assertion.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// XMD supplies nothing here and accepts only that assertion.

Comment thread packages/acp/src/native-launch.ts Outdated
//
// The first thing the Codex ACP adapter consults when deciding which
// Codex to run, so the build that creates the session through ACP is the
// build the native UI then resumes it with.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// build the native UI then resumes it with.

Comment thread packages/acp/src/native-launch.ts Outdated
// resume <id>` reads rollouts — so a thread ACP created and nothing has
// spoken in is refused by name. One turn closes exactly that gap and
// nothing else, which is why the prompt asks for an acknowledgement and
// forbids the work the session was prepared for.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// forbids the work the session was prepared for.

// child of a build nothing has observed, which is the one thing a bound
// session may not talk to. Nothing on that path needs the answer: a
// client-native session is created by a native process, and where ACP does
// serve one, the establishment itself reports being unable to.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// serve one, the establishment itself reports being unable to.

Comment thread packages/acp/src/provider.ts Outdated
if (route.schema !== "session-route.v3") {
// Constructed before any build was recorded. A build observed now says
// which build is installed today, not which one issued this identity, so
// there is nothing to compare and nothing to continue.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// there is nothing to compare and nothing to continue.

Comment thread packages/acp/src/provider.ts Outdated
}
const agentCommand = agentCommandOf(prepared);
// An existing managed entry, or a durable record ACPX already kept, is
// provider state — and existing history is never reclassified.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// provider state — and existing history is never reclassified.

// established placement has one of its own.
// established placement has one of its own. A build alone does not
// defer anything — a session the provider names is still constructed
// by this ensure, bound to the build that names it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// by this ensure, bound to the build that names it.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

PR #764: ✨ Launch Codex sessions with <Session.Launch> (#755)

90 files, +20491 / -3874

Scope

🔴 PR has 24365 lines changed. Split into focused PRs.

🟡 24365 lines changed. PRs under 400 receive more thorough review.

🟡 90 files changed. Are all changes related?

🟡 Changes span 14 directories.

🟡 PR mixes config and source changes.

Structural

🟡 Type declarations with no consumers: LaunchPhase, ExecutableRefusal.
Symbol Declared at Refs in diff Why flagged
LaunchPhase packages/core/src/agent/launch.ts:37 1 referenced ≤1× within the added diff (pre-existing usages not counted)
ExecutableRefusal packages/runtime/executable-observer.ts:35 1 referenced ≤1× within the added diff (pre-existing usages not counted)

Oxlint structural signals:

  • no-unused-vars ×14: packages/core/src/agent/launch-owner.ts, packages/acp/src/provider.ts, packages/core/src/expand.ts (+1)
  • no-redundant-type-constituents ×12: packages/terminal-tmux/src/pane-channel.ts, packages/acp/tests/fixtures/claude-native-to-acp-proof.ts, packages/acp/tests/fixtures/codex-native-launch-proof.ts (+4)
  • no-empty-function ×5: packages/terminal-tmux/src/pane-worker.ts, packages/terminal/src/native-launcher.ts, packages/acp/src/provider.ts (+1)
  • no-unnecessary-type-assertion ×3: packages/acp/tests/helpers.ts, packages/core/src/expand.ts
  • no-unnecessary-type-arguments ×1: packages/terminal-tmux/src/pane-channel.ts

Slop

  • packages/acp/src/provider.ts:1202 (removed)
  • packages/acp/src/provider.ts:1876 (removed)
  • packages/terminal-tmux/src/provider.ts:414 (removed)
  • packages/terminal-tmux/src/tmux-grid.ts:286 (removed)
  • packages/terminal/src/controlled-composite.ts:158 (removed)
  • packages/terminal/src/controlled-composite.ts:164 (removed)

Oxlint slop signals:

  • no-console ×3: packages/cli/src/cli.ts
  • no-inferrable-types ×2: packages/core/src/expand.ts

Static Analysis

Oxlint: 124 diagnostics across 23 files (26 rules)
Density: 0.006 violations/added-line

consistent-function-scoping (16): packages/acp/tests/fixtures/codex-native-launch-proof.ts, packages/terminal-tmux/src/pane-worker.ts, packages/test-agent/src/provider.ts (+1)
no-unused-vars (14): packages/core/src/agent/launch-owner.ts, packages/acp/src/provider.ts, packages/core/src/expand.ts (+1)
no-unsafe-type-assertion (13): packages/acp/tests/helpers.ts, packages/acp/tests/fixtures/claude-native-to-acp-proof.ts, packages/cli/src/cli.ts (+5)
no-floating-promises (12): packages/test-agent/tests/fixtures/terminal-grid-worker.ts, packages/terminal-tmux/src/pane-channel.ts, packages/acp/tests/fixtures/claude-native-to-acp-proof.ts (+5)
no-redundant-type-constituents (12): packages/terminal-tmux/src/pane-channel.ts, packages/acp/tests/fixtures/claude-native-to-acp-proof.ts, packages/acp/tests/fixtures/codex-native-launch-proof.ts (+4)
unbound-method (10): packages/acp/tests/helpers.ts, packages/core/src/agent/launch-authority.ts, packages/acp/tests/fixtures/terminal-screen.ts (+3)
no-shadow (5): packages/runtime/deno-executable-observer.ts, packages/terminal/src/posix-launcher.ts, packages/acp/src/provider.ts (+1)
no-empty-function (5): packages/terminal-tmux/src/pane-worker.ts, packages/terminal/src/native-launcher.ts, packages/acp/src/provider.ts (+1)
no-underscore-dangle (4): packages/terminal-tmux/src/pane-protocol.ts
no-useless-spread (4): packages/terminal-tmux/src/pane-channel.ts, packages/acp/src/provider.ts, packages/acp/tests/helpers.ts
no-base-to-string (4): packages/core/src/expand.ts
no-control-regex (3): packages/acp/tests/fixtures/claude-native-launch-proof.ts, packages/acp/tests/fixtures/claude-native-to-acp-proof.ts, packages/acp/tests/fixtures/codex-native-launch-proof.ts
no-console (3): packages/cli/src/cli.ts
no-unnecessary-type-assertion (3): packages/acp/tests/helpers.ts, packages/core/src/expand.ts
consistent-return (3): packages/terminal-tmux/src/pane-channel.ts, packages/cli/src/cli.ts, packages/terminal-tmux/src/attach-client.ts
no-useless-escape (2): packages/acp/tests/fixtures/codex-native-launch-proof.ts, packages/acp/src/native-launch.ts
no-inferrable-types (2): packages/core/src/expand.ts
prefer-string-starts-ends-with (1): packages/acp/src/native-launch.ts
no-array-sort (1): packages/acp/src/native-launch.ts
no-new-array (1): packages/acp/tests/fixtures/terminal-screen.ts
no-useless-fallback-in-spread (1): packages/core/src/expand.ts
no-unmodified-loop-condition (1): packages/cli/src/cli.ts
no-unnecessary-type-arguments (1): packages/terminal-tmux/src/pane-channel.ts
no-unnecessary-template-expression (1): packages/acp/src/provider.ts
restrict-template-expressions (1): packages/core/src/expand.ts
no-implied-eval (1): packages/core/src/expand.ts

Correctness

No extraneous code patterns detected.

taras and others added 15 commits September 7, 2026 04:42
An advertised adapter name selected a command shape and, by omission,
authorized acting on a session a native UI may be in. DEC-017 makes the
authority a point — adapter, capability, exact reported version, host OS
and architecture — so one real-CLI proof admits exactly what it proved.

The host states the machine as a value beside the coordinator, route
store and observer, so shared provider and session assembly never detects
the runtime it happens to be on.
A native launch acknowledged ownership quiescence whenever `stop()` on its
scope succeeded. A launch that completed naturally tore itself down first, so
a finalizer that failed on the way reached the owner as a crash while `stop()`
afterwards succeeded against an already-settled scope — proof that the scope is
settled now, never that the cleanup which failed had succeeded. The session was
released idle with an unproved child behind it.

The invocation now suspends once the native child settles, so the one teardown
there is happens under the one call that reports it. Quiescence is still
withheld unless the child settled, every finalizer of that invocation
completed, and the provider holds no handle; otherwise the exclusion is
released with the record left active, and the next acquisition is refused as
session-recovery-required before any spawn, ensure, allocation or route work.
The original cleanup failure is what the launch caller observes.
An exact-version compatibility point admitted a capability from the release
string an executable happened to print. That is the wrong evidence twice: a
release name says nothing about which operations a build declares, and pinning
one refuses every later build that declares the same shape.

Admission is now four exact facts — the adapter's own versioned protocol, the
capability being asked for, the profile its observer returned, and the host's
platform and architecture. An Agent registry name or a launcher command admits
nothing. The two Claude capabilities are read independently, so neither is ever
inferred from the other having been proved.

The host-owned observer is generalized: it resolves and canonicalizes the
launcher, requires an executable regular file, hashes it once, and asks that
same exact path whatever read-only queries the adapter declared, returning
settled status and captured output. It knows no provider and parses nothing.
Reading Claude's `--help` structurally — caller-supplied identity, exact
resume, private-file instructions — stays in the adapter, so additive options,
prose and wrapping do not change the answer.

Version becomes optional evidence beside the digest rather than a gate. A build
that will not name its release, names it in words this adapter does not
recognize, or names several is bound by its bytes alone. Build equality is
asymmetric: the digest decides, a retained release is a claim the live build
must still make, and a record that named none is not rewritten when one appears.
The help probe read flag spellings. `--session-id <name>`, `--resume <url>`
and `--system-prompt-file <text>` all satisfied it, so a build that accepts
the spelling and means something else by it was admitted for native launch —
and the launch would then hand a UUID to an option that takes a name.

Each declaration is now parsed into its spellings, its value placeholder,
whether that value is required, and its own description. Chosen identity
needs the sole `--session-id` to require a UUID. Exact resume needs the sole
`--resume` to name session identity, either in its placeholder or, for the
uncommitted `[value]` spelling Claude ships today, in that entry's own words.
Private instructions need a file-valued `--system-prompt-file`, or the
`--system-prompt[-file]` family spelling inside a parsed entry. Product is
read from dedicated unindented lines, so a compatibility claim or a quoted
usage example no longer says what this executable is.

Two entries declaring one spelling is unreadable, not a choice between them.
Three surfaces that say the opposite of the contract were admitted by it.
`Resume a conversation by URL; session ID is not supported` contained both
words the read looked for. `Inline text; file paths are not supported` was
accepted from a description while its argument was `<text>`. `Claude Code
compatibility wrapper` began a line with the product name.

Prose is now read one clause at a time, and a clause that withdraws what it
states admits nothing: a sentence states one thing, and its neighbours are
not it. A generic `[value]` resume needs the phrase saying the argument is
what the conversation is named by, not two words scattered through it. The
private instruction file is decided by the argument alone, since a
description explains a value rather than changing it. A product line names
its subject and then describes it, so words running on into a longer name
have named something else.

The 2.1.241 and 2.1.263 surfaces, additive options, wrapping and unrelated
prose are unchanged, and no version is consulted.
A route's `ExecutableBuildBindingV1` was a release lock: every continuation
held the live observation against the retained one, so upgrading Claude
stranded every session the previous build had opened. It is now immutable
audit evidence — written once at first publication, never rewritten, and never
compared with a live build.

What authorizes acting on an existing session is what the installed executable
independently proves: the route's stable adapter protocol, the capability the
work needs, the shape its own probe recognizes, and this host. Which build
accepted the identity first is history, and history is not a capability.

Removes the four live-vs-retained comparisons (attachment, native resume,
concurrent winner, replay) and the `buildDrift` helper they shared. The one
remaining comparison stays: a route and its prepared journal are two durable
accounts of a single observation, so a difference between them is still a
replay that cannot say which session it is resuming.

Tier XR takes a 2.1.261 session to a 2.1.263 build across native resume, ACP
attachment, prepared-only replay, detached replay, a concurrently published
winner, and a build reporting no release — each asserting the exact retained
identity, unchanged audit evidence, no allocation or republication, and the
live executable's own path. XR7 and XR8 hold the fail-closed boundary that a
release change is not a protocol change.
A V2 construction route fixes the stable protocol its identity was published
under through its provider, agent and launcher contract. Continuation read that
protocol off whatever adapter happened to be registered under the launcher
instead, so an adapter declaring another protocol — beside a host policy that
had proved exactly that protocol, for that capability, in that shape, on this
machine — could adopt a session it never constructed. ACP attachment also never
held the route's retained launcher to the live adapter at all.

The pin is compiled in and reachable through no host-supplied dependency, and it
is asked before the executable is observed: which conversation this is was never
a question about a build. Native resume, ACP attachment, concurrent-winner
adoption and both incomplete replay phases now require the live adapter's
launcher and protocol to be the ones the retained contract names, and the
independent admission of the current executable's capability, shape and host
follows it. A refusal observes nothing, allocates nothing, launches, ensures,
prompts and constructs nothing, and leaves the route and the journal exactly as
their first publication left them.

Executable bindings stay what they became: immutable audit evidence, and a
cross-check between two durable accounts of one observation — never a live
same-build gate. The source contracts that still described them as one are
reconciled.

Evidence: XR9 takes an injected adapter speaking another protocol, with the host
policy admitting that protocol, through native continuation, ACP attachment,
both replay phases and concurrent-winner adoption, and asserts the executable is
never even observed. XR10 refuses an attachment whose retained launcher is not
the live adapter's. Planting each seam's absence back one at a time fails only
that seam's case, and reading the pin off the adapter fails both cases and
nothing else.
An empty route read settles nothing: a concurrent publication is only
revealed by publishing. Until now a run whose read found no route fell
straight through to observing the executable, allocating an identity and
writing a candidate, and asked whether it could account for the protocol
only after meeting the winner — having already acted on behalf of a
session it was never entitled to construct.

The pin is now asked twice from one reading. Prospectively, before any
observation or allocation, against the live adapter's launcher; and
again of the record that actually won, whose route may differ from the
candidate's. The legacy V1 path is untouched.

Reconcile the remaining same-build continuation wording across the
touched source set. A retained binding is immutable audit evidence and
is only ever held to the prepared journal derived from the same
observation, never to a build installed later.
`packages/terminal` is created by this stack's extraction commit, which was
authored while the repository pinned `@effectionx/node` 0.2.4. Main has since
moved to 0.2.5 (#748). A file a commit creates conflicts with nothing, so the
rebase carried the stale pin forward beside a root that now asks for 0.2.5, and
`deno task setup` refused: the frozen lockfile carries no `npm:@effectionx/node@0.2.4`
specifier for a workspace member to resolve through.

The pin now matches the one every other package and the root already use. The
0.2.4 entries that remain in `deno.lock` are `@effectionx/process@0.8.1`'s own
transitive dependency, and they are present on main unchanged. No other
dependency version moves.
Three call sites this stack added kept compiling against surfaces main has
since changed. Each pair of edits touched different lines, so the rebase merged
both sides without a conflict and the mismatch only surfaced under `deno check`.

`xmd plan` used to settle a whole agent stack through a local three-argument
helper. Main replaced that with the authorship-only resolver, which takes two
arguments because the command starts no program, so the terminal-grid installer
this stack threaded through no longer has a parameter to occupy. `xmd run` is
unchanged and still installs it: the installer reaches `installRunAgentStack`
through `resolveAgentStack`, which is the one path with a terminal to give away.

The syntax suite's two terminal-grid rows still named `syntaxCatalog` and the
local `parseCatalog`, which main renamed to `syntaxSymbols` and `parseSymbols`.
Only the names move; both rows assert the same origins, authored forms,
descriptions and reserved-name exclusions they always did.

The launcher suite imported `reap` from the provider-neutral contract after this
stack split the module in two. `reap` ends a POSIX child, so it comes from
`posix-launcher.ts` beside `installForegroundLauncher`, and the contract import
keeps only what a consumer that never reaches a process needs.
The rebase reintroduced fourteen listeners the ownership rule refuses: a
raw `once()` in the POSIX launcher's spawned callback, subscriptions whose
`ensure()` was yielded after them — itself a suspension an owner can be
halted in — removals hidden behind a helper the pairing cannot be read
from, a self-removing socket handler a cancelled scope never reaches, and
three process signals registered under a computed name.

Every one of them now names its receiver, event and handler on both sides,
with the removal established before the subscription or held by a `finally`
around it. The launcher keeps its inline exit/error race, its complete
reap and a spawned callback that fires exactly once: the handler comes off
when the start arrives and again on every way out, so a cancelled or failed
launch publishes no readiness at all. `usePaneChannels` remembers each
accepted socket with the handler it carries and detaches every one of them
at teardown, including a socket that closed on its own.

FL10 reads the child's own listener counts after delivery, after a startup
failure and after cancellation, then replays `spawn` on it: restore the raw
`once()` and the row fails. TW14 already held the tmux half, and fails if
the new channel detach is removed.

Terminal-grid TG3 expects the catalog version main's Syntax v2 migration
publishes.

Co-Authored-By: Claude <noreply@anthropic.com>
@taras
taras force-pushed the agent/issue-755-codex-native-launch branch from 24551e8 to 44826a9 Compare September 7, 2026 11:42

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 14 redundant comments. Inline suggestions to remove them below.

useMachineSessions(),
// The machine, read at the one place that is actually running on it. A
// native capability is admitted for an exact OS and architecture, so
// those cross from here as values rather than being detected below.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// those cross from here as values rather than being detected below.

Comment thread packages/cli/src/deno.ts
useMachineSessions(),
// The machine, read at the one place that is actually running on it. A
// native capability is admitted for an exact OS and architecture, so
// those cross from here as values rather than being detected below.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// those cross from here as values rather than being detected below.

if (typeof code !== "number") {
// A child that produced no status did not answer, whatever it wrote on the
// way. Reporting output beside an unknown status would invite reading it as
// an answer.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// an answer.

child.on("exit", onExit);
// Established before the subscriptions and naming every one of them:
// entering an ensure() is itself a suspension, so a scope halted while it
// registers unwinds with nothing on it at all.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// registers unwinds with nothing on it at all.

live.add(socket);
closable++;
const onSocketClose = (): void => {
// A socket that closed is nobody's to close again.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// A socket that closed is nobody's to close again.

// child inherits this process's, so a pane collapsing it to `{}` started
// the program with no environment whatsoever — no `TERM`, so no colour, and
// no `PATH` or `HOME` either. An environment that *is* supplied crosses
// exactly, gaining nothing ambient.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// exactly, gaining nothing ambient.

// tmux sends its first record immediately on attach, so a handler
// attached after `exec()` returns is attached after that record could
// already have been forwarded. Nothing about the parsing below changes —
// the stream is still read and classified exactly as before.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// the stream is still read and classified exactly as before.

}
// The default shell starts: a suite that says nothing about a pane
// wants a pane that works, and one that never reported a spawn would
// hang the readiness barrier instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// hang the readiness barrier instead.

} finally {
// Counted down however the shell left — returned, thrown, or
// cancelled — because a shell a suite can still find is a shell the
// provider is still holding.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// provider is still holding.

started.once("spawn", () => spawned());
// reports having started. Off on arrival and off again in the `finally`,
// so one start is reported exactly once and a launch that was cancelled or
// never started reports none at all.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// never started reports none at all.

@taras
taras changed the base branch from agent/issue-732-tmux-grid to agent/issue-717-terminal-packages September 7, 2026 11:46
@taras
taras marked this pull request as ready for review September 7, 2026 11:51
@taras taras changed the title Launch Codex sessions with <Session.Launch> (#755) 🔒 Admit native Agent capabilities by proved protocol shape (#717) Sep 7, 2026
@taras taras changed the title 🔒 Admit native Agent capabilities by proved protocol shape (#717) ✨ Launch Codex sessions with <Session.Launch> (#755) Sep 7, 2026
Include the extracted terminal package in SYN47's explicit published payload closure and assert its lifecycle subpath resolves to the staged file. Production manifests already declare the dependency; preserve canonical workspace imports and the no-reexport package boundary.
Normalize Linux ps's no-terminal spelling to the existing process-interface marker without changing real device names or holder proofs. Declare the extracted terminal packages only in the private root test host, keeping provider dependencies out of the neutral domain. Freeze both boundaries with regressions; regenerate the two pnpm workspace links through setup.
Run the native-grid TestAgent harness through a focused subprocess entry that installs the compiler and invokes the real worker. Keep ACPX, controller transport, scenarios, journal, lifecycle, public CLI dispatch coverage, and the 20-second document deadlines intact. Require the GN2 journey to complete successfully as well as checking retained identity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Launch Codex sessions with <Session.Launch>

1 participant