pi-openai-codex-compat 0.0.16 → 0.0.17
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.
- package/CHANGELOG.md +23 -0
- package/README.md +57 -4
- package/extensions/openai-codex-compat/command-tools.ts +2 -0
- package/extensions/openai-codex-compat/config.ts +2 -56
- package/extensions/openai-codex-compat/index.ts +9 -3
- package/extensions/openai-codex-compat/settings-pane.ts +227 -536
- package/extensions/settings-menu.ts +618 -0
- package/extensions/settings-session.ts +130 -0
- package/extensions/settings-store.ts +240 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.0.17 - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Separate menu sections with blank rows, using compact spacing on short terminals.
|
|
10
|
+
- Align setting names on the left and values in a stable column on the right.
|
|
11
|
+
Render the search placeholder in muted gray instead of ordinary input text.
|
|
12
|
+
Keep Save and Discard hints visible in narrow terminals.
|
|
13
|
+
- Align settings with the Anthropic menu: Enter changes values, Ctrl+S saves and
|
|
14
|
+
applies drafts, and Escape discards unapplied drafts without changing active tools.
|
|
15
|
+
- Save only changed overrides, preserve inheritance and external edits, and
|
|
16
|
+
report conflicting saves without overwriting them.
|
|
17
|
+
- Block command-tool switches while persistent command sessions remain instead
|
|
18
|
+
of terminating them while browsing settings.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Apply to session without changing configuration files. Reopening settings shows
|
|
23
|
+
active session values, including after reload, restart, or resume.
|
|
24
|
+
Ctrl+S can save session-only changes later.
|
|
25
|
+
- Custom percentage editing, configuration-source and model-applicability labels,
|
|
26
|
+
inherited-value resets, and consistent keyboard, focus, and mouse support.
|
|
27
|
+
|
|
5
28
|
## 0.0.16 - 2026-09-23
|
|
6
29
|
|
|
7
30
|
### Added
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ OpenAI Codex compatibility for [Pi](https://github.com/earendil-works/pi-mono),
|
|
|
14
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.
|
|
15
15
|
- **Hosted web-search fallback**: injects native `web_search` only when `web.run` is inactive, with cached, indexed, or live modes.
|
|
16
16
|
- **Native request controls**: configures Responses API text verbosity, reasoning summaries, and standard/pro reasoning mode on supported models.
|
|
17
|
-
- **
|
|
17
|
+
- **Draft settings pane**: `/codex-settings` stages 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.
|
|
18
18
|
- **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.
|
|
19
19
|
|
|
20
20
|
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.
|
|
@@ -173,7 +173,56 @@ A trusted project can override it at:
|
|
|
173
173
|
<project>/.pi/openai-codex-compat.json
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
Each session inherits the effective file-backed settings.
|
|
176
|
+
Each session inherits the effective file-backed settings. `/codex-settings`
|
|
177
|
+
uses the same interaction contract as `/anthropic-settings`:
|
|
178
|
+
|
|
179
|
+
- **Enter** changes the selected value or opens its editor. It never implicitly saves.
|
|
180
|
+
- **Space** activates a result or inserts a space when search has focus.
|
|
181
|
+
- **Tab / Shift+Tab** move between search, results, and action controls.
|
|
182
|
+
- **F1** opens scrollable details, full errors, and the exact save target.
|
|
183
|
+
- Edits remain drafts until explicitly applied or saved.
|
|
184
|
+
- **Apply to session** applies drafts without writing configuration files and stays open.
|
|
185
|
+
- **Ctrl+S** saves and applies changes without closing.
|
|
186
|
+
- **Save and close** saves and applies changes, then closes on success.
|
|
187
|
+
- **Escape** cancels a field editor or discards unapplied drafts and closes the main menu.
|
|
188
|
+
It does not undo settings already applied to the session.
|
|
189
|
+
|
|
190
|
+
Search matches labels, configuration keys, and descriptions. The percentage
|
|
191
|
+
editor supports presets and custom fractional values. **Use inherited value**
|
|
192
|
+
removes an override. This differs from **Pi default**, which explicitly disables
|
|
193
|
+
an inherited percentage trigger. Rows show configuration sources, environment
|
|
194
|
+
locks, and model applicability. Pi's remapped selection and cancel keys are respected.
|
|
195
|
+
|
|
196
|
+
The global file is the normal save target. An existing trusted project override
|
|
197
|
+
is the target for that project. The target stays fixed while the menu is open.
|
|
198
|
+
Saving changes only edited overrides, preserves unknown keys and inherited values,
|
|
199
|
+
and preserves non-conflicting external edits. Conflicting edits are rejected for review.
|
|
200
|
+
Opening or closing without changes does not create a configuration file.
|
|
201
|
+
Reopening shows active session values, not freshly read file values. `*` marks
|
|
202
|
+
an unapplied draft. `~` marks an active session override or a value that differs
|
|
203
|
+
from the saved configuration. Details shows the saved value when it differs.
|
|
204
|
+
Ctrl+S also persists changes previously applied only to the session.
|
|
205
|
+
Session settings are stored as extension state entries in the Pi session,
|
|
206
|
+
outside model context. They survive menu closure, tree navigation, extension
|
|
207
|
+
reload, process restart, and switching away and back. Resuming the same session
|
|
208
|
+
restores its settings before provider operations. A new session, fork, or clone
|
|
209
|
+
has a separate identity and starts from configuration files.
|
|
210
|
+
Pi's `--no-session` mode cannot be resumed after exit. Pi also defers creating a
|
|
211
|
+
new session file until its first assistant message.
|
|
212
|
+
Environment-controlled fields remain locked and take precedence over restored
|
|
213
|
+
session preferences. Their values are not copied into session settings.
|
|
214
|
+
|
|
215
|
+
Applying and saving wait for Pi to become idle. Collect pending output with `write_stdin` or
|
|
216
|
+
stop running command sessions through `/ps` before applying or saving a command-tool switch.
|
|
217
|
+
Browsing settings and discarding drafts
|
|
218
|
+
never terminate those sessions. Escape cancels a pending operation without discarding
|
|
219
|
+
the draft. Failures leave the menu open. If a save reaches disk but cannot
|
|
220
|
+
be applied to the session, the menu reports that distinction and Ctrl+S retries
|
|
221
|
+
application. Cooperating writers use a `.settings-lock` file. An interrupted
|
|
222
|
+
writer can leave a lock that requires review; locks are not automatically
|
|
223
|
+
deleted. External editors do not participate in that lock.
|
|
224
|
+
Session overrides retain their original conflict checks across menu reopenings.
|
|
225
|
+
Conflicting external edits remain protected after reload or restart.
|
|
177
226
|
|
|
178
227
|
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` and `pro` only when enabled, and shows text verbosity or reasoning summary only when they differ from their defaults.
|
|
179
228
|
|
|
@@ -225,7 +274,9 @@ Responses Lite supports exactly `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`,
|
|
|
225
274
|
standard reasoning mode even when `pro` is configured. Both controls remain
|
|
226
275
|
opt-in. GPT-6 Sol and Luna require Pi 0.87.1's model catalog.
|
|
227
276
|
|
|
228
|
-
Invalid JSON setting values are ignored and invalid JSON does not prevent Pi from
|
|
277
|
+
Invalid JSON setting values are ignored and invalid JSON does not prevent Pi from
|
|
278
|
+
starting. The settings pane reports malformed JSON instead of overwriting it.
|
|
279
|
+
Project configuration is read only when the project is trusted.
|
|
229
280
|
|
|
230
281
|
Every setting can also be overridden for one Pi process with an environment variable:
|
|
231
282
|
|
|
@@ -533,7 +584,9 @@ The tool generates new images with `gpt-image-2` or edits up to five local/recen
|
|
|
533
584
|
~/.pi/agent/generated_images/<session-id>/<call-id>.png
|
|
534
585
|
```
|
|
535
586
|
|
|
536
|
-
The active Pi agent directory replaces `~/.pi/agent` when configured differently.
|
|
587
|
+
The active Pi agent directory replaces `~/.pi/agent` when configured differently.
|
|
588
|
+
Saving `imageGeneration: false` through `/codex-settings` removes the tool from
|
|
589
|
+
the current session.
|
|
537
590
|
|
|
538
591
|
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.
|
|
539
592
|
|
|
@@ -59,6 +59,7 @@ export { formatBackgroundProcesses } from "./background-process-browser.ts";
|
|
|
59
59
|
export type CommandToolsApi = Pick<ExtensionAPI, "on" | "registerCommand" | "registerTool">;
|
|
60
60
|
|
|
61
61
|
export type CommandToolsController = {
|
|
62
|
+
hasPersistentSessions: () => boolean;
|
|
62
63
|
terminateUnifiedExecSessions: () => void;
|
|
63
64
|
};
|
|
64
65
|
|
|
@@ -352,6 +353,7 @@ export default function registerCommandTools(
|
|
|
352
353
|
pi.on("session_shutdown", () => manager.terminateAll());
|
|
353
354
|
|
|
354
355
|
return {
|
|
356
|
+
hasPersistentSessions: () => manager.activeSessionCount() > 0,
|
|
355
357
|
terminateUnifiedExecSessions() {
|
|
356
358
|
manager.terminateAll().catch((error: unknown) => {
|
|
357
359
|
console.error("Could not terminate unified exec sessions.", error);
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { isBoolean, isNumber, nodeErrorCode } from "./value-contracts.ts";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import {
|
|
5
|
-
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { join } from "node:path";
|
|
6
4
|
import { CONFIG_DIR_NAME, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
5
|
import type { Static } from "typebox";
|
|
8
6
|
import { Value } from "typebox/value";
|
|
9
|
-
import { isObject
|
|
7
|
+
import { isObject } from "./codex-protocol.ts";
|
|
10
8
|
|
|
11
9
|
export const WEB_SEARCH_MODE_SCHEMA = {
|
|
12
10
|
enum: ["disabled", "cached", "indexed", "live"],
|
|
@@ -465,55 +463,3 @@ export function withoutEnvironmentOverrides(
|
|
|
465
463
|
}
|
|
466
464
|
return result;
|
|
467
465
|
}
|
|
468
|
-
|
|
469
|
-
async function readWritableConfig(filePath: string): Promise<JsonRecord> {
|
|
470
|
-
try {
|
|
471
|
-
const value: unknown = JSON.parse(await readFile(filePath, "utf8"));
|
|
472
|
-
if (!isObject(value)) throw new Error("the root value must be a JSON object");
|
|
473
|
-
return value;
|
|
474
|
-
} catch (error) {
|
|
475
|
-
if (nodeErrorCode(error) === "ENOENT") return {};
|
|
476
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
477
|
-
throw new Error(`Cannot update ${filePath}: ${detail}`, { cause: error });
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Merge known setting changes into the dedicated extension file. Unknown keys
|
|
483
|
-
* are retained, and an invalid existing file is never overwritten.
|
|
484
|
-
*/
|
|
485
|
-
export async function saveConfig(
|
|
486
|
-
cwd: string,
|
|
487
|
-
projectTrusted: boolean,
|
|
488
|
-
patch: ConfigLayer,
|
|
489
|
-
): Promise<string> {
|
|
490
|
-
const filePath = writableConfigPath(cwd, projectTrusted);
|
|
491
|
-
const current = await readWritableConfig(filePath);
|
|
492
|
-
const next = { ...current, ...patch };
|
|
493
|
-
const directory = dirname(filePath);
|
|
494
|
-
const temporaryPath = join(
|
|
495
|
-
directory,
|
|
496
|
-
`.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
await mkdir(directory, { recursive: true });
|
|
500
|
-
const existingMode = await stat(filePath)
|
|
501
|
-
.then((metadata) => metadata.mode & 0o777)
|
|
502
|
-
.catch((error: unknown) => {
|
|
503
|
-
if (nodeErrorCode(error) === "ENOENT") return 0o600;
|
|
504
|
-
throw error;
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
try {
|
|
508
|
-
await writeFile(temporaryPath, `${JSON.stringify(next, null, 2)}\n`, {
|
|
509
|
-
encoding: "utf8",
|
|
510
|
-
flag: "wx",
|
|
511
|
-
mode: existingMode,
|
|
512
|
-
});
|
|
513
|
-
await rename(temporaryPath, filePath);
|
|
514
|
-
} finally {
|
|
515
|
-
await rm(temporaryPath, { force: true });
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return filePath;
|
|
519
|
-
}
|
|
@@ -12,7 +12,7 @@ import registerOutputLimitContinuation, {
|
|
|
12
12
|
} from "./output-limit-continuation.ts";
|
|
13
13
|
import registerRemoteCompaction, { remoteCompactionApi } from "./remote-compaction.ts";
|
|
14
14
|
import registerCodexRequestOptions from "./request-options.ts";
|
|
15
|
-
import registerCodexSettings from "./settings-pane.ts";
|
|
15
|
+
import registerCodexSettings, { loadSessionConfig } from "./settings-pane.ts";
|
|
16
16
|
import registerCodexThreadLineage, { codexThreadLineageApi } from "./codex-thread-lineage.ts";
|
|
17
17
|
import registerCodexTools, { syncCodexTools } from "./tools.ts";
|
|
18
18
|
|
|
@@ -26,8 +26,11 @@ export {
|
|
|
26
26
|
export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
27
27
|
requirePiTranscriptRuntime(piAi, piCodingAgent);
|
|
28
28
|
let activeConfig: CodexCompatConfig | undefined;
|
|
29
|
+
let sessionContext: Parameters<typeof loadSessionConfig>[0] | undefined;
|
|
29
30
|
const resolveConfig = (ctx: ConfigContext): CodexCompatConfig => {
|
|
30
|
-
activeConfig ??=
|
|
31
|
+
activeConfig ??= sessionContext
|
|
32
|
+
? loadSessionConfig(sessionContext)
|
|
33
|
+
: loadConfig(ctx.cwd, ctx.isProjectTrusted());
|
|
31
34
|
return activeConfig;
|
|
32
35
|
};
|
|
33
36
|
const resolveToolBackground = () => activeConfig?.toolBackground ?? DEFAULT_CONFIG.toolBackground;
|
|
@@ -37,7 +40,9 @@ export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
|
37
40
|
activeConfig?.applyPatchDiagnostics ?? DEFAULT_CONFIG.applyPatchDiagnostics;
|
|
38
41
|
|
|
39
42
|
pi.on("session_start", (event, ctx) => {
|
|
40
|
-
|
|
43
|
+
sessionContext = ctx;
|
|
44
|
+
activeConfig = undefined;
|
|
45
|
+
activeConfig = resolveConfig(ctx);
|
|
41
46
|
installCodexFooter(ctx, resolveConfig);
|
|
42
47
|
if (event.reason !== "reload" && ctx.mode === "tui") {
|
|
43
48
|
ctx.ui.notify("pi-openai-codex-compat loaded · /codex-settings", "info");
|
|
@@ -60,6 +65,7 @@ export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
|
60
65
|
registerCodexModelPolicy(modelPolicy, resolveConfig, commandTools);
|
|
61
66
|
registerCodexSettings(pi, {
|
|
62
67
|
getConfig: resolveConfig,
|
|
68
|
+
hasPersistentSessions: commandTools.hasPersistentSessions,
|
|
63
69
|
onChange(config, ctx) {
|
|
64
70
|
activeConfig = config;
|
|
65
71
|
codexProvider.updateSessionConfig(ctx.sessionId, config);
|