pi-openai-codex-compat 0.0.10-alpha.2 → 0.0.10-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/LICENSES/node-pty-MIT.txt +69 -0
  3. package/LICENSES/tree-sitter-MIT.txt +50 -0
  4. package/README.md +84 -24
  5. package/THIRD_PARTY_NOTICES.md +30 -0
  6. package/extensions/openai-codex-compat/apply-patch-diff-render.ts +10 -1
  7. package/extensions/openai-codex-compat/apply-patch-render.ts +36 -8
  8. package/extensions/openai-codex-compat/background-process-browser.ts +254 -0
  9. package/extensions/openai-codex-compat/codex-tool-surface.ts +36 -8
  10. package/extensions/openai-codex-compat/command-apply-patch.ts +276 -0
  11. package/extensions/openai-codex-compat/command-output.ts +434 -0
  12. package/extensions/openai-codex-compat/command-process.ts +418 -0
  13. package/extensions/openai-codex-compat/command-render.ts +191 -0
  14. package/extensions/openai-codex-compat/command-runtime.ts +838 -0
  15. package/extensions/openai-codex-compat/command-shell.ts +267 -0
  16. package/extensions/openai-codex-compat/command-tool-contract.ts +163 -0
  17. package/extensions/openai-codex-compat/command-tools.ts +288 -0
  18. package/extensions/openai-codex-compat/config.ts +25 -0
  19. package/extensions/openai-codex-compat/footer.ts +206 -28
  20. package/extensions/openai-codex-compat/image-generation-render.ts +17 -2
  21. package/extensions/openai-codex-compat/index.ts +7 -35
  22. package/extensions/openai-codex-compat/model-policy.ts +15 -4
  23. package/extensions/openai-codex-compat/settings-pane.ts +142 -42
  24. package/extensions/openai-codex-compat/tools.ts +57 -3
  25. package/extensions/openai-codex-compat/web-run-render.ts +17 -2
  26. package/package.json +7 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - Add configurable Codex `exec_command` + `write_stdin` and `shell_command`
8
+ command surfaces, defaulting to persistent unified exec and replacing only
9
+ an active Pi `bash` tool. Command output follows Pi's 2,000-line/50-KiB tail
10
+ limit with complete truncated output in temporary files, while PTY sessions
11
+ use pinned `node-pty` prebuilds.
7
12
  - Show the current Pi session ID on the compatibility footer's first line.
8
13
  - Add default-off, failure-only `apply_patch` diagnostics capture with
9
14
  pre-execution text snapshots and binary metadata for every instruction in a
@@ -13,6 +18,33 @@
13
18
 
14
19
  ### Fixed
15
20
 
21
+ - Show effective unified-exec yield durations beside accent-colored command
22
+ tool titles, include explicit working directories, emphasize commands on
23
+ their own separated rows, separate `apply_patch` results, and mute command
24
+ output and protocol metadata.
25
+ - Stream live `exec_command`, `write_stdin`, and `shell_command` output in the
26
+ TUI while each tool call is running instead of hiding updates until it exits
27
+ or yields.
28
+ - Remove trailing line endings from debug `apply_patch` result previews so they
29
+ do not add an empty output row before the intended bottom padding.
30
+ - Remove trailing command-output line endings from TUI previews so the intended
31
+ bottom padding matches the top padding instead of rendering an extra blank
32
+ output row.
33
+ - Cache footer session names, context and cumulative usage, and rendered lines by
34
+ session and display state, and cache immutable web, image, and debug-aware
35
+ `apply_patch` child renderers so animation-only frames avoid repeated history
36
+ and formatting work.
37
+ - Omit Code Mode-only command output schemas from normal Responses tool
38
+ declarations, matching official direct function calling and preventing plain
39
+ text command results from being rejected as non-JSON.
40
+ - Cache completed command previews, `apply_patch` diffs, and extension-owned
41
+ Codex tool surfaces, deriving collapsed command tails without splitting full
42
+ output on every TUI frame so long tool-heavy sessions remain responsive.
43
+ - Give the `/codex-settings` search field explicit focus that follows typed
44
+ input into search and Up/Down back to the result list, allowing Space to
45
+ change a filtered setting and applying accent color to the result cursor and
46
+ selected item only while that list has focus, with concise focus and action
47
+ labels.
16
48
  - Preserve regular-file permission modes across sequential `apply_patch`
17
49
  replacements and state-changing moves, including files created by earlier
18
50
  instructions in the same patch.
@@ -41,6 +73,31 @@
41
73
 
42
74
  ### Changed
43
75
 
76
+ - Describe `shell_command`, `exec_command`, and `write_stdin` in Pi's tool list,
77
+ guidelines, and detailed tool contracts using the actual resolved default
78
+ shell. Document command working-directory and `PI_*` context behavior,
79
+ explain unified process output and session-ID transitions, advertise only
80
+ available platform-appropriate shell override values, and bind execution to
81
+ the same advertised default shell for the extension lifecycle.
82
+ - Align all command tools' retained input-field descriptions, successful
83
+ nonzero/timeout semantics, and unified child environment (except `CODEX_CI`),
84
+ while preserving Pi's complete-output truncation files and retaining
85
+ initially cancelled processes with a model-visible session ID. Add a `/ps`
86
+ background-terminal browser
87
+ with live scrollable recent output, confirmed `Ctrl+X` termination for one
88
+ session, and confirmed `Ctrl+S` termination for all sessions. Match Codex's
89
+ zero-value timing inputs, hidden `shell_command.timeout` alias, PowerShell
90
+ arguments, post-PTY-write reaction delay, and graceful one-shot
91
+ cancellation. Intercept top-level `apply_patch` shell heredocs before process
92
+ creation, match Codex's protected and interaction-safe process pruning,
93
+ count invalid UTF-8 output tokens from raw bytes, and align command argument,
94
+ session, stdin, and spawn-error prefixes. Resolve default and model-requested
95
+ shells with Codex's type-first discovery and fallback policy, recognize
96
+ shell-text `apply_patch` calls with Codex's exact Tree-sitter query and argv
97
+ routing, and aggregate classic `shell_command` output as stdout followed by
98
+ stderr while retaining Pi's complete-output files.
99
+ - Replace the multi-line startup configuration dump with a concise package and
100
+ settings-command notice, and simplify the fallback settings-save error.
44
101
  - Align deferred tool loading with Pi 0.84.3, preferring message-anchored
45
102
  additional tools on capable Codex models while retaining tool-search and
46
103
  top-level fallbacks.
@@ -0,0 +1,69 @@
1
+ Copyright (c) 2012-2015, Christopher Jeffrey (https://github.com/chjj/)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
20
+
21
+
22
+
23
+ The MIT License (MIT)
24
+
25
+ Copyright (c) 2016, Daniel Imms (http://www.growingwiththeweb.com)
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining a copy
28
+ of this software and associated documentation files (the "Software"), to deal
29
+ in the Software without restriction, including without limitation the rights
30
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
+ copies of the Software, and to permit persons to whom the Software is
32
+ furnished to do so, subject to the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be included in
35
+ all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
+ SOFTWARE.
44
+
45
+
46
+
47
+ MIT License
48
+
49
+ Copyright (c) 2018 - present Microsoft Corporation
50
+
51
+ All rights reserved.
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining a copy
54
+ of this software and associated documentation files (the "Software"), to deal
55
+ in the Software without restriction, including without limitation the rights
56
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
57
+ copies of the Software, and to permit persons to whom the Software is
58
+ furnished to do so, subject to the following conditions:
59
+
60
+ The above copyright notice and this permission notice shall be included in
61
+ all copies or substantial portions of the Software.
62
+
63
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
66
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
67
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
68
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
69
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ tree-sitter
2
+ ===========
3
+
4
+ The MIT License (MIT)
5
+
6
+ Copyright (c) 2014 maxbrunsfeld
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+
26
+
27
+ tree-sitter-bash
28
+ ================
29
+
30
+ The MIT License (MIT)
31
+
32
+ Copyright (c) 2017 Max Brunsfeld
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining a copy
35
+ of this software and associated documentation files (the "Software"), to deal
36
+ in the Software without restriction, including without limitation the rights
37
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
+ copies of the Software, and to permit persons to whom the Software is
39
+ furnished to do so, subject to the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be included in all
42
+ copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
+ SOFTWARE.
package/README.md CHANGED
@@ -7,10 +7,11 @@ OpenAI Codex compatibility for [Pi](https://github.com/earendil-works/pi-mono),
7
7
  - **Request-level fast mode**: keeps the canonical `openai-codex` provider id and models selected while adding `service_tier: "priority"` at the request boundary.
8
8
  - **Native compaction**: uses Codex `remote_compaction_v2` for `/compact`, Pi threshold compaction, context-overflow recovery, and an optional percentage threshold.
9
9
  - **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.
10
+ - **Codex command tools**: replaces an active Pi `bash` tool with either the persistent `exec_command` + `write_stdin` pair or the one-shot `shell_command` tool. Unified exec is the default and supports optional PTY sessions through `node-pty`.
10
11
  - **Opt-in patch diagnostics**: records failed `apply_patch` requests with pre-execution text snapshots or binary metadata for every instruction, outcomes, and Pi/Codex identifiers.
11
12
  - **Standalone image generation**: exposes Pi's dotted `image_gen.imagegen` tool as a native Responses namespace and executes generation or edits through the Codex Images endpoints.
12
13
  - **Standalone web search**: exposes Pi's dotted `web.run` tool as a native Responses namespace and executes search and browsing through Codex `alpha/search`.
13
- - **Dedicated Codex tool UI**: renders `apply_patch`, `image_gen.imagegen`, and `web.run` on a shared configurable surface with compact summaries and `Ctrl+O` expansion.
14
+ - **Dedicated Codex tool UI**: renders command tools, `apply_patch`, `image_gen.imagegen`, and `web.run` on a shared configurable surface with compact summaries and `Ctrl+O` expansion.
14
15
  - **Hosted web-search fallback**: injects native `web_search` only when `web.run` is inactive, with cached, indexed, or live modes.
15
16
  - **Native request controls**: configures Responses API text verbosity, reasoning summaries, and GPT-5.6 standard/pro reasoning mode.
16
17
  - **Session-local settings pane**: `/codex-settings` changes every compatibility setting for the current session; `Enter` persists and closes, `Escape` discards unsaved changes and closes, and `Ctrl+S` persists without closing.
@@ -32,7 +33,7 @@ Authenticate through Pi if needed:
32
33
 
33
34
  ## Compatibility baseline and differences
34
35
 
35
- The compatibility baseline is official Codex CLI `0.149.0`, released August 20, 2026, at commit `758ef40f50c1a458425c7cfbf1eb12cbc07af0b0`. 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 [official release compatibility log](OFFICIAL_CODEX_CLI_RELEASES.md) 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](RESPONSES_LITE_COMPATIBILITY.md) and [Codex caching and transport comparison](CODEX_CACHE_RESEARCH.md) for detailed request-path findings and live cache trajectories.
36
+ 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](OFFICIAL_CODEX_CLI_RELEASES.md) 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](RESPONSES_LITE_COMPATIBILITY.md) and [Codex caching and transport comparison](CODEX_CACHE_RESEARCH.md) for detailed request-path findings and live cache trajectories.
36
37
 
37
38
  ### Configurable defaults that differ from Codex
38
39
 
@@ -43,6 +44,7 @@ The compatibility baseline is official Codex CLI `0.149.0`, released August 20,
43
44
  | 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. |
44
45
  | 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`. |
45
46
  | 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. |
47
+ | 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`. |
46
48
  | `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. |
47
49
  | `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. |
48
50
  | 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`. |
@@ -65,13 +67,13 @@ The compatibility baseline is official Codex CLI `0.149.0`, released August 20,
65
67
  | 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. |
66
68
  | 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. |
67
69
  | 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. |
68
- | 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.0` 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. |
70
+ | 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. |
69
71
  | 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. |
70
72
  | Header hooks | An internal percentage-compaction request reuses the already transformed provider headers. It cannot independently rerun Pi's `before_provider_headers` hook. |
71
- | Native retained context | Deliberately differs from current Codex. The package retains recent user/developer/system messages under the 64k budget before the opaque compaction item. 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. Pi keeps its existing checkpoint shape by design. |
73
+ | Native retained context | Deliberately differs from current Codex. The package retains recent user/developer/system messages under a text-only 64k budget before the opaque compaction item. 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. |
72
74
  | 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. |
73
75
  | 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. |
74
- | Sandbox and approvals | Pi extensions run with full process permissions. `apply_patch`, local image reads, generated-image writes, and sibling Codex endpoints do not use Codex's sandbox, permission-profile, or approval lifecycle. |
76
+ | 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. |
75
77
  | 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. |
76
78
  | 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. |
77
79
  | 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. |
@@ -97,6 +99,7 @@ The package implements the Codex-specific pieces that fit a provider compatibili
97
99
 
98
100
  - native Responses transport and history;
99
101
  - remote compaction v2;
102
+ - `exec_command`, `write_stdin`, and `shell_command`;
100
103
  - `apply_patch`;
101
104
  - hosted `web_search`;
102
105
  - `web.run`;
@@ -108,10 +111,9 @@ The following official Codex facilities are not exact equivalents in this packag
108
111
 
109
112
  | Official Codex facility | Pi/package behavior |
110
113
  | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
111
- | `exec_command` and `write_stdin` persistent PTY sessions | Pi `bash` is a one-shot command tool; no persistent PTY/session protocol is implemented. |
112
- | Legacy `shell_command` | Pi uses `bash`; the Codex schema and execution/approval envelope are not reproduced. |
113
114
  | `view_image` | Pi `read` already accepts images; no canonical `view_image` alias is registered. |
114
115
  | `update_plan`, `request_user_input`, permissions, and environment tools | Not implemented by this package. |
116
+ | `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. |
115
117
  | Context-window and clock tools | Not implemented. Compaction remains host/provider managed rather than model managed. |
116
118
  | MCP resources and dynamic MCP tools | Pi does not provide this package with Codex's MCP runtime. |
117
119
  | Plugin/connector installation | Not implemented; package installation remains an explicit Pi/user operation. |
@@ -176,6 +178,7 @@ Example:
176
178
  "fastMode": true,
177
179
  "responsesLite": true,
178
180
  "toolBackground": "subtle",
181
+ "shellTool": "unified_exec",
179
182
  "applyPatch": true,
180
183
  "applyPatchDebug": false,
181
184
  "applyPatchDiagnostics": false,
@@ -192,22 +195,23 @@ Example:
192
195
 
193
196
  Defaults:
194
197
 
195
- | Setting | Values | Default | Behavior |
196
- | ----------------------- | ---------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
197
- | `fastMode` | boolean | `false` | Adds `service_tier: "priority"` to requests while retaining the current `openai-codex` provider and model. |
198
- | `responsesLite` | boolean | `false` | Uses Codex's Responses Lite input envelope on supported GPT-5.6 models when enabled. By default, those models use ordinary Responses instructions and tools. |
199
- | `toolBackground` | `subtle`, `status`, `none` | `subtle` | Controls the shared self-rendered background for `apply_patch`, `image_gen.imagegen`, and `web.run`. `status` uses Pi's pending/success/error backgrounds; `none` keeps the custom layout transparent. |
200
- | `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. |
201
- | `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. |
202
- | `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`](#apply_patch) for storage and sensitivity details. |
203
- | `imageGeneration` | boolean | `true` | Enables the extension-owned `image_gen.imagegen` tool on selected `openai-codex` models. |
204
- | `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. |
205
- | `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. |
206
- | `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. |
207
- | `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. |
208
- | `textVerbosity` | `low`, `medium`, `high` | `low` | Sets Responses API `text.verbosity`. |
209
- | `reasoningSummary` | `auto`, `concise`, `detailed`, `off` | `auto` | Sets `reasoning.summary` when reasoning is enabled; `off` omits the summary parameter. |
210
- | `reasoningMode` | `standard`, `pro` | `standard` | Controls GPT-5.6 execution mode independently of Pi's reasoning-effort control. The default omits `reasoning.mode`; `pro` sends `reasoning.mode: "pro"`. |
198
+ | Setting | Values | Default | Behavior |
199
+ | ----------------------- | ---------------------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
200
+ | `fastMode` | boolean | `false` | Adds `service_tier: "priority"` to requests while retaining the current `openai-codex` provider and model. |
201
+ | `responsesLite` | boolean | `false` | Uses Codex's Responses Lite input envelope on supported GPT-5.6 models when enabled. By default, those models use ordinary Responses instructions and tools. |
202
+ | `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. |
203
+ | `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. |
204
+ | `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. |
205
+ | `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. |
206
+ | `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`](#apply_patch) for storage and sensitivity details. |
207
+ | `imageGeneration` | boolean | `true` | Enables the extension-owned `image_gen.imagegen` tool on selected `openai-codex` models. |
208
+ | `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. |
209
+ | `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. |
210
+ | `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. |
211
+ | `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. |
212
+ | `textVerbosity` | `low`, `medium`, `high` | `low` | Sets Responses API `text.verbosity`. |
213
+ | `reasoningSummary` | `auto`, `concise`, `detailed`, `off` | `auto` | Sets `reasoning.summary` when reasoning is enabled; `off` omits the summary parameter. |
214
+ | `reasoningMode` | `standard`, `pro` | `standard` | Controls GPT-5.6 execution mode independently of Pi's reasoning-effort control. The default omits `reasoning.mode`; `pro` sends `reasoning.mode: "pro"`. |
211
215
 
212
216
  Invalid JSON setting values are ignored and invalid JSON does not prevent Pi from starting. The settings pane never writes on ordinary changes, refuses to overwrite invalid JSON when `Enter` or `Ctrl+S` attempts to save, and retains unknown keys when saving. Project configuration is read only when the project is trusted.
213
217
 
@@ -218,6 +222,7 @@ Every setting can also be overridden for one Pi process with an environment vari
218
222
  | `fastMode` | `PI_OPENAI_CODEX_COMPAT_FAST_MODE` |
219
223
  | `responsesLite` | `PI_OPENAI_CODEX_COMPAT_RESPONSES_LITE` |
220
224
  | `toolBackground` | `PI_OPENAI_CODEX_COMPAT_TOOL_BACKGROUND` |
225
+ | `shellTool` | `PI_OPENAI_CODEX_COMPAT_SHELL_TOOL` |
221
226
  | `applyPatch` | `PI_OPENAI_CODEX_COMPAT_APPLY_PATCH` |
222
227
  | `applyPatchDebug` | `PI_OPENAI_CODEX_COMPAT_APPLY_PATCH_DEBUG` |
223
228
  | `applyPatchDiagnostics` | `PI_OPENAI_CODEX_COMPAT_APPLY_PATCH_DIAGNOSTICS` |
@@ -239,11 +244,66 @@ For example:
239
244
  ```bash
240
245
  PI_OPENAI_CODEX_COMPAT_WEB_RUN=off \
241
246
  PI_OPENAI_CODEX_COMPAT_RESPONSES_LITE=off \
247
+ PI_OPENAI_CODEX_COMPAT_SHELL_TOOL=unified_exec \
242
248
  PI_OPENAI_CODEX_COMPAT_IMAGE_DETAIL=high \
243
249
  PI_OPENAI_CODEX_COMPAT_AUTO_COMPACT_AT_PERCENT=90 \
244
250
  pi
245
251
  ```
246
252
 
253
+ ## Command tools
254
+
255
+ On an `openai-codex` model, `shellTool` selects exactly one command surface:
256
+
257
+ - `unified_exec` activates `exec_command` and `write_stdin` and is the default.
258
+ - `shell_command` activates the legacy one-shot command tool.
259
+
260
+ The extension replaces `bash` only when it was active before the Codex command
261
+ surface was selected. It therefore preserves sessions started with restricted
262
+ tool lists such as `--no-tools`. Switching away from an `openai-codex` model
263
+ restores the previously active `bash` tool. Selecting `shell_command`, or
264
+ switching models, also terminates persistent unified-exec sessions.
265
+
266
+ `exec_command` returns immediately when a command finishes within its yield
267
+ window. Otherwise it returns a numeric session ID for `write_stdin`.
268
+ `tty: true` allocates a persistent pseudoterminal (PTY), allowing
269
+ `write_stdin` to send characters to interactive programs; without a PTY,
270
+ stdin is closed, but `"\u0003"` can still interrupt the process. Empty
271
+ `write_stdin` calls poll without writing. Sessions are in-memory, are capped at
272
+ 64 per Pi process, and are terminated on session shutdown. If an initial
273
+ `exec_command` call is cancelled after its process starts, the process remains
274
+ available and the cancellation result reports its session ID for later
275
+ `write_stdin` interaction. `/ps` lists live background sessions with their
276
+ session ID, operating-system process ID, command, working directory, and
277
+ PTY/pipe mode. `Enter` opens a live, scrollable recent-output popup;
278
+ `Ctrl+X` stops the selected session from either view, and `Ctrl+S` stops every
279
+ session. Both stop actions require confirmation.
280
+
281
+ `shell_command` is one-shot and has a 10-second default timeout. Nonzero exits
282
+ and timeouts are successful tool results carrying exit metadata, so the model
283
+ can inspect and react to command failure normally. Both command families use
284
+ Pi's bash-compatible shell resolution and expose the current
285
+ `PI_SESSION_ID`, `PI_SESSION_FILE`, `PI_PROVIDER`, `PI_MODEL`, and
286
+ `PI_REASONING_LEVEL` values to child processes. Login-shell behavior defaults
287
+ to enabled and can be disabled per call. Unified exec additionally normalizes
288
+ `NO_COLOR`, `TERM`, UTF-8 locale variables, `COLORTERM`, and common pager
289
+ variables to the official Codex values; it deliberately does not set
290
+ `CODEX_CI`.
291
+
292
+ Model-visible output has a hard cap of the last 2,000 lines or 50 KiB,
293
+ whichever limit is reached first. Unified exec defaults
294
+ `max_output_tokens` to 10,000 approximate tokens (roughly 40 KiB) and allows a
295
+ call to lower that budget, but never to raise the Pi cap. When output is
296
+ truncated, the complete raw output for that interaction is stored in a
297
+ temporary log file and its absolute path is included in the tool result.
298
+ Persistent PTY support uses the pinned
299
+ `node-pty` `1.2.0-beta.15` prebuilds on supported macOS, Linux, and Windows
300
+ architectures.
301
+
302
+ The schemas intentionally omit Codex execution environments, sandbox
303
+ permissions, additional permission profiles, approval justifications, and
304
+ prefix rules. These tools execute with the Pi extension process's full host
305
+ permissions.
306
+
247
307
  ## Native compaction
248
308
 
249
309
  The extension handles native compaction for `openai-codex`. It follows the Codex v2 flow:
@@ -271,7 +331,7 @@ Any model switch is rejected while the active branch contains a native Codex che
271
331
 
272
332
  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.
273
333
 
274
- When the active branch has no native Codex checkpoint, Pi model switching remains available. Selecting a provider other than `openai-codex` disables `apply_patch`, `image_gen.imagegen`, and `web.run`, and restores the Pi `edit` and `write` tools that `apply_patch` suppressed. Switching back to an `openai-codex` model reapplies the current session settings.
334
+ 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.
275
335
 
276
336
  ## `apply_patch`
277
337
 
@@ -19,3 +19,33 @@ Pi AI, Copyright (c) 2025 Mario Zechner
19
19
  Pi AI is licensed under the MIT License. A copy is included at [`LICENSES/pi-ai-MIT.txt`](LICENSES/pi-ai-MIT.txt).
20
20
 
21
21
  Source: <https://github.com/earendil-works/pi/tree/main/packages/ai>
22
+
23
+ ## node-pty
24
+
25
+ Persistent `exec_command` sessions use `node-pty`.
26
+
27
+ node-pty contributors, Copyright (c) 2012-2015 Christopher Jeffrey,
28
+ Copyright (c) 2016 Daniel Imms, and Copyright (c) 2018-present Microsoft
29
+ Corporation
30
+
31
+ node-pty is licensed under the MIT License. A copy is included at
32
+ [`LICENSES/node-pty-MIT.txt`](LICENSES/node-pty-MIT.txt).
33
+
34
+ Source: <https://github.com/microsoft/node-pty>
35
+
36
+ ## tree-sitter and tree-sitter-bash
37
+
38
+ Shell-text `apply_patch` detection uses the Tree-sitter runtime and Bash
39
+ grammar.
40
+
41
+ tree-sitter, Copyright (c) 2014 maxbrunsfeld
42
+
43
+ tree-sitter-bash, Copyright (c) 2017 Max Brunsfeld
44
+
45
+ Both packages are licensed under the MIT License. Copies are included at
46
+ [`LICENSES/tree-sitter-MIT.txt`](LICENSES/tree-sitter-MIT.txt).
47
+
48
+ Sources:
49
+
50
+ - <https://github.com/tree-sitter/node-tree-sitter>
51
+ - <https://github.com/tree-sitter/tree-sitter-bash>
@@ -511,6 +511,8 @@ export class ApplyPatchDiffComponent implements Component {
511
511
  private readonly theme: RenderTheme;
512
512
  private readonly cwd: string;
513
513
  private readonly expanded: boolean;
514
+ private cachedWidth: number | undefined;
515
+ private cachedLines: string[] | undefined;
514
516
 
515
517
  constructor(details: ApplyPatchDetails, theme: RenderTheme, cwd: string, expanded: boolean) {
516
518
  this.details = details;
@@ -521,6 +523,8 @@ export class ApplyPatchDiffComponent implements Component {
521
523
 
522
524
  render(width: number): string[] {
523
525
  const effectiveWidth = Math.max(1, width);
526
+ if (this.cachedLines && this.cachedWidth === effectiveWidth) return this.cachedLines;
527
+
524
528
  const changes = sortedChanges(this.details, this.cwd);
525
529
  const showInstructionResults = applyPatchNeedsInstructionResults(this.details, this.cwd);
526
530
  const lines: string[] = [];
@@ -588,8 +592,13 @@ export class ApplyPatchDiffComponent implements Component {
588
592
  }
589
593
  }
590
594
 
595
+ this.cachedWidth = effectiveWidth;
596
+ this.cachedLines = lines;
591
597
  return lines;
592
598
  }
593
599
 
594
- invalidate(): void {}
600
+ invalidate(): void {
601
+ this.cachedWidth = undefined;
602
+ this.cachedLines = undefined;
603
+ }
595
604
  }
@@ -34,6 +34,7 @@ class ApplyPatchTitleComponent implements Component {
34
34
  private readonly text = new Text("", 0, 0);
35
35
  private readonly theme: RenderTheme;
36
36
  private readonly resolveDebug: ApplyPatchDebugResolver;
37
+ private renderedDebug: boolean | undefined;
37
38
 
38
39
  constructor(theme: RenderTheme, resolveDebug: ApplyPatchDebugResolver) {
39
40
  this.theme = theme;
@@ -41,12 +42,17 @@ class ApplyPatchTitleComponent implements Component {
41
42
  }
42
43
 
43
44
  render(width: number): string[] {
44
- const title = this.resolveDebug() ? "apply_patch (debug)" : "apply_patch";
45
- this.text.setText(this.theme.fg("toolTitle", this.theme.bold(title)));
45
+ const debug = this.resolveDebug();
46
+ if (this.renderedDebug !== debug) {
47
+ const title = debug ? "apply_patch (debug)" : "apply_patch";
48
+ this.text.setText(this.theme.fg("accent", this.theme.bold(title)));
49
+ this.renderedDebug = debug;
50
+ }
46
51
  return this.text.render(width);
47
52
  }
48
53
 
49
54
  invalidate(): void {
55
+ this.renderedDebug = undefined;
50
56
  this.text.invalidate();
51
57
  }
52
58
  }
@@ -55,7 +61,8 @@ function modelFeedback(result: ApplyPatchResult): string | undefined {
55
61
  const text = result.content.flatMap((item) =>
56
62
  item.type === "text" && isString(item.text) ? [item.text] : [],
57
63
  );
58
- return text.length > 0 ? text.join("\n") : undefined;
64
+ const feedback = text.join("\n").replace(/(?:\r?\n)+$/u, "");
65
+ return feedback || undefined;
59
66
  }
60
67
 
61
68
  class ApplyPatchResultComponent implements Component {
@@ -63,6 +70,9 @@ class ApplyPatchResultComponent implements Component {
63
70
  private readonly feedback: Container | undefined;
64
71
  private readonly expanded: boolean;
65
72
  private readonly resolveDebug: ApplyPatchDebugResolver;
73
+ private cachedWidth: number | undefined;
74
+ private cachedFeedback: boolean | undefined;
75
+ private cachedLines: string[] | undefined;
66
76
 
67
77
  constructor(
68
78
  ordinary: Component,
@@ -81,12 +91,30 @@ class ApplyPatchResultComponent implements Component {
81
91
  }
82
92
 
83
93
  render(width: number): string[] {
84
- return !this.expanded && this.resolveDebug() && this.feedback
85
- ? this.feedback.render(width)
86
- : this.ordinary.render(width);
94
+ const effectiveWidth = Math.max(1, width);
95
+ const showFeedback = Boolean(!this.expanded && this.resolveDebug() && this.feedback);
96
+ if (
97
+ this.cachedLines &&
98
+ this.cachedWidth === effectiveWidth &&
99
+ this.cachedFeedback === showFeedback
100
+ ) {
101
+ return this.cachedLines;
102
+ }
103
+
104
+ const lines =
105
+ showFeedback && this.feedback
106
+ ? this.feedback.render(effectiveWidth)
107
+ : this.ordinary.render(effectiveWidth);
108
+ this.cachedWidth = effectiveWidth;
109
+ this.cachedFeedback = showFeedback;
110
+ this.cachedLines = lines;
111
+ return lines;
87
112
  }
88
113
 
89
114
  invalidate(): void {
115
+ this.cachedWidth = undefined;
116
+ this.cachedFeedback = undefined;
117
+ this.cachedLines = undefined;
90
118
  this.ordinary.invalidate();
91
119
  this.feedback?.invalidate();
92
120
  }
@@ -136,7 +164,7 @@ export function renderApplyPatchResult(
136
164
  {
137
165
  background: resolveBackground,
138
166
  status: context.isError ? "error" : "success",
139
- top: false,
167
+ top: true,
140
168
  bottom: true,
141
169
  },
142
170
  );
@@ -149,7 +177,7 @@ export function renderApplyPatchResult(
149
177
  {
150
178
  background: resolveBackground,
151
179
  status: "error",
152
- top: false,
180
+ top: true,
153
181
  bottom: true,
154
182
  },
155
183
  );