pi-openai-codex-compat 0.0.1-alpha.0
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 +64 -0
- package/LICENSE +20 -0
- package/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/pi-ai-MIT.txt +21 -0
- package/README.md +331 -0
- package/THIRD_PARTY_NOTICES.md +21 -0
- package/extensions/openai-codex-compat/apply-patch-diff-render.ts +436 -0
- package/extensions/openai-codex-compat/apply-patch-engine.ts +1004 -0
- package/extensions/openai-codex-compat/apply-patch-render.ts +133 -0
- package/extensions/openai-codex-compat/apply-patch.ts +142 -0
- package/extensions/openai-codex-compat/codex-protocol.ts +598 -0
- package/extensions/openai-codex-compat/codex-provider.ts +740 -0
- package/extensions/openai-codex-compat/codex-stream.ts +444 -0
- package/extensions/openai-codex-compat/codex-tool-surface.ts +186 -0
- package/extensions/openai-codex-compat/codex-transport.ts +855 -0
- package/extensions/openai-codex-compat/compaction-checkpoint.ts +304 -0
- package/extensions/openai-codex-compat/config.ts +268 -0
- package/extensions/openai-codex-compat/footer.ts +99 -0
- package/extensions/openai-codex-compat/image-generation-render.ts +166 -0
- package/extensions/openai-codex-compat/image-generation.ts +355 -0
- package/extensions/openai-codex-compat/index.ts +65 -0
- package/extensions/openai-codex-compat/model-policy.ts +67 -0
- package/extensions/openai-codex-compat/namespaced-tools.ts +43 -0
- package/extensions/openai-codex-compat/native-history.ts +78 -0
- package/extensions/openai-codex-compat/remote-compaction.ts +198 -0
- package/extensions/openai-codex-compat/request-options.ts +121 -0
- package/extensions/openai-codex-compat/responses-replay.ts +33 -0
- package/extensions/openai-codex-compat/settings-pane.ts +298 -0
- package/extensions/openai-codex-compat/tool-runtime.ts +32 -0
- package/extensions/openai-codex-compat/tools.ts +70 -0
- package/extensions/openai-codex-compat/vendor/pi-ai/README.md +15 -0
- package/extensions/openai-codex-compat/vendor/pi-ai/openai-responses-serialization.ts +660 -0
- package/extensions/openai-codex-compat/web-run-description.txt +105 -0
- package/extensions/openai-codex-compat/web-run-output.ts +172 -0
- package/extensions/openai-codex-compat/web-run-render.ts +681 -0
- package/extensions/openai-codex-compat/web-run-schema.ts +301 -0
- package/extensions/openai-codex-compat/web-run.ts +164 -0
- package/package.json +63 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSettingsListTheme,
|
|
3
|
+
type ExtensionAPI,
|
|
4
|
+
type ExtensionCommandContext,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import {
|
|
7
|
+
Container,
|
|
8
|
+
Key,
|
|
9
|
+
matchesKey,
|
|
10
|
+
type SettingItem,
|
|
11
|
+
SettingsList,
|
|
12
|
+
Text,
|
|
13
|
+
} from "@earendil-works/pi-tui";
|
|
14
|
+
import {
|
|
15
|
+
configLayer,
|
|
16
|
+
loadConfig,
|
|
17
|
+
saveConfig,
|
|
18
|
+
writableConfigPath,
|
|
19
|
+
type CodexCompatConfig,
|
|
20
|
+
type ConfigLayer,
|
|
21
|
+
} from "./config.ts";
|
|
22
|
+
|
|
23
|
+
const COMMAND_NAME = "codex-settings";
|
|
24
|
+
|
|
25
|
+
type SettingId =
|
|
26
|
+
| "fastMode"
|
|
27
|
+
| "textVerbosity"
|
|
28
|
+
| "reasoningSummary"
|
|
29
|
+
| "reasoningMode"
|
|
30
|
+
| "toolBackground"
|
|
31
|
+
| "applyPatch"
|
|
32
|
+
| "imageGeneration"
|
|
33
|
+
| "imageDetail"
|
|
34
|
+
| "webRun"
|
|
35
|
+
| "webSearch"
|
|
36
|
+
| "autoCompactAtPercent";
|
|
37
|
+
|
|
38
|
+
export type SettingsCallbacks = {
|
|
39
|
+
getConfig?: (ctx: ExtensionCommandContext) => CodexCompatConfig;
|
|
40
|
+
onChange?: (config: CodexCompatConfig, ctx: ExtensionCommandContext) => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function toggleValue(value: boolean): string {
|
|
44
|
+
return value ? "on" : "off";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function thresholdValue(value: number | undefined): string {
|
|
48
|
+
return value === undefined ? "Pi default" : `${value}%`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function settingItems(config: CodexCompatConfig): SettingItem[] {
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
id: "fastMode",
|
|
55
|
+
label: "Fast mode",
|
|
56
|
+
description: "Send requests through OpenAI's priority service tier.",
|
|
57
|
+
currentValue: toggleValue(config.fastMode),
|
|
58
|
+
values: ["off", "on"],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "textVerbosity",
|
|
62
|
+
label: "Text verbosity",
|
|
63
|
+
description: "Set Responses API text.verbosity.",
|
|
64
|
+
currentValue: config.textVerbosity,
|
|
65
|
+
values: ["low", "medium", "high"],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "reasoningSummary",
|
|
69
|
+
label: "Reasoning summary",
|
|
70
|
+
description: "Choose the reasoning summary detail, or omit summaries.",
|
|
71
|
+
currentValue: config.reasoningSummary,
|
|
72
|
+
values: ["auto", "concise", "detailed", "off"],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "reasoningMode",
|
|
76
|
+
label: "Reasoning mode",
|
|
77
|
+
description:
|
|
78
|
+
"Choose standard or pro execution independently of reasoning effort. Applied only to GPT-5.6 models.",
|
|
79
|
+
currentValue: config.reasoningMode,
|
|
80
|
+
values: ["standard", "pro"],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "toolBackground",
|
|
84
|
+
label: "Codex tool background",
|
|
85
|
+
description: "Choose a distinct subtle surface, Pi's normal status colors, or no background.",
|
|
86
|
+
currentValue: config.toolBackground,
|
|
87
|
+
values: ["subtle", "status", "none"],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "applyPatch",
|
|
91
|
+
label: "apply_patch tool",
|
|
92
|
+
description: "Use Codex apply_patch instead of Pi's edit and write tools.",
|
|
93
|
+
currentValue: toggleValue(config.applyPatch),
|
|
94
|
+
values: ["off", "on"],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "imageGeneration",
|
|
98
|
+
label: "image_gen.imagegen tool",
|
|
99
|
+
description: "Generate or edit images through the standalone Codex image endpoint.",
|
|
100
|
+
currentValue: toggleValue(config.imageGeneration),
|
|
101
|
+
values: ["off", "on"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "imageDetail",
|
|
105
|
+
label: "Image result detail",
|
|
106
|
+
description: "Set input_image.detail when image tool results are returned to the model.",
|
|
107
|
+
currentValue: config.imageDetail,
|
|
108
|
+
values: ["auto", "low", "high", "original"],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "webRun",
|
|
112
|
+
label: "web.run tool",
|
|
113
|
+
description: "Search and browse through the standalone Codex search endpoint.",
|
|
114
|
+
currentValue: toggleValue(config.webRun),
|
|
115
|
+
values: ["off", "on"],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "webSearch",
|
|
119
|
+
label: "Web search mode",
|
|
120
|
+
description:
|
|
121
|
+
"Control hosted search and web.run access: disabled removes hosted search but keeps web.run cached-only.",
|
|
122
|
+
currentValue: config.webSearch,
|
|
123
|
+
values: ["disabled", "cached", "indexed", "live"],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "autoCompactAtPercent",
|
|
127
|
+
label: "Auto-compact threshold",
|
|
128
|
+
description: "Add provider-boundary compaction or rely on Pi's normal compaction threshold.",
|
|
129
|
+
currentValue: thresholdValue(config.autoCompactAtPercent),
|
|
130
|
+
values: ["Pi default", "75%", "80%", "85%", "90%", "95%"],
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function settingPatch(id: string, value: string): ConfigLayer | undefined {
|
|
136
|
+
switch (id as SettingId) {
|
|
137
|
+
case "fastMode":
|
|
138
|
+
return value === "on" || value === "off" ? { fastMode: value === "on" } : undefined;
|
|
139
|
+
case "textVerbosity":
|
|
140
|
+
if (value === "low" || value === "medium" || value === "high") {
|
|
141
|
+
return { textVerbosity: value };
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
case "reasoningSummary":
|
|
145
|
+
if (value === "auto" || value === "concise" || value === "detailed" || value === "off") {
|
|
146
|
+
return { reasoningSummary: value };
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
case "reasoningMode":
|
|
150
|
+
if (value === "standard" || value === "pro") return { reasoningMode: value };
|
|
151
|
+
return undefined;
|
|
152
|
+
case "toolBackground":
|
|
153
|
+
if (value === "subtle" || value === "status" || value === "none") {
|
|
154
|
+
return { toolBackground: value };
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
case "applyPatch":
|
|
158
|
+
return value === "on" || value === "off" ? { applyPatch: value === "on" } : undefined;
|
|
159
|
+
case "imageGeneration":
|
|
160
|
+
return value === "on" || value === "off" ? { imageGeneration: value === "on" } : undefined;
|
|
161
|
+
case "imageDetail":
|
|
162
|
+
if (value === "auto" || value === "low" || value === "high" || value === "original") {
|
|
163
|
+
return { imageDetail: value };
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
case "webRun":
|
|
167
|
+
return value === "on" || value === "off" ? { webRun: value === "on" } : undefined;
|
|
168
|
+
case "webSearch":
|
|
169
|
+
if (value === "disabled" || value === "cached" || value === "indexed" || value === "live") {
|
|
170
|
+
return { webSearch: value };
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
case "autoCompactAtPercent": {
|
|
174
|
+
if (value === "Pi default") return { autoCompactAtPercent: null };
|
|
175
|
+
const percent = Number(value.replace(/%$/, ""));
|
|
176
|
+
return Number.isFinite(percent) && percent > 0 && percent <= 100
|
|
177
|
+
? { autoCompactAtPercent: percent }
|
|
178
|
+
: undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function applySettingPatch(config: CodexCompatConfig, patch: ConfigLayer): CodexCompatConfig {
|
|
184
|
+
const next: CodexCompatConfig = { ...config };
|
|
185
|
+
if (typeof patch.fastMode === "boolean") next.fastMode = patch.fastMode;
|
|
186
|
+
if (typeof patch.applyPatch === "boolean") next.applyPatch = patch.applyPatch;
|
|
187
|
+
if (patch.toolBackground) next.toolBackground = patch.toolBackground;
|
|
188
|
+
if (typeof patch.imageGeneration === "boolean") {
|
|
189
|
+
next.imageGeneration = patch.imageGeneration;
|
|
190
|
+
}
|
|
191
|
+
if (patch.imageDetail) next.imageDetail = patch.imageDetail;
|
|
192
|
+
if (typeof patch.webRun === "boolean") next.webRun = patch.webRun;
|
|
193
|
+
if (patch.webSearch) next.webSearch = patch.webSearch;
|
|
194
|
+
if (patch.textVerbosity) next.textVerbosity = patch.textVerbosity;
|
|
195
|
+
if (patch.reasoningSummary) next.reasoningSummary = patch.reasoningSummary;
|
|
196
|
+
if (patch.reasoningMode) next.reasoningMode = patch.reasoningMode;
|
|
197
|
+
if (typeof patch.autoCompactAtPercent === "number") {
|
|
198
|
+
next.autoCompactAtPercent = patch.autoCompactAtPercent;
|
|
199
|
+
} else if (patch.autoCompactAtPercent === null) {
|
|
200
|
+
delete next.autoCompactAtPercent;
|
|
201
|
+
}
|
|
202
|
+
return next;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function showSettings(
|
|
206
|
+
ctx: ExtensionCommandContext,
|
|
207
|
+
callbacks: SettingsCallbacks,
|
|
208
|
+
): Promise<void> {
|
|
209
|
+
if (ctx.mode !== "tui") {
|
|
210
|
+
ctx.ui.notify(`/${COMMAND_NAME} requires TUI mode`, "error");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let config = callbacks.getConfig?.(ctx) ?? loadConfig(ctx.cwd, ctx.isProjectTrusted());
|
|
215
|
+
let revision = 0;
|
|
216
|
+
let savedRevision = 0;
|
|
217
|
+
let saveQueue = Promise.resolve();
|
|
218
|
+
const filePath = writableConfigPath(ctx.cwd, ctx.isProjectTrusted());
|
|
219
|
+
|
|
220
|
+
await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
221
|
+
const container = new Container();
|
|
222
|
+
container.addChild(
|
|
223
|
+
new Text(theme.fg("accent", theme.bold("OpenAI Codex Compatibility Settings")), 1, 1),
|
|
224
|
+
);
|
|
225
|
+
container.addChild(
|
|
226
|
+
new Text(theme.fg("dim", `Session-only. Ctrl+S saves to ${filePath}`), 1, 0),
|
|
227
|
+
);
|
|
228
|
+
const saveStatus = new Text(
|
|
229
|
+
theme.fg("dim", "Changes apply to this session immediately."),
|
|
230
|
+
1,
|
|
231
|
+
0,
|
|
232
|
+
);
|
|
233
|
+
container.addChild(saveStatus);
|
|
234
|
+
|
|
235
|
+
const list = new SettingsList(
|
|
236
|
+
settingItems(config),
|
|
237
|
+
12,
|
|
238
|
+
getSettingsListTheme(),
|
|
239
|
+
(id, value) => {
|
|
240
|
+
const patch = settingPatch(id, value);
|
|
241
|
+
if (!patch) return;
|
|
242
|
+
|
|
243
|
+
config = applySettingPatch(config, patch);
|
|
244
|
+
revision++;
|
|
245
|
+
callbacks.onChange?.(config, ctx);
|
|
246
|
+
saveStatus.setText(theme.fg("warning", "Unsaved session changes."));
|
|
247
|
+
tui.requestRender();
|
|
248
|
+
},
|
|
249
|
+
() => done(undefined),
|
|
250
|
+
{ enableSearch: true },
|
|
251
|
+
);
|
|
252
|
+
container.addChild(list);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
render: (width: number) => container.render(width),
|
|
256
|
+
invalidate: () => container.invalidate(),
|
|
257
|
+
handleInput: (data: string) => {
|
|
258
|
+
if (matchesKey(data, Key.ctrl("s"))) {
|
|
259
|
+
const snapshot = configLayer(config);
|
|
260
|
+
const snapshotRevision = revision;
|
|
261
|
+
saveStatus.setText(theme.fg("dim", "Saving…"));
|
|
262
|
+
saveQueue = saveQueue.then(async () => {
|
|
263
|
+
try {
|
|
264
|
+
const savedPath = await saveConfig(ctx.cwd, ctx.isProjectTrusted(), snapshot);
|
|
265
|
+
savedRevision = Math.max(savedRevision, snapshotRevision);
|
|
266
|
+
saveStatus.setText(
|
|
267
|
+
savedRevision === revision
|
|
268
|
+
? theme.fg("success", `Saved to ${savedPath}`)
|
|
269
|
+
: theme.fg("warning", "Unsaved session changes."),
|
|
270
|
+
);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
273
|
+
saveStatus.setText(theme.fg("error", message));
|
|
274
|
+
ctx.ui.notify(message, "error");
|
|
275
|
+
}
|
|
276
|
+
tui.requestRender();
|
|
277
|
+
});
|
|
278
|
+
tui.requestRender();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
list.handleInput(data);
|
|
282
|
+
tui.requestRender();
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await saveQueue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export default function registerCodexSettings(
|
|
291
|
+
pi: ExtensionAPI,
|
|
292
|
+
callbacks: SettingsCallbacks = {},
|
|
293
|
+
): void {
|
|
294
|
+
pi.registerCommand(COMMAND_NAME, {
|
|
295
|
+
description: "Configure OpenAI Codex compatibility",
|
|
296
|
+
handler: async (_args, ctx) => showSettings(ctx, callbacks),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
3
|
+
import { providerHistory } from "./compaction-checkpoint.ts";
|
|
4
|
+
import type { ImageDetail } from "./config.ts";
|
|
5
|
+
import type { ResponsesItem } from "./codex-protocol.ts";
|
|
6
|
+
|
|
7
|
+
export async function codexToolAuthentication(
|
|
8
|
+
ctx: ExtensionContext,
|
|
9
|
+
model: Model<any>,
|
|
10
|
+
): Promise<{ apiKey: string; headers?: Record<string, string> }> {
|
|
11
|
+
const authentication = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
12
|
+
if (!authentication.ok) throw new Error(authentication.error);
|
|
13
|
+
if (!authentication.apiKey) throw new Error("OpenAI Codex authentication is unavailable.");
|
|
14
|
+
return {
|
|
15
|
+
apiKey: authentication.apiKey,
|
|
16
|
+
...(authentication.headers ? { headers: authentication.headers } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function codexToolHistory(
|
|
21
|
+
pi: ExtensionAPI,
|
|
22
|
+
ctx: ExtensionContext,
|
|
23
|
+
model: Model<any>,
|
|
24
|
+
imageDetail: ImageDetail = "auto",
|
|
25
|
+
): ResponsesItem[] {
|
|
26
|
+
return providerHistory({
|
|
27
|
+
branch: ctx.sessionManager.getBranch() as SessionEntry[],
|
|
28
|
+
wireModel: model,
|
|
29
|
+
allTools: pi.getAllTools(),
|
|
30
|
+
imageDetail,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
3
|
+
import registerApplyPatch, { APPLY_PATCH_TOOL_NAME } from "./apply-patch.ts";
|
|
4
|
+
import type { CodexToolBackgroundResolver } from "./codex-tool-surface.ts";
|
|
5
|
+
import type { CodexCompatConfig } from "./config.ts";
|
|
6
|
+
import { DEFAULT_CONFIG } from "./config.ts";
|
|
7
|
+
import registerImageGeneration from "./image-generation.ts";
|
|
8
|
+
import { IMAGE_GENERATION_TOOL_NAME, WEB_RUN_TOOL_NAME } from "./namespaced-tools.ts";
|
|
9
|
+
import { isCodexModel } from "./request-options.ts";
|
|
10
|
+
import registerWebRun from "./web-run.ts";
|
|
11
|
+
|
|
12
|
+
const PI_EDIT_TOOLS = ["edit", "write"] as const;
|
|
13
|
+
const CODEX_EXTENSION_TOOLS = [
|
|
14
|
+
APPLY_PATCH_TOOL_NAME,
|
|
15
|
+
IMAGE_GENERATION_TOOL_NAME,
|
|
16
|
+
WEB_RUN_TOOL_NAME,
|
|
17
|
+
] as const;
|
|
18
|
+
const suppressedEditTools = new WeakMap<ExtensionAPI, Set<string>>();
|
|
19
|
+
type ConfigResolver = (ctx: ExtensionContext) => CodexCompatConfig;
|
|
20
|
+
|
|
21
|
+
export function setApplyPatchEnabled(pi: ExtensionAPI, enabled: boolean): void {
|
|
22
|
+
const active = new Set(pi.getActiveTools());
|
|
23
|
+
active.delete(APPLY_PATCH_TOOL_NAME);
|
|
24
|
+
if (enabled) {
|
|
25
|
+
let suppressed = suppressedEditTools.get(pi);
|
|
26
|
+
if (!suppressed) {
|
|
27
|
+
suppressed = new Set(PI_EDIT_TOOLS.filter((tool) => active.has(tool)));
|
|
28
|
+
suppressedEditTools.set(pi, suppressed);
|
|
29
|
+
}
|
|
30
|
+
for (const tool of PI_EDIT_TOOLS) active.delete(tool);
|
|
31
|
+
active.add(APPLY_PATCH_TOOL_NAME);
|
|
32
|
+
} else {
|
|
33
|
+
active.delete(APPLY_PATCH_TOOL_NAME);
|
|
34
|
+
const suppressed = suppressedEditTools.get(pi);
|
|
35
|
+
if (suppressed) {
|
|
36
|
+
for (const tool of suppressed) active.add(tool);
|
|
37
|
+
suppressedEditTools.delete(pi);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
pi.setActiveTools([...active]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function syncCodexTools(
|
|
44
|
+
pi: ExtensionAPI,
|
|
45
|
+
model: Model<any> | undefined,
|
|
46
|
+
config: CodexCompatConfig,
|
|
47
|
+
): void {
|
|
48
|
+
const codexSelected = isCodexModel(model);
|
|
49
|
+
setApplyPatchEnabled(pi, codexSelected && config.applyPatch);
|
|
50
|
+
|
|
51
|
+
const active = new Set(pi.getActiveTools());
|
|
52
|
+
for (const tool of CODEX_EXTENSION_TOOLS) {
|
|
53
|
+
if (tool !== APPLY_PATCH_TOOL_NAME) active.delete(tool);
|
|
54
|
+
}
|
|
55
|
+
if (codexSelected) {
|
|
56
|
+
if (config.imageGeneration) active.add(IMAGE_GENERATION_TOOL_NAME);
|
|
57
|
+
if (config.webRun) active.add(WEB_RUN_TOOL_NAME);
|
|
58
|
+
}
|
|
59
|
+
pi.setActiveTools([...active]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default function registerCodexTools(
|
|
63
|
+
pi: ExtensionAPI,
|
|
64
|
+
resolveConfig: ConfigResolver,
|
|
65
|
+
resolveToolBackground: CodexToolBackgroundResolver = () => DEFAULT_CONFIG.toolBackground,
|
|
66
|
+
): void {
|
|
67
|
+
registerApplyPatch(pi, resolveToolBackground);
|
|
68
|
+
registerImageGeneration(pi, resolveConfig, resolveToolBackground);
|
|
69
|
+
registerWebRun(pi, resolveConfig, resolveToolBackground);
|
|
70
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Focused Pi AI code copies
|
|
2
|
+
|
|
3
|
+
This directory intentionally contains only the Pi AI methods needed to serialize checkpoint and native replay history for OpenAI's Responses API. It does not contain Pi AI's complete source dependency graph.
|
|
4
|
+
|
|
5
|
+
[`openai-responses-serialization.ts`](openai-responses-serialization.ts) adapts the relevant methods from `@earendil-works/pi-ai@0.83.0`. Its header lists the upstream source files. Keep the behavioral equivalence test in [`test/pi-ai-serialization.test.ts`](../../../../test/pi-ai-serialization.test.ts) passing when updating the Pi dependencies.
|
|
6
|
+
|
|
7
|
+
The optional `namespacedToolNames`, `textContentItemToolResultNames`, and `toolResultImageDetail` paths are extension-owned additions. Without those options, serialization must continue to match Pi AI. `namespacedToolNames` groups only the fixed Codex allowlist into native Responses namespaces and replays namespace/member call identities. `textContentItemToolResultNames` preserves Codex tools such as `web.run` whose successful text output is transported as an `input_text` content-item array instead of Pi's usual plain string. `toolResultImageDetail` overrides the otherwise canonical `auto` detail used for image tool-result content.
|
|
8
|
+
|
|
9
|
+
The local copy is necessary because Pi's extension loader does not expose `@earendil-works/pi-ai/api/openai-responses-shared` to extensions.
|
|
10
|
+
|
|
11
|
+
The focused provider transport and stream-processing adaptations live in [`codex-transport.ts`](../../codex-transport.ts) and [`codex-stream.ts`](../../codex-stream.ts).
|
|
12
|
+
|
|
13
|
+
Upstream: <https://github.com/earendil-works/pi/tree/main/packages/ai>
|
|
14
|
+
|
|
15
|
+
License: MIT; see [`LICENSES/pi-ai-MIT.txt`](../../../../LICENSES/pi-ai-MIT.txt).
|