pi-openai-codex-compat
OpenAI Codex compatibility for Pi with native compaction, fast mode, and Codex-optimized capabilities
Package details
Install pi-openai-codex-compat from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-openai-codex-compat- Package
pi-openai-codex-compat- Version
0.0.21- Published
- Oct 2, 2026
- Downloads
- 2,559/mo · 809/wk
- Author
- kaanozdokmeci
- License
- MIT
- Types
- extension
- Size
- 1,010.8 KB
- Dependencies
- 3 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./extensions/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-openai-codex-compat
OpenAI Codex compatibility for Pi, combining priority fast mode, native Codex compaction, and Codex-optimized model features in one Pi package.
Features
- Request-level fast mode: keeps the canonical
openai-codexprovider id and models selected while addingservice_tier: "priority"at the request boundary. - Native compaction: uses Codex
remote_compaction_v2for/compact, Pi threshold compaction, context-overflow recovery, and an optional percentage threshold. - Codex
apply_patch: provides an optional patch tool with the Codex grammar, parser, fuzzy matcher, overwrite semantics, filesystem behavior, model-facing result format, structured history, and diff-oriented TUI rendering. Pi sends it as an OpenAI custom grammar tool when the model supports that protocol and as a normal function tool otherwise. - Codex command tools: replaces an active Pi
bashtool with either the persistentexec_command+write_stdinpair or the one-shotshell_commandtool. Unified exec is the default and supports optional PTY sessions throughnode-pty. - Opt-in patch diagnostics: records failed
apply_patchrequests with pre-execution text snapshots or binary metadata for every instruction, outcomes, and Pi/Codex identifiers. - Standalone image generation: exposes Pi's dotted
image_gen.imagegentool as a native Responses namespace and executes generation or edits through the Codex Images endpoints. - Standalone web search: exposes Pi's dotted
web.runtool as a native Responses namespace and executes search and browsing through Codexalpha/search. - Dedicated Codex tool UI: renders command tools,
apply_patch,image_gen.imagegen, andweb.runon a shared configurable surface with compact summaries andCtrl+Oexpansion. - Hosted web-search fallback: injects native
web_searchonly whenweb.runis inactive, with cached, indexed, or live modes. - Native request controls: configures Responses API text verbosity and reasoning summaries.
- Draft settings pane:
/codex-settingsstages edits before applying them. Apply to session stores them with the session without changing configuration files. Ctrl+S saves and applies them. Escape discards only unapplied drafts. - Session-aware footer: shows the current Pi session ID on the first line and appends non-default Codex request modes to the model side of Pi's normal second line.
Pi provides the Codex OAuth flow and model catalog. At session start, this package overrides the built-in openai-codex runtime under the same provider id so ordinary responses and remote compaction share one transport, parser, native-history store, and sticky WebSocket session.
Requirements
- Node.js 22.19 or newer
- Pi
>=1.0.0 <1.1.0 - An OpenAI Codex login in Pi
Restart Pi after upgrading its runtime. /reload reloads extensions but cannot
upgrade the running Pi process. At load time, the extension requires the host's
transcript APIs, the Pi 0.99 APIs SessionManager.getEntryCount() and
ExtensionAPI.getSettings(), and a reported Pi version of at least 1.0.0. It
refuses to load otherwise. The load check does not reject newer releases: the
<1.1.0 bound is the package's peer range, which Pi does not enforce when it
installs packages. PI_PACKAGE_DIR can point an older executable at
newer package metadata, so the reported version alone does not establish
compatibility. The API checks detect missing capabilities from before Pi 0.99.
They do not distinguish a Pi 0.99 executable from Pi 1.0.0 when its metadata
points at the newer installation.
Authenticate through Pi if needed:
/login openai-codex
Pi's virtual models are not supported. The package's features check the selected model, not the model that answers a request. Pi's experimental virtual models, registered with pi.registerVirtualModel(), stay selected while Pi routes each request to a physical model. With a virtual model selected, the package deactivates its tools, omits fast mode and the other request controls, and leaves compaction and output-limit continuation to Pi. This holds even for a virtual model listed under openai-codex or one that routes every request to an openai-codex model. Routed openai-codex requests still use the package's transport. Select an openai-codex model directly to use the package's features.
Compatibility baseline and differences
The compatibility baseline is official Codex CLI 0.149.1, released August 24, 2026, at commit ff29a44391deccde0aba0f8390337d7f3c319ea4. It retains the 0.147.0 Responses Lite contract that groups direct function and custom declarations into one canonical functions namespace for namespace-capable providers. The 0.148.0–0.149.0 review adopted typed misalignment-policy failures while recording official unbounded connection recovery as an intentional bounded-transport deviation. The 0.149.1 review retained this package's checkpoint shape rather than exposing official Codex's new default-disabled retained-image budget. The official release compatibility log is the canonical release-by-release record of protocol and apply_patch alignment, intentional deviations, and excluded runtimes. This section is the package's user-facing compatibility contract. See the Responses Lite compatibility report and Codex caching and transport comparison for detailed request-path findings and live cache trajectories.
Configurable defaults that differ from Codex
| Area | This package by default | Official Codex | Configuration |
|---|---|---|---|
| Generated-image detail sent back to the model | Sends image tool-result content with input_image.detail: "auto". On GPT-5.6, auto uses original-size image accounting. |
Uses high. |
imageDetail: auto, low, high, or original. |
| Image-generation tool | Enabled whenever an openai-codex model is selected. Backend capability and account failures surface when the tool executes. |
Stable and enabled by default, but additionally gated by plan, model, provider, authentication, image-generation, and namespace capabilities. | imageGeneration: boolean. |
Standalone web.run |
Disabled by default; when enabled, preferred over hosted web_search and sent with the complete reserved schema and description. |
Enabled by default for gpt-5.6-sol through Responses Lite; otherwise subject to standalone-search feature and runtime gates. |
webRun: boolean. |
| Hosted web search | Disabled by default; when enabled, injected only for ordinary Responses while web.run is inactive. Responses Lite omits hosted tools. |
Omitted for gpt-5.6-sol while standalone web.run is available; otherwise defaults to cached mode when hosted search is supported. |
webRun and webSearch: disabled, cached, indexed, or live. |
| Coding mutation tools | Enables apply_patch and suppresses Pi's active edit and write tools. |
Chooses its tool surface from model metadata and runtime capabilities; there are no Pi edit or write tools to suppress. |
applyPatch: boolean. |
| Command tools | Replaces an active Pi bash tool with exec_command and write_stdin. |
Chooses unified exec or legacy shell from model metadata, platform, execution environment, and runtime capabilities. | shellTool: unified_exec or shell_command. |
apply_patch debug output |
Disabled; collapsed results show the normal visual summary and instruction rows. | Not applicable to Pi's tool-result renderer. | applyPatchDebug: boolean. |
apply_patch diagnostics capture |
Disabled; no separate request or filesystem snapshot artifacts are retained. | Codex owns its rollout diagnostics rather than writing this package's artifact format. | applyPatchDiagnostics: boolean. |
| Codex tool background | Uses a subtle theme-derived surface for extension-owned Codex tools. | Uses Codex's own TUI activity cells rather than Pi tool rows. | toolBackground: subtle, status, or none. |
| Auto-compaction trigger | Relies on Pi's reserve-token threshold unless a percentage is configured. | Tracks Codex's model/token-budget state before and between sampling steps. | autoCompactAtPercent: percentage or unset. Mid-response percentage boundaries use Pi's bounded compact-and-continue lifecycle, so Pi auto-compaction must remain enabled. |
| Fast mode | Uses the normal tier. | Uses the configured Codex service tier. | fastMode: boolean; true requests the priority tier. |
| Responses Lite | Disabled; supported models use ordinary Responses. | Enabled according to Codex model metadata. | responsesLite: boolean; true enables Responses Lite. |
| Text and reasoning request controls | Sends low text verbosity and automatic reasoning summaries, and omits reasoning.mode, which the Codex endpoint rejects. |
Resolves these controls through Codex configuration, model metadata, and turn state. | textVerbosity and reasoningSummary. |
web.run is a reserved GPT-5.6 tool name. Its declaration therefore reproduces the complete current Codex post-normalization SearchCommands schema and official tool description instead of using Pi's normal compact tool schema. This intentionally omits generated annotations such as format and minimum that Codex removes before sending the declaration to Responses.
Non-configurable implementation differences
| Area | Difference |
|---|---|
| Session storage | Pi remains the canonical session owner. Opaque Codex compaction checkpoints are stored in Pi compaction entries, otherwise lossy Responses output is stored in sparse custom native-response entries, and each change to the tools a turn request sends is stored in a custom request-tools entry. Official Codex owns a rollout/thread store directly. |
| Branching | Checkpoints and native response overrides follow Pi's active session branch. Official Codex uses its own thread, turn, rollback, fork, and context-window lineage. |
| Model switching | This package rejects model switches while the active Pi branch contains a native Codex checkpoint because the checkpoint is model-specific. |
| System instructions | Send the leading Pi system message as the prompt, as Pi AI's Codex adapter does: Responses Lite models prepend it as developer input after additional_tools; other models send it through Responses instructions. Later Pi system messages (/reload, section changes) travel inline as developer items on models that accept mid-conversation system messages and collapse into the leading prompt otherwise. A forced prompt replaces the leading message. Compaction snapshots and resumed sessions lead with Pi's replayed prompt without rewriting old checkpoints. |
| Dynamic tool declarations | Send the complete current tool set, replayed from Pi's toolsAdded and toolsRemoved system messages, in the top-level tools field on every request, as the official Codex client does. Tool declarations never travel inside input: inline additional_tools items and synthetic tool-search pairs made the Codex backend unreliable about which tools exist. A mid-session tool change costs one prompt-cache miss, and native checkpoints need no declaration rebase. |
| Turn metadata | Requests send a persisted installation id plus Pi-derived session, thread, context-window, turn, source, sandbox, request-kind, and nested compaction-operation metadata in client_metadata and compatible headers. The in-memory context-window number advances after successful compaction. One turn id is reused throughout a Pi agent run, while prewarm has its own id. First-party requests also carry Codex's model-and-tier routing hint. The provider captures the server-issued x-codex-turn-state once per agent run, replays it on WebSocket retries, SSE requests, and WebSocket-to-SSE fallback, and records all identity values in transport diagnostics. Pi does not reconstruct prior window number after extension reload/session resume or reproduce workspace Git/parent/subagent/Code Mode metadata. Each marked Pi tree branch receives its own persisted thread UUID. |
| Cache preparation | Before the first cache-enabled WebSocket turn, the package prewarms only the stable instruction/tool prefix: ordinary Responses uses empty input, while Responses Lite uses additional_tools plus the developer instructions. The first generated request then contributes only dynamic conversation input to the continuation. No explicit prompt-cache breakpoints are added. |
| Mid-turn compaction | Provider-boundary percentage compaction preserves a successful end_turn:false prefix as its own Pi assistant message, installs a checkpoint, and continues without synthetic model input. Pi threshold compaction normally runs after the agent response; after Codex output-token truncation, the extension queues a hidden continuation so threshold compaction completes before sampling resumes. Official Codex owns this sampling and compaction loop directly. |
| Provider-owned follow-up | Completed responses with end_turn: false continue immediately from completed native output without synthetic user input. Retryable response.failed and all response.incomplete events are resampled with the official five-retry stream budget, preserving completed output and cumulative usage while excluding unfinished attempt content. A max_output_tokens response that exhausts this budget still becomes Pi stopReason: "length" and uses the extension's unbounded host-level continuation recovery. |
| Transport recovery | WebSocket failures before model-visible output receive five fresh-connection retries before sticky SSE fallback; SSE transport failures receive five retries. Official Codex 0.149.1 separately retries sampling connection-establishment failures indefinitely with delays capped at 60 seconds. The package remains bounded because Fetch does not expose Reqwest's narrower connection-error category, and a phase-only approximation could indefinitely repeat requests that reached the server. |
| Compaction lifecycle events | Pre-turn percentage compaction writes through Pi's mutable session manager and cannot emit Pi's internal session_compact event through the public extension API. Mid-response percentage boundaries and manual, threshold, or overflow compactions initiated by Pi emit the normal lifecycle. |
| Header hooks | An internal percentage-compaction request reuses the already transformed provider headers. It cannot independently rerun Pi's before_provider_headers hook. |
| Native retained context | Deliberately differs from current Codex. The package retains recent user messages under a text-only 64k budget before the opaque compaction item, and keeps every developer and system message in place outside that budget so prompt updates are never compacted away. Current Codex applies a second installed-history filter that drops developer/system wrappers and non-real-user messages, can retain eligible structured agent commentary, and trims oversized function outputs before compaction. Codex 0.149.1 also adds a default-disabled mode that charges retained images to the budget and keeps each image with adjacent harness labels as an atomic boundary unit. Pi keeps its existing checkpoint shape by design. |
| Tool namespaces | Responses Lite groups Pi's ordinary function/custom declarations into upstream's canonical functions namespace and maps that default namespace back to bare Pi names. Pi registers dotted names such as web.run as exact flat identifiers, so the provider converts only the fixed extension-owned allowlist into non-default Responses namespace/member identities and rejects unknown or ambiguously flat namespaced calls. |
| Capability gating | Tool activation is based on the selected openai-codex provider plus package settings. It does not reproduce every official model-metadata, plan, feature-stage, executor, mode, or account gate. |
| Sandbox and approvals | Pi extensions run with full process permissions. Command tools, apply_patch, local image reads, generated-image writes, and sibling Codex endpoints do not use Codex's sandbox, permission-profile, environment, or approval lifecycle. |
| Image tool instructions | The package retains the server-reserved image-generation schema while replacing Rust-specific path annotations and Codex Code Mode instructions with model-facing descriptions, a prompt snippet, and system-prompt guidelines. Image-count bounds and selector exclusivity are enforced before execution. |
| Image artifact hint | When image saving succeeds, this package always returns the path hint, says “the generated image,” and has no 1,024-byte cutoff. Official Codex says “a generated image” and omits the hint when it exceeds 1,024 UTF-8 bytes. |
| Image artifacts | Generated files use Pi's agent directory and the Pi session/tool-call IDs. Official Codex uses its own artifact/output-directory lifecycle. |
| Web references | web.run structured results are retained branch-locally in Pi tool-result details rather than Codex extension events, and hosted native items are preserved for provider replay. Reference IDs are resolved remotely by alpha/search, as in Codex. Hosted citation annotations remain a separate unimplemented path. |
| UI | Pi renders its own conversation, footer, settings pane, branches, and compaction lifecycle. Extension-owned Codex tools have dedicated Pi renderers, but do not reproduce Codex app-server WebSearchItem or image-generation lifecycle notifications. |
Nested compaction metadata uses the official Responses Compaction v2 implementation and memento strategy. Manual compaction is user-requested and standalone; threshold and provider-boundary compaction are automatic context-limit operations in the pre-turn phase; overflow recovery is the corresponding mid-turn operation.
When a finalized user prompt creates a new /tree branch, the extension inserts a hidden,
context-free custom marker as that prompt's parent. Navigation alone writes nothing. The marker is
hidden by Pi's default, no-tools, and user-only tree filters and appears only in the all-entries
filter. Root session_id and prompt_cache_key remain stable, while the branch gets a UUID
thread_id, forked_from_thread_id, and thread-scoped window number. Switching threads closes the
old WebSocket and discards its incompatible previous_response_id baseline; the new full-history
request remains eligible to reuse the common backend-cached prefix under the unchanged cache key.
Tool and runtime coverage
The package implements the Codex-specific pieces that fit a provider compatibility extension:
- native Responses transport and history;
- remote compaction v2;
exec_command,write_stdin, andshell_command;apply_patch;- hosted
web_search; web.run;image_gen.imagegen;- namespaced tool serialization;
- text verbosity, reasoning summaries/mode, and priority service tier.
The following official Codex facilities are not exact equivalents in this package:
| Official Codex facility | Pi/package behavior |
|---|---|
view_image |
Pi read already accepts images; no canonical view_image alias is registered. |
update_plan, request_user_input, permissions, and environment tools |
Not implemented by this package. |
send_user_message_async |
Not implemented; Pi owns visible assistant output and user-message history, while this package does not own Codex's asynchronous message-injection lifecycle. |
| Context-window and clock tools | Not implemented. Compaction remains host/provider managed rather than model managed. |
| MCP resources and dynamic MCP tools | Pi does not provide this package with Codex's MCP runtime. |
| Plugin/connector installation | Not implemented; package installation remains an explicit Pi/user operation. |
Searchable tool_search catalog |
This package declares every tool at the top level and does not implement Codex's searchable deferred-tool catalog and ranking runtime. |
| Multi-agent V1/V2 coordination | Out of scope; no Codex agent tree, mailbox, task-path, or fork-depth runtime is implemented. |
| JavaScript Code Mode and yielded cells | Out of scope; no V8 isolate, nested tool namespace, cell storage, or wait lifecycle is implemented. |
| Goals, memories, and remote skill-resource tools | Not implemented; Pi's sessions, files, and native skills remain separate systems. |
| Remote/deferred execution environments | Not implemented. |
Each tool declares Pi tool annotations, which permission extensions can use to decide which calls to confirm:
| Tool | Read-only | Destructive | Open world |
|---|---|---|---|
exec_command, write_stdin, shell_command |
No | Yes | Yes |
apply_patch |
No | Yes | No |
image_gen.imagegen |
No | No | Yes |
web.run |
Yes | n/a | Yes |
None of the tools is idempotent. Pi does not verify annotations.
The provider passes every event of a turn request, over WebSocket and SSE, to Pi's provider_stream_event hook as the server sent it. Forwarding happens before the transport drops WebSocket response.metadata events, renames response.done to response.completed, or turns an error event into a failed turn. Prewarm and remote compaction requests are not forwarded. Pi reports a failing hook handler and continues the turn. When an SDK caller passes its own onProviderStreamEvent callback and it throws, the turn ends with an error without a WebSocket retry or the SSE fallback.
See OFFICIAL_CODEX_CLI_TOOL_CATALOG.md for the complete researched Codex tool inventory, and CUSTOM_CODEX_PROVIDER_WEB_REFERENCES.md for the unimplemented citation/reference design.
Install
From npm after a release is published:
pi install npm:pi-openai-codex-compat
From a local checkout:
pi install .
For a temporary development run:
pi --no-extensions -e .
Fast mode
Keep using an openai-codex model and enable Fast mode in /codex-settings. The extension adds service_tier: "priority" to ordinary and native-compaction requests without introducing another provider id or changing the selected model.
Fast mode applies to whichever built-in openai-codex model is selected. Priority-tier costs are reflected in Pi's usage totals, including when Codex echoes service_tier: "default" in its response.
Configuration
Create a global configuration file at:
~/.pi/agent/openai-codex-compat.json
The extension also creates openai-codex-compat-installation-id in the active Pi agent directory. It contains the stable UUID used for official Codex installation metadata and is reused across sessions.
A trusted project can override it at:
<project>/.pi/openai-codex-compat.json
Each session inherits the effective file-backed settings. /codex-settings
uses the same interaction contract as /anthropic-settings:
- Enter changes the selected value or opens its editor. It never implicitly saves.
- Space activates a result or inserts a space when search has focus.
- Tab / Shift+Tab move between search, results, and action controls.
- F1 opens scrollable details, full errors, and the exact save target.
- Edits remain drafts until explicitly applied or saved.
- Apply to session applies drafts without writing configuration files and stays open.
- Ctrl+S saves and applies changes without closing.
- Save and close saves and applies changes, then closes on success.
- Escape cancels a field editor or discards unapplied drafts and closes the main menu. It does not undo settings already applied to the session.
Search matches labels, configuration keys, and descriptions. The percentage editor supports presets and custom fractional values. Use inherited value removes an override. This differs from Pi default, which explicitly disables an inherited percentage trigger. Rows show configuration sources, environment locks, and model applicability. Pi's remapped selection and cancel keys are respected.
The global file is the normal save target. An existing trusted project override
is the target for that project. The target stays fixed while the menu is open.
Saving changes only edited overrides, preserves unknown keys and inherited values,
and preserves non-conflicting external edits. Conflicting edits are rejected for review.
Opening or closing without changes does not create a configuration file.
Reopening shows active session values, not freshly read file values. * marks
an unapplied draft. ~ marks an active session override or a value that differs
from the saved configuration. Details shows the saved value when it differs.
Ctrl+S also persists changes previously applied only to the session.
Session settings are stored as extension state entries in the Pi session,
outside model context. They survive menu closure, tree navigation, extension
reload, process restart, and switching away and back. Resuming the same session
restores its settings before provider operations. A new session, fork, or clone
has a separate identity and starts from configuration files.
Pi's --no-session mode cannot be resumed after exit. Pi also defers creating a
new session file until its first assistant message.
Environment-controlled fields remain locked and take precedence over restored
session preferences. Their values are not copied into session settings.
Applying and saving wait for Pi to become idle. Collect pending output with write_stdin or
stop running command sessions through /ps before applying or saving a command-tool switch.
Browsing settings and discarding drafts
never terminate those sessions. Escape cancels a pending operation without discarding
the draft. Failures leave the menu open. If a save reaches disk but cannot
be applied to the session, the menu reports that distinction and Ctrl+S retries
application. Cooperating writers use a .settings-lock file. An interrupted
writer can leave a lock that requires review; locks are not automatically
deleted. External editors do not participate in that lock.
Session overrides retain their original conflict checks across menu reopenings.
Conflicting external edits remain protected after reload or restart.
The effective settings are printed once when a TUI session starts. The footer shows the current Pi session ID alongside the working directory and optional session name. It shows fast only when enabled, and shows text verbosity or reasoning summary only when they differ from their defaults.
Example:
{
"fastMode": true,
"responsesLite": true,
"toolBackground": "subtle",
"shellTool": "unified_exec",
"applyPatch": true,
"applyPatchDebug": false,
"applyPatchDiagnostics": false,
"imageGeneration": true,
"imageDetail": "auto",
"webRun": false,
"autoCompactAtPercent": 90,
"webSearch": "disabled",
"textVerbosity": "low",
"reasoningSummary": "auto"
}
Defaults:
| Setting | Values | Default | Behavior |
|---|---|---|---|
fastMode |
boolean | false |
Adds service_tier: "priority" to requests while retaining the current openai-codex provider and model. |
responsesLite |
boolean | false |
Uses Codex's Responses Lite input envelope on supported models when enabled. By default, those models use ordinary Responses instructions and tools. |
toolBackground |
subtle, status, none |
subtle |
Controls the shared self-rendered background for command tools, apply_patch, image_gen.imagegen, and web.run. status uses Pi's pending/success/error backgrounds; none keeps the custom layout transparent. |
shellTool |
unified_exec, shell_command |
unified_exec |
Selects the command surface on openai-codex models. The selected Codex command surface replaces Pi bash only when bash was active. |
applyPatch |
boolean | true |
On selected openai-codex models, uses the extension's apply_patch tool instead of Pi's active edit and write tools. Other providers always use their normal Pi tool set. |
applyPatchDebug |
boolean | false |
Shows the exact model-facing tool result while a completed apply_patch result is collapsed. Expanded results continue to show the normal visual summary and complete diffs. |
applyPatchDiagnostics |
boolean | false |
Persists failed patch requests with pre-execution text snapshots or binary metadata for every instruction, outcomes, and trace identifiers. See apply_patch for storage and sensitivity details. |
imageGeneration |
boolean | true |
Enables the extension-owned image_gen.imagegen tool on selected openai-codex models. |
imageDetail |
auto, low, high, original |
auto |
Sets input_image.detail when an image tool result is sent back to the model. It does not change gpt-image-2 generation quality. |
webRun |
boolean | false |
Enables the extension-owned web.run tool on selected openai-codex models. When active, it replaces hosted web_search in the Responses tool list. |
autoCompactAtPercent |
number greater than 0 and at most 100, or null |
unset | Adds provider-boundary compaction independently of Pi's normal reserve-token threshold. Mid-response boundaries require Pi auto-compaction. A project value of null disables a global percentage threshold. |
webSearch |
disabled, cached, indexed, live |
disabled |
Controls hosted search and standalone-search external access. disabled removes hosted search but leaves an independently enabled web.run in cached-only mode; indexed prefers indexed content; live permits live external access. |
textVerbosity |
low, medium, high |
low |
Sets Responses API text.verbosity. |
reasoningSummary |
auto, concise, detailed, off |
auto |
Sets reasoning.summary when reasoning is enabled; off omits the summary parameter. |
Responses Lite supports exactly gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna,
gpt-6-astra, gpt-6-sol, gpt-6-luna, and gpt-6.1-sol. It remains opt-in.
Invalid JSON setting values are ignored and invalid JSON does not prevent Pi from starting. The settings pane reports malformed JSON instead of overwriting it. Project configuration is read only when the project is trusted.
Every setting can also be overridden for one Pi process with an environment variable:
| Setting | Environment variable |
|---|---|
fastMode |
PI_OPENAI_CODEX_COMPAT_FAST_MODE |
responsesLite |
PI_OPENAI_CODEX_COMPAT_RESPONSES_LITE |
toolBackground |
PI_OPENAI_CODEX_COMPAT_TOOL_BACKGROUND |
shellTool |
PI_OPENAI_CODEX_COMPAT_SHELL_TOOL |
applyPatch |
PI_OPENAI_CODEX_COMPAT_APPLY_PATCH |
applyPatchDebug |
PI_OPENAI_CODEX_COMPAT_APPLY_PATCH_DEBUG |
applyPatchDiagnostics |
PI_OPENAI_CODEX_COMPAT_APPLY_PATCH_DIAGNOSTICS |
imageGeneration |
PI_OPENAI_CODEX_COMPAT_IMAGE_GENERATION |
imageDetail |
PI_OPENAI_CODEX_COMPAT_IMAGE_DETAIL |
webRun |
PI_OPENAI_CODEX_COMPAT_WEB_RUN |
autoCompactAtPercent |
PI_OPENAI_CODEX_COMPAT_AUTO_COMPACT_AT_PERCENT |
webSearch |
PI_OPENAI_CODEX_COMPAT_WEB_SEARCH_MODE |
textVerbosity |
PI_OPENAI_CODEX_COMPAT_TEXT_VERBOSITY |
reasoningSummary |
PI_OPENAI_CODEX_COMPAT_REASONING_SUMMARY |
Environment variables have the highest precedence: defaults < global JSON < trusted-project JSON < environment. Boolean values accept true/false, 1/0, on/off, or enabled/disabled. Other settings use the values in the defaults table; PI_OPENAI_CODEX_COMPAT_AUTO_COMPACT_AT_PERCENT=off and PI_OPENAI_CODEX_COMPAT_AUTO_COMPACT_AT_PERCENT=default explicitly select Pi's default compaction lifecycle.
Environment-controlled rows are marked (env) and locked in /codex-settings. Saving the pane does not copy their effective values into JSON, so CLI overrides remain transient. Invalid environment values fail fast with the variable name and accepted values.
For example:
PI_OPENAI_CODEX_COMPAT_WEB_RUN=off \
PI_OPENAI_CODEX_COMPAT_RESPONSES_LITE=off \
PI_OPENAI_CODEX_COMPAT_SHELL_TOOL=unified_exec \
PI_OPENAI_CODEX_COMPAT_IMAGE_DETAIL=high \
PI_OPENAI_CODEX_COMPAT_AUTO_COMPACT_AT_PERCENT=90 \
pi
Command tools
On an openai-codex model, shellTool selects exactly one command surface:
unified_execactivatesexec_commandandwrite_stdinand is the default.shell_commandactivates the legacy one-shot command tool.
The extension replaces bash only when it was active before the Codex command
surface was selected. It therefore preserves sessions started with restricted
tool lists such as --no-tools. Switching away from an openai-codex model
restores the previously active bash tool. Selecting shell_command, or
switching models, also terminates persistent unified-exec sessions.
exec_command returns immediately when a command finishes within its yield
window. Otherwise it returns a numeric session ID for write_stdin.
tty: true allocates a persistent pseudoterminal (PTY), allowing
write_stdin to send characters to interactive programs; without a PTY,
stdin is closed, but "\u0003" can still interrupt the process. Empty
write_stdin calls poll without writing. Sessions are in-memory, are capped at
64 per Pi process, and are terminated on session shutdown. If an initial
exec_command call is cancelled after its process starts, the process remains
available and the cancellation result reports its session ID for later
write_stdin interaction. /ps lists live background sessions with their
session ID, operating-system process ID, command, working directory, and
PTY/pipe mode. Enter opens a live, scrollable recent-output popup;
Ctrl+X stops the selected session from either view, and Ctrl+S stops every
session. Both stop actions require confirmation.
shell_command is one-shot and has a 10-second default timeout. Nonzero exits
and timeouts are successful tool results carrying exit metadata, so the model
can inspect and react to command failure normally. Both command families use
Pi's bash-compatible shell resolution and expose the current
PI_SESSION_ID, PI_SESSION_FILE, PI_PROVIDER, PI_MODEL, and
PI_REASONING_LEVEL values to child processes. Login-shell behavior defaults
to enabled and can be disabled per call. Unified exec additionally normalizes
NO_COLOR, TERM, UTF-8 locale variables, COLORTERM, and common pager
variables to the official Codex values; it deliberately does not set
CODEX_CI.
Model-visible output has a hard cap of the last 2,000 lines or 50 KiB,
whichever limit is reached first. Unified exec defaults
max_output_tokens to 10,000 approximate tokens (roughly 40 KiB) and allows a
call to lower that budget, but never to raise the Pi cap. When output is
truncated, the complete raw output for that interaction is stored in a
temporary log file and its absolute path is included in the tool result.
Persistent PTY support uses the pinned
node-pty 1.2.0-beta.15 prebuilds on supported macOS, Linux, and Windows
architectures.
The schemas intentionally omit Codex execution environments, sandbox permissions, additional permission profiles, approval justifications, and prefix rules. These tools execute with the Pi extension process's full host permissions.
Native compaction
See the Codex compaction approach review
for a focused 0.153.4 source review, including experimental notes/history
recovery and unsummarized context resets, implementation options, and trade-offs.
That proposal does not change this package's runtime or its package-wide
compatibility baseline.
The extension handles native compaction for openai-codex. It follows the Codex v2 flow:
- Send normal Responses history followed by
{ "type": "compaction_trigger" }. - Validate the returned opaque
compactionitem. - Retain approximately 64,000 tokens of recent user messages, plus every developer and
system message outside that budget. Repeated prompt updates (for example several
/reloadruns or prompt-section changes) each remain in place as their own inline developer item; they accumulate across checkpoints and are never dropped, merged, or deduplicated, because each one is a distinct instruction the model already saw. - Persist the opaque checkpoint in the Pi session and replay it on later requests.
A compaction request declares the same tools as the branch's latest turn request to the
selected model, so both share a prompt-cache prefix. Pi's transcript lists every active
tool, but Pi leaves some out of requests, for example the tools codemode hides in only
mode, and other extensions can edit a request's tools. Each turn request therefore saves
the tools it sent in an openai-codex-compat-request-tools session entry whenever they
differ from the branch's latest saved tools for that model. Compaction declares the saved
tools, also after a resume or a branch switch. A branch without a saved entry for the
model, such as one an older version wrote, declares the transcript's current tools.
Overflow and length recovery follow Pi 0.87's context_edit semantics. Every request
reads Pi's session projection, so earlier omissions and replacements (an elided tool
output, an omitted reply) apply exactly as they do for Pi's own adapters. Before an
overflow-recovery compaction, Pi omits the latest failed or truncated attempt and its tool
results; the checkpoint request projects only those entries as they were before that
omission, so a truncated response and its tool results survive as committed progress while
every unrelated earlier edit stays in force. A failed attempt contributes only the native
output committed before the overflowing subrequest.
Ordinary responses and compaction use the same extension-managed SSE/WebSocket transport. Before the first WebSocket turn for a session and model, the provider performs a best-effort v2 generate: false prewarm of the static instruction/tool prefix, then generates the dynamic conversation input from its continuation. With responsesLite: false, supported models use the ordinary Responses envelope and receive their own ordinary-prefix prewarm.
Healthy session WebSockets remain available until the server closes them or Pi tears down the session. Retryable WebSocket failures before model-visible output receive up to five fresh-connection retries before the session switches to sticky SSE. Retryable SSE HTTP failures and dropped streams receive up to five same-request resampling attempts before model-visible output. Both transports use Codex-style exponential backoff with ±10% jitter and preserve the same prompt-cache, session, account, installation, and window identities. Server metadata is not considered model-visible output, so a routing-state-only response can still be retried safely. Transport failures after model-visible output fail closed rather than risk duplicate text or tool calls. Explicit retryable response.failed/response.incomplete protocol terminals instead return to the provider-owned sampling loop, which preserves completed output items as the next request's history.
The provider stores a native response override only when Pi's canonical assistant representation cannot round-trip the provider output exactly; normal text, reasoning, and tool responses therefore do not duplicate session data. Native overrides are associated with canonical assistants by response id and replayed only when they are present on the active Pi branch.
Transparent prewarm, requests, continuation, and transport recovery are recorded in the resulting assistant message's diagnostics array in Pi's session JSONL:
codex_transport_prewarmrecords whether static prewarm completed, established continuation state, and received turn state.codex_transport_requestrecords the selected transport, full/delta input counts and byte sizes, exact session/account/cache/turn/response/routing-state identifiers, static-prefix and request-template fingerprints, instruction/tool fingerprints, cache affinity, and reported cache read/write token usage.codex_transport_recoveryidentifies fresh-WebSocket and SSE retries, rejected or locally bypassed continuations, and WebSocket-to-SSE recovery, including the exact triggering error, attempted request modes, and whether cache and account affinity were preserved.
Diagnostics intentionally retain exact request, cache-affinity, response, account, and server routing identifiers so a local Pi session file contains enough information to trace retries and cache behavior directly. Prompt and tool contents are still represented by byte counts and SHA-256 fingerprints rather than duplicated into every diagnostic.
Any model switch is rejected while the active branch contains a native Codex checkpoint because checkpoints are model-specific. Navigate to a branch before the checkpoint or start a new session before switching. Toggling fast mode does not change the model id or invalidate the checkpoint.
Native compaction fails closed for Codex models: a failed compaction is cancelled instead of silently replacing the opaque state with a local text summary. Other providers continue to use Pi's default compaction behavior. /tree branch summarization is intentionally not intercepted.
When the active branch has no native Codex checkpoint, Pi model switching remains available. Selecting a provider other than openai-codex disables the extension-owned Codex tools, restores the Pi edit and write tools that apply_patch suppressed, restores a replaced Pi bash tool, and terminates persistent unified-exec sessions. Switching back to an openai-codex model reapplies the current session settings.
apply_patch
The package registers an apply_patch tool using the Codex patch format:
*** Begin Patch
*** Update File: src/example.ts
@@
-old value
+new value
*** End Patch
While applyPatch is enabled and an openai-codex model is selected, the extension temporarily disables Pi's active edit and write tools. Turning the setting off or selecting another provider restores only the tools that were active before apply_patch replaced them.
Supported operations:
- add files;
- update files with ordered context chunks;
- delete files;
- update and move a file in one instruction;
- move regular files or symlink entries without content changes;
- evaluate repeated and aliased paths sequentially;
- anchor updates at the end of a file.
Compatibility behavior:
*** Add Fileoverwrites an existing file, matching Codex.*** Move tooverwrites an existing destination, matching Codex.- Hunk matching retries exact text, trailing-whitespace-insensitive text, fully trimmed text, and Codex's Unicode punctuation normalization.
- Matching stops after the official Codex-compatible line matcher. There is no Tree-sitter, Markdown-table, code-fence, formatter-reflow, candidate-ranking, or output-equivalence fallback.
- Move identity and context chunks must match before the entry is moved; chunkless moves remain byte-opaque.
- The parser accepts Codex's lenient marker whitespace, blank update-context lines, and direct heredoc wrappers.
- Empty and identity updates, identical adds, absent deletes, self-moves, and same-patch fulfilled moves succeed with concise
NO CHANGEresults. Inapplicable operations areSKIPPEDonly when later operations deterministically make every effect unobservable. - Repeated identical adds use source-ordered virtual content and exact spelling, so an earlier add or move can satisfy a later add without a redundant replacement.
- State-dependent
NO CHANGEresults retain source-ordered execution checkpoints without rereading the filesystem. A checkpoint after an earlier failure isNOT RUN; empty updates, identity updates without moves, and chunkless lexical self-moves remain unconditional. - Model-facing results retain the aggregate A/M/D summary. When any instruction is not applied or an applied instruction has feedback, they list every source-ordered instruction under
Patch instruction results:asN. [STATUS] operation, without an instruction limit; ordinary all-applied results omit the ledger. - Combined text updates and moves are labeled
Update & Move; move-only operations remainMove. - Replacement feedback always identifies the verified previous and resulting entry types. Symlink feedback also uses the raw target pathname stored in the symlink.
- Tool-result history stores per-file old/new content, display diffs, move destinations, overwrite information, per-instruction filesystem effects, and deterministic final-path inspection after runtime failures.
- Opaque moves and symlink deletions use path-only history, so binary bytes and link-target bytes are not serialized as textual deletions.
- The TUI retains Codex-style changed-file summaries and uses the same conditional instruction ledger; when present,
Ctrl+Onests complete diffs beneath the instruction that produced them. - With
applyPatchDebugenabled, the tool title becomesapply_patch (debug)and a completed collapsed result shows the exact text returned to the model without an extra renderer-only heading; expanding it withCtrl+Ostill shows the normal visual summary and complete diffs. - Failed instruction feedback colocates its error, completed effects, and final path states without repeating patch text or using speculative language. Matching failures report the original context or expected-lines mismatch.
With applyPatchDiagnostics enabled, the extension prepares pre-execution
snapshots in memory for each invocation. A successful invocation discards that
prepared data and writes no diagnostic artifacts or tool-result references. A
failed invocation writes paired JSON artifacts under:
~/.pi/agent/openai-codex-compat-apply-patch-diagnostics/<session-id>/
The active Pi agent directory replaces ~/.pi/agent when configured
differently. The request artifact contains the raw patch, parsed instructions
(or parse failure), pre-execution snapshots for paths referenced by every
instruction in the failed patch, and available session, assistant, response,
turn, transport-request, and tool-call identifiers. It also records the
compatibility-package, Pi, Node.js, operating-system, and architecture versions.
Process identity, working directory, and umask are included when available.
Each file and parent-directory snapshot includes mode, size, modification time,
device, inode, link count, user ID, and group ID so hard-link, alias,
cross-filesystem, and permission failures remain traceable. Parent metadata is
collected from each referenced path through the filesystem root. Malformed
patches retain snapshots for every instruction recognized by the fallback
scanner.
Valid UTF-8 regular-file content is stored as text with its byte length and SHA-256. Binary content is not copied; its snapshot contains only the byte length and SHA-256. Symlink snapshots retain the raw target and either readable UTF-8 target content or binary target metadata. The result artifact records the failed outcome, structured tool details, and the error chain up to eight causes, including available filesystem codes, operation names, source paths, and destination paths. Those details also retain the diagnostic record ID and both artifact paths so the invocation can be located from Pi session history.
These artifacts can contain sensitive source code, ownership and filesystem
identity metadata, absolute paths, patches, and request identifiers. Capture is
disabled by default.
Directories are restricted to mode 0700, files to 0600, and records are not
automatically pruned; delete them manually when they are no longer needed. If
either artifact cannot be written after a patch failure, the diagnostic error
is reported to stderr without replacing or changing the original patch
failure.
Filesystem behavior:
- Relative paths resolve from Pi's current working directory; absolute paths and
..traversal are honored. .gitpaths are unrestricted.- Text updates follow live symlinks; adds replace live or dangling symlinks without writing through them; deletes remove only the symlink; pure moves move the source symlink; and state-changing moves create a regular file at the destination without writing updated text through a source or destination symlink.
- Relative symlink targets, including targets containing
.., resolve from the canonical directory containing the link even when the source or moved destination is reached through a symlink-parent alias. - Entry-only operations and no-op updates do not dereference cyclic or inaccessible symlink targets during mutation-queue acquisition.
- Same-filesystem pure moves use native rename topology. Cross-filesystem moves copy through a temporary entry, create or replace the destination, and then unlink the source, producing an inode independent from remaining source hard links.
- Supplied identity or context chunks validate a pure move through the text matcher without rewriting the moved entry. A blank
@@therefore requires valid UTF-8; omit chunks for arbitrary binary content. - Strict edits preserve the matched region's local CRLF or mixed line endings.
- In-place text updates use a direct path write, following source symlinks and preserving normal hard-link visibility.
- Adds and text updates verify the complete expected final byte buffer byte-for-byte before succeeding. This is whole-file equality, not a substring search, so duplicate or unrelated regions cannot satisfy the postcondition. Deletes verify absence; moves verify their source, destination type, exact destination spelling, native identity where available, known bytes, and raw symlink target.
- The extension does not add path filtering, sandboxing, or approval prompts.
- Every hunk is parsed and validated before filesystem writes begin.
- No-change checkpoints are interleaved with mutations in patch order, so later checkpoints are not reported as completed after an earlier failure.
- Mutations participate in Pi's per-file mutation queue for the complete preflight-and-execution window. Queue paths are acquired deterministically, follow operation-specific symlink semantics, and include every move source and destination. There is no additional extension-local alias queue.
Operating model: relevant filesystem state is not modified outside the queued
apply_patch execution window. This includes ancestor paths, symlink targets,
hard-link aliases, callbacks, injected filesystem hooks, separate Pi sessions,
and external processes. Preflight is authoritative under this model; the
executor does not attempt cross-process drift detection or transactional
isolation.
A low-level I/O failure can still complete part of an instruction. The failed instruction reports every confirmed effect and final path state; when a path cannot be inspected, it says that the final state was not verified.
The complete feedback and rendering contract is documented in
APPLY_PATCH_INSTRUCTION_FEEDBACK.md.
pi-apply-patch command line
The package installs a pi-apply-patch executable for use outside Pi sessions:
pi-apply-patch parse patch.txt
cat patch.txt | pi-apply-patch parse
parse runs the same parser as the tool and prints { operations, environmentId? } as JSON.
Each operation is add with content, delete, or update with chunks and an optional
moveTo. It does not read or write any other file. Exit status is 0 when parsed, 1 for an
invalid patch, and 2 for a usage or read error.
The command line does not apply patches. Applying through a standalone process would bypass Pi's
file mutation queue and the tool-result contract, so applying stays inside the apply_patch tool.
image_gen.imagegen
The package registers the dotted Pi tool name image_gen.imagegen and serializes it as a native Responses API namespace:
{
"type": "namespace",
"name": "image_gen",
"tools": [{ "type": "function", "name": "imagegen" }]
}
The tool generates new images with gpt-image-2 or edits up to five local/recent conversation images. Local edit inputs must use absolute paths and are read directly with the Pi process's filesystem permissions. PNG, JPEG, GIF, and WebP edit inputs are accepted. Generated PNGs are returned to Pi as image tool content and stored without overwriting existing files under:
~/.pi/agent/generated_images/<session-id>/<call-id>.png
The active Pi agent directory replaces ~/.pi/agent when configured differently.
Saving imageGeneration: false through /codex-settings removes the tool from
the current session.
The tool registers the server-reserved schema directly with a model-facing absolute-path annotation that names the supported image formats. OpenAI rejects additional schema keywords for image-count bounds and selector exclusivity, so the executor enforces those constraints before filesystem or network access. Local paths are lexically normalized before reading. A one-line system-prompt snippet and four high-signal guidelines cover normal model use. Local images are inspected with Pi's read tool, and generated image content is displayed and saved automatically without Codex Code Mode wrappers.
Pi also persists the returned image content in tool-result history so later image edits and provider replay remain self-contained. Generated-image turns therefore increase the session file by approximately the base64 image size in addition to the saved PNG artifact.
When the image tool result is serialized back to the model, imageDetail controls its Responses input_image.detail. The default remains auto; select high for the official Codex default. The saved-path hint intentionally differs from Codex: it always uses “the generated image” and is not removed when the UTF-8 hint exceeds 1,024 bytes.
The TUI shows a compact generation/edit summary and saved artifact path instead of the model-facing path hint. Ctrl+O reveals the full prompt and artifact metadata; terminal image display continues to use Pi's normal image support.
web.run
The package registers the dotted Pi tool name web.run and serializes it as a native Responses API web namespace. Calls are executed through codex/alpha/search. Like Codex, successful model-facing output is the unmodified plaintext output wrapped in a single input_text content item:
{
"type": "function_call_output",
"call_id": "<call-id>",
"output": [{ "type": "input_text", "text": "<alpha/search output>" }]
}
Structured results are not sent to the model by either implementation. Codex stores them in extension-backed web-search events; this package stores the equivalent opaque JSON branch-locally in Pi tool-result details. Extensions and session readers can inspect those details, while subsequent web.run calls resolve model-visible reference IDs through alpha/search rather than querying the details directly.
The collapsed TUI view provides action-specific summaries for search, image search, page navigation, in-page find, PDF screenshots, finance, weather, sports, and time. Ctrl+O expands structured source or image cards, page metadata with line/page gutters, PDF page cards, operation-specific result cards, and readable labeled fields for forward-compatible result types. Citation markers and backend separators are normalized for display, while empty or unavailable operations use compact warning states instead of appearing successful.
Standalone screenshot calls return a plaintext PDF-page reference from alpha/search, not image bytes or an image content item. The TUI therefore labels these results as reference-only, and the next model request receives the same plaintext reference rather than screenshot pixels.
The exposed command schema includes:
search_query;image_query;open;click;find;screenshot;finance;weather;sports;time;response_length.
The tool sends the current user message plus the preceding visible user/assistant turn as search context. When web.run is active, hosted web_search is omitted; turning webRun off restores the hosted tool according to webSearch.
Both namespace tools are accepted only from the fixed extension-owned allowlist. Unknown namespaced calls and flat wire calls named web.run or image_gen.imagegen fail instead of being routed ambiguously.
Development
mise trust
mise install
npm install
mise run check
npm run pack:dry
mise run check runs the linters, formatters, type checks, and the offline
test suite; mise run test runs only the offline suite. Both tasks bind
PI_PACKAGE_DIR to node_modules/@earendil-works/pi-coding-agent, so tests
that load Pi in-process read this repository's Pi 1.0.0 resources even when a
global PI_PACKAGE_DIR points at another installation. Ordinary pi launches
outside these tasks are unaffected. Offline tests also exercise
scripts/release.ts with every child process mocked: they never run Git, npm,
Mise, or provider calls.
Run the credentialed Pi/Codex integration tests separately. They load the real extension into headless Pi sessions, use the real WebSocket service, and ask the model to report all prior history markers after each text and tool continuation:
mise run test:live:codex
The task obtains the local Codex bearer token and runs the tests with
gpt-5.6-luna, gpt-6-sol, gpt-6-luna, and gpt-6.1-sol at medium reasoning
effort. It also packs the extension and loads that archive through the shipped Pi
1.0.0 CLI. Ordinary Responses and Responses Lite tests exercise tool calls,
prompt reload, native compaction, and persisted-session resume against Codex.
SDK tests verify Responses Lite
WebSocket history, prewarming, continuation, and the built-in read tool.
CLI tests also execute the built-in read tool after resume.
Terminal tests check interrupted exec_command error framing in regular and
fullscreen mode. A live terminal case blocks a real Codex command call before
execution and checks that Pi's metadata-free error retains its tool box.
Six additional CLI cases replay grammar-tool history with gpt-5.6-luna in both
response formats. They cover foreign-provider history, different-model history,
and calls previously recorded as function calls.
Test credentials stay in memory and child-process environments. Test sessions
and configuration are isolated from the user's agent directory.
The packaged-CLI test selects its archive and executable as follows:
- Without
PI_CODEX_PACKAGE_ARCHIVE, it packs the current worktree into a temporary directory. WithPI_CODEX_PACKAGE_ARCHIVE, it loads exactly that archive: the value resolves against the working directory, must be an existing regular file thattarcan list, and an empty, missing, directory, or invalid value fails the test instead of falling back to a fresh pack. PI_CODEX_CLI_PATHselects another Picli.js; the default is this repository's Pi 1.0.0 dependency. The test asserts that the selected executable reports version 1.0.0, the only Pi version the packaged CLI test is run against. The supported range is>=1.0.0 <1.1.0.- Each Pi child process receives
PI_PACKAGE_DIRbound to the selected executable's package directory, so the runtime under test reads its own metadata.
The public package entrypoint is extensions/index.ts; implementation modules remain under
extensions/openai-codex-compat/. The focused Pi AI serializer copy lives under
extensions/openai-codex-compat/vendor/pi-ai/. The custom Codex provider transport and stream
parser are focused adaptations of Pi AI's corresponding implementation. Equivalence and protocol
tests cover canonical serialization, native namespace round-trips, raw native replay, sibling Codex
JSON endpoints, SSE request behavior, WebSocket reuse, grammar tools, image results, standalone
search, and compaction continuation.
Release staging
- Run
npm run release -- X.Y.Zfrom a clean, synchronizedmain. - The command builds the exact package locally and runs live CLI tests against that archive. It also runs the existing live SDK tests against checkout source. Both suites must pass before it records the archive's SHA-256 in an SSH-signed release commit, proves a clean rebuild is reproducible, and creates a lightweight tag. Missing credentials or failing live tests stop the release.
- Inspect the result, then push atomically with
git push --atomic origin main vX.Y.Z. - A read-only GitHub Actions job validates and packs the package. After approval
in the tag-restricted
npm-publishenvironment, a separate GitHub-owned job verifies the signature and signed digest before attesting and staging that exact archive through npm trusted publishing. - A final job creates the immutable GitHub release from the verified archive,
its checksum, and the version's changelog section (
Unreleasedfor prereleases). - Approve the staged package on npmjs.com or with
npm stage approve <stage-id>.
The release command runs these steps in order:
- Refuse to start unless the branch is
main,git status --porcelainis empty,CHANGELOG.mdhas a section for the version,HEADequalsorigin/mainafter a fetch, and tagvX.Y.Zdoes not exist. Nothing has changed when one of these checks fails. - Write the version into
package.jsonandpackage-lock.jsonand stage only those two files. - Check out the staged index into a temporary directory, run
npm ci --ignore-scriptsandnpm packthere, validate the package identity, file list, and manifest, then runmise run test:live:codexin the repository withPI_CODEX_PACKAGE_ARCHIVEset to that archive. This is the exact archive release gate: the live CLI tests load that archive and nothing else. A live process that cannot start, a nonzero exit, or a failed package validation stops the release here. - Create the SSH-signed
release: vX.Y.Zcommit with the archive's SHA-256 as itsNpm-Artifact-SHA256trailer and verify the signature and trailer. - Rebuild the package from the committed tree without repeating the live tests and require the same digest.
- Create the lightweight tag and verify that it points at the release commit.
Recovering from a failed release command
Do not run any blanket git restore, git reset, git checkout --, or
git clean. Inspect first, then undo only what the failed command produced.
If the command fails during the version update, version changes can remain
in the worktree. Once the update and git add succeed, package, live-test,
and signing failures leave those changes staged. No release commit or tag
was created by that attempt. Inspect the state:
git status --short
git diff -- package.json package-lock.json
git diff --cached -- package.json package-lock.json
Undo only this attempt's version edits in the worktree and index. Preserve
concurrent changes, including edits in those same files. Do not stage whole
files containing unrelated edits. Fix the cause and confirm that main is
clean and synchronized before rerunning the release command.
If the command fails after the release commit (steps 4 to 6), a local signed
release: vX.Y.Z commit exists on main, and a local tag may exist. Do not rerun
the release command: it would refuse the unsynchronized HEAD, and a second
run on top of the existing commit would produce a duplicate release commit.
Do not push the commit or tag until signature verification, the reproducibility
check, and tag verification are complete. Inspect the commit and the digest:
git log -1 --format='%H%n%s%n%(trailers:key=Npm-Artifact-SHA256,valueonly)' HEAD
git diff origin/main..HEAD -- package.json package-lock.json
git tag --list 'vX.Y.Z'
Removing the local release commit or tag changes local refs. Confirm that
they were never pushed, record their hashes and a recovery path, and obtain
explicit approval before changing them. Never replace a published tag.
Fix the underlying cause before starting a new release from clean,
synchronized main.
Stable releases use latest; prereleases derive their npm dist-tag from the first prerelease identifier.
Acknowledgements
The remote-compaction implementation follows the current OpenAI Codex remote_compaction_v2 protocol. The apply_patch, standalone image-generation, and standalone web-search behavior is adapted from OpenAI Codex under Apache-2.0. The Codex provider transport, stream processing, and OpenAI Responses history serialization adapt selected Pi AI methods under MIT; see third-party notices.
License
MIT © 2026 Kaan Ozdokmeci. See LICENSE.