pi-openai-codex-compat 0.0.17 → 0.0.19
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 +51 -0
- package/README.md +62 -38
- package/extensions/openai-codex-compat/apply-patch.ts +7 -0
- package/extensions/openai-codex-compat/codex-provider/codex-provider-contracts.ts +0 -2
- package/extensions/openai-codex-compat/codex-provider/codex-provider-runtime.ts +58 -23
- package/extensions/openai-codex-compat/codex-transport/codex-transport-contracts.ts +6 -0
- package/extensions/openai-codex-compat/codex-transport/codex-transport-errors.ts +13 -1
- package/extensions/openai-codex-compat/codex-transport/codex-transport-sse-stream.ts +1 -0
- package/extensions/openai-codex-compat/codex-transport/codex-transport-websocket-stream.ts +1 -0
- package/extensions/openai-codex-compat/codex-transport.ts +1 -0
- package/extensions/openai-codex-compat/command-tool-contract.ts +9 -0
- package/extensions/openai-codex-compat/command-tools.ts +4 -0
- package/extensions/openai-codex-compat/compaction-checkpoint.ts +35 -145
- package/extensions/openai-codex-compat/config.ts +0 -24
- package/extensions/openai-codex-compat/footer.ts +23 -3
- package/extensions/openai-codex-compat/image-generation.ts +8 -0
- package/extensions/openai-codex-compat/index.ts +2 -2
- package/extensions/openai-codex-compat/pi-runtime.ts +62 -9
- package/extensions/openai-codex-compat/remote-compaction.ts +46 -71
- package/extensions/openai-codex-compat/request-options.ts +2 -9
- package/extensions/openai-codex-compat/request-tools-entry.ts +113 -0
- package/extensions/openai-codex-compat/responses-lite.ts +1 -0
- package/extensions/openai-codex-compat/settings-pane.ts +2 -11
- package/extensions/openai-codex-compat/web-run.ts +2 -0
- package/extensions/settings-session.ts +11 -7
- package/package.json +7 -7
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isString } from "./value-contracts.ts";
|
|
2
1
|
import type {
|
|
3
2
|
BeforeProviderHeadersEvent,
|
|
4
3
|
CompactionResult,
|
|
@@ -6,31 +5,34 @@ import type {
|
|
|
6
5
|
ExtensionAPI,
|
|
7
6
|
ExtensionContext,
|
|
8
7
|
SessionBeforeCompactEvent,
|
|
9
|
-
ToolInfo,
|
|
10
8
|
} from "@earendil-works/pi-coding-agent";
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
import {
|
|
10
|
+
getCurrentTools,
|
|
11
|
+
type Api,
|
|
12
|
+
type Message,
|
|
13
|
+
type Model,
|
|
14
|
+
type OpenAICodexResponsesOptions,
|
|
15
|
+
type ProviderHeaders,
|
|
16
16
|
} from "@earendil-works/pi-ai";
|
|
17
|
-
import { addRemoteCompactionFeature,
|
|
17
|
+
import { addRemoteCompactionFeature, type JsonRecord } from "./codex-protocol.ts";
|
|
18
18
|
import { CODEX_API, CODEX_PROVIDER } from "./codex-identifiers.ts";
|
|
19
19
|
import {
|
|
20
|
-
activeResponsesTools,
|
|
21
20
|
branchInstructions,
|
|
22
|
-
|
|
21
|
+
branchTranscript,
|
|
23
22
|
providerHistory,
|
|
24
23
|
remoteCompactionMarkerSummary,
|
|
24
|
+
requestGrammarToolInputProperties,
|
|
25
|
+
requestTools,
|
|
25
26
|
responsesCompatibility,
|
|
26
27
|
searchCheckpoint,
|
|
27
28
|
type GrammarToolInputProperties,
|
|
28
29
|
} from "./compaction-checkpoint.ts";
|
|
30
|
+
import { APPLY_PATCH_INPUT_PROPERTY, APPLY_PATCH_TOOL_NAME } from "./apply-patch.ts";
|
|
29
31
|
import { resolveFileConfig, type ConfigResolver } from "./config-context.ts";
|
|
30
32
|
import type { CodexProviderRuntime } from "./codex-provider.ts";
|
|
31
33
|
import { responsesCompactionV2Metadata, type CodexCompactionMetadata } from "./codex-metadata.ts";
|
|
32
|
-
import { APPLY_PATCH_INPUT_PROPERTY, APPLY_PATCH_TOOL_NAME } from "./apply-patch.ts";
|
|
33
34
|
import { errorFromThrown } from "./error-from-thrown.ts";
|
|
35
|
+
import { latestRequestTools } from "./request-tools-entry.ts";
|
|
34
36
|
import { selectedRegistryModel } from "./model-context.ts";
|
|
35
37
|
|
|
36
38
|
export type RemoteCompactionContext = Parameters<ConfigResolver>[0] & {
|
|
@@ -88,7 +90,6 @@ export type RemoteCompactionHookHandler = (
|
|
|
88
90
|
|
|
89
91
|
export type RemoteCompactionApi = {
|
|
90
92
|
getActiveTools: () => string[];
|
|
91
|
-
getAllTools: () => ToolInfo[];
|
|
92
93
|
onBeforeProviderHeaders: (handler: RemoteCompactionHeadersHandler) => void;
|
|
93
94
|
onContext: (handler: RemoteCompactionContextHandler) => void;
|
|
94
95
|
onSessionBeforeCompact: (handler: RemoteCompactionHookHandler) => void;
|
|
@@ -113,7 +114,6 @@ export function remoteCompactionApi(pi: ExtensionAPI): RemoteCompactionApi {
|
|
|
113
114
|
|
|
114
115
|
return {
|
|
115
116
|
getActiveTools: () => pi.getActiveTools(),
|
|
116
|
-
getAllTools: () => pi.getAllTools(),
|
|
117
117
|
onBeforeProviderHeaders: (handler) =>
|
|
118
118
|
pi.on("before_provider_headers", (event, ctx) => handler(event, context(ctx))),
|
|
119
119
|
// The full-transcript event returns messages verbatim. A changed `context`
|
|
@@ -156,45 +156,28 @@ function instructionsForCompaction(systemPrompt: string, customInstructions?: st
|
|
|
156
156
|
: systemPrompt;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (requiredProperty === undefined) return undefined;
|
|
168
|
-
const property = tool.parameters["properties"][requiredProperty];
|
|
169
|
-
return isObject(property) && property.type === "string" ? requiredProperty : undefined;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function requestGrammarToolInputProperties(
|
|
173
|
-
payload: JsonRecord,
|
|
174
|
-
tools: readonly ToolInfo[],
|
|
175
|
-
): GrammarToolInputProperties {
|
|
176
|
-
const byName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
177
|
-
const properties = new Map<string, string>();
|
|
178
|
-
if (!Array.isArray(payload.tools)) return properties;
|
|
179
|
-
|
|
180
|
-
for (const declaration of payload.tools) {
|
|
181
|
-
if (!isObject(declaration)) continue;
|
|
182
|
-
const tool = declaration;
|
|
183
|
-
if (tool.type !== "custom" || !isString(tool.name)) continue;
|
|
184
|
-
const property = toolInputProperty(byName.get(tool.name));
|
|
185
|
-
if (property) properties.set(tool.name, property);
|
|
186
|
-
}
|
|
187
|
-
return properties;
|
|
188
|
-
}
|
|
189
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Grammar input properties for a branch without saved request tools. The next
|
|
161
|
+
* turn declares the transcript's tools plus the active ones. Sessions from
|
|
162
|
+
* before Pi recorded tool declarations declare none, so an active
|
|
163
|
+
* `apply_patch`, the only grammar tool Pi's tool registry can identify, is
|
|
164
|
+
* added as that turn would add it. Its replayed calls then keep their custom
|
|
165
|
+
* tool call and output types.
|
|
166
|
+
*/
|
|
190
167
|
function fallbackGrammarToolInputProperties(
|
|
191
|
-
activeNames: readonly string[],
|
|
192
168
|
model: Model<Api>,
|
|
169
|
+
transcript: readonly Message[],
|
|
170
|
+
activeNames: readonly string[],
|
|
193
171
|
): GrammarToolInputProperties {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
172
|
+
const properties = new Map(requestGrammarToolInputProperties(model, transcript));
|
|
173
|
+
if (
|
|
174
|
+
!properties.has(APPLY_PATCH_TOOL_NAME) &&
|
|
175
|
+
activeNames.includes(APPLY_PATCH_TOOL_NAME) &&
|
|
176
|
+
responsesCompatibility(model.compat).supportsOpenAIGrammarTools
|
|
177
|
+
) {
|
|
178
|
+
properties.set(APPLY_PATCH_TOOL_NAME, APPLY_PATCH_INPUT_PROPERTY);
|
|
179
|
+
}
|
|
180
|
+
return properties;
|
|
198
181
|
}
|
|
199
182
|
|
|
200
183
|
function compactionMetadata(reason: SessionBeforeCompactEvent["reason"]): CodexCompactionMetadata {
|
|
@@ -242,13 +225,19 @@ export default function registerRemoteCompaction(
|
|
|
242
225
|
if (!authentication.apiKey) throw new Error("OpenAI Codex authentication is unavailable.");
|
|
243
226
|
|
|
244
227
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
245
|
-
const allTools = pi.getAllTools();
|
|
246
228
|
const cached = runtime.latestTemplate(sessionId);
|
|
247
229
|
const matching = cached?.modelId === ctx.model.id ? cached : undefined;
|
|
230
|
+
// Declare the tools the branch's latest request to this model sent, as
|
|
231
|
+
// saved in the session. The transcript also lists tools Pi leaves out of
|
|
232
|
+
// requests (codemode `only` mode), and other extensions can edit them.
|
|
233
|
+
// Sessions without a saved entry, such as those older versions wrote,
|
|
234
|
+
// declare the transcript's current tools.
|
|
235
|
+
const saved = latestRequestTools(event.branchEntries, ctx.model.id);
|
|
236
|
+
const transcript = saved ? undefined : branchTranscript(event.branchEntries);
|
|
248
237
|
const config = resolveConfig(ctx);
|
|
249
238
|
const grammarToolInputProperties =
|
|
250
|
-
|
|
251
|
-
fallbackGrammarToolInputProperties(pi.getActiveTools()
|
|
239
|
+
saved?.grammarToolInputProperties ??
|
|
240
|
+
fallbackGrammarToolInputProperties(ctx.model, transcript ?? [], pi.getActiveTools());
|
|
252
241
|
const history = providerHistory({
|
|
253
242
|
branch: event.branchEntries,
|
|
254
243
|
wireModel: ctx.model,
|
|
@@ -256,22 +245,10 @@ export default function registerRemoteCompaction(
|
|
|
256
245
|
imageDetail: config.imageDetail,
|
|
257
246
|
recoverLatestOverflowPrefix: event.reason === "overflow" && event.willRetry,
|
|
258
247
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// changed since that turn, or when no turn template exists.
|
|
264
|
-
const template: JsonRecord = { ...matching?.payload };
|
|
265
|
-
const activeNames = pi.getActiveTools();
|
|
266
|
-
const cachedTools = Array.isArray(matching?.payload.tools)
|
|
267
|
-
? matching.payload.tools.filter(isObject)
|
|
268
|
-
: undefined;
|
|
269
|
-
template.tools =
|
|
270
|
-
cachedTools &&
|
|
271
|
-
matching &&
|
|
272
|
-
declaresActiveTools(cachedTools, matching.toolFingerprint, allTools, activeNames)
|
|
273
|
-
? cachedTools
|
|
274
|
-
: (activeResponsesTools(allTools, activeNames, grammarToolInputProperties) ?? []);
|
|
248
|
+
const template: JsonRecord = {
|
|
249
|
+
...matching?.payload,
|
|
250
|
+
tools: saved?.tools ?? requestTools(ctx.model, getCurrentTools(transcript ?? [])),
|
|
251
|
+
};
|
|
275
252
|
const requestOptions: OpenAICodexResponsesOptions = {
|
|
276
253
|
...matching?.requestOptions,
|
|
277
254
|
apiKey: authentication.apiKey,
|
|
@@ -291,9 +268,7 @@ export default function registerRemoteCompaction(
|
|
|
291
268
|
branchInstructions(ctx.model, event.branchEntries) || ctx.getSystemPrompt(),
|
|
292
269
|
event.customInstructions,
|
|
293
270
|
),
|
|
294
|
-
grammarToolInputProperties
|
|
295
|
-
matching?.grammarToolInputProperties ??
|
|
296
|
-
requestGrammarToolInputProperties(template, allTools),
|
|
271
|
+
grammarToolInputProperties,
|
|
297
272
|
template,
|
|
298
273
|
priority: config.fastMode,
|
|
299
274
|
compactionMetadata: compactionMetadata(event.reason),
|
|
@@ -19,10 +19,6 @@ export function isCodexModel(model: Model<Api> | undefined): model is Model<type
|
|
|
19
19
|
return model !== undefined && model.provider === CODEX_PROVIDER && hasApi(model, CODEX_API);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function supportsReasoningMode(modelId: string): boolean {
|
|
23
|
-
return modelId === "gpt-6-astra" || /^gpt-5\.6(?:-|$)/.test(modelId);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
22
|
function isWebSearchTool(value: unknown): boolean {
|
|
27
23
|
return isObject(value) && value.type === "web_search";
|
|
28
24
|
}
|
|
@@ -72,11 +68,8 @@ export function applyCodexRequestOptions(
|
|
|
72
68
|
} else {
|
|
73
69
|
updatedReasoning["summary"] = config.reasoningSummary;
|
|
74
70
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} else {
|
|
78
|
-
Reflect.deleteProperty(updatedReasoning, "mode");
|
|
79
|
-
}
|
|
71
|
+
// The Codex endpoint rejects `reasoning.mode`.
|
|
72
|
+
Reflect.deleteProperty(updatedReasoning, "mode");
|
|
80
73
|
result["reasoning"] = updatedReasoning;
|
|
81
74
|
}
|
|
82
75
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { isString } from "./value-contracts.ts";
|
|
2
|
+
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { isJsonValue, isObject, type JsonRecord } from "./codex-protocol.ts";
|
|
4
|
+
import type { GrammarToolInputProperties } from "./compaction-checkpoint.ts";
|
|
5
|
+
import { stableResponsesJson } from "./responses-replay.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Custom session entry holding the exact tool declarations a turn request sent.
|
|
9
|
+
* Pi's transcript declares every active tool, but Pi removes some from requests
|
|
10
|
+
* (codemode `only` mode) and other extensions can edit the request. Native
|
|
11
|
+
* compaction declares these saved tools so it shares the turns' prompt-cache
|
|
12
|
+
* prefix, also after a resume or a branch switch.
|
|
13
|
+
*/
|
|
14
|
+
export const REQUEST_TOOLS_ENTRY_TYPE = "openai-codex-compat-request-tools";
|
|
15
|
+
export const REQUEST_TOOLS_FORMAT_VERSION = 1;
|
|
16
|
+
|
|
17
|
+
export type RequestToolsData = {
|
|
18
|
+
kind: typeof REQUEST_TOOLS_ENTRY_TYPE;
|
|
19
|
+
version: typeof REQUEST_TOOLS_FORMAT_VERSION;
|
|
20
|
+
modelId: string;
|
|
21
|
+
tools: JsonRecord[];
|
|
22
|
+
/** Input properties of grammar tools, which replayed history encodes as custom tool calls. */
|
|
23
|
+
grammarToolInputProperties: Array<[string, string]>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type RequestTools = {
|
|
27
|
+
tools: JsonRecord[];
|
|
28
|
+
grammarToolInputProperties: GrammarToolInputProperties;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function requestToolsData(
|
|
32
|
+
modelId: string,
|
|
33
|
+
tools: readonly JsonRecord[],
|
|
34
|
+
grammarToolInputProperties: GrammarToolInputProperties,
|
|
35
|
+
): RequestToolsData {
|
|
36
|
+
return {
|
|
37
|
+
kind: REQUEST_TOOLS_ENTRY_TYPE,
|
|
38
|
+
version: REQUEST_TOOLS_FORMAT_VERSION,
|
|
39
|
+
modelId,
|
|
40
|
+
tools: tools.map((tool) => structuredClone(tool)),
|
|
41
|
+
grammarToolInputProperties: [...grammarToolInputProperties].toSorted(([left], [right]) =>
|
|
42
|
+
left.localeCompare(right),
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseRequestTools(value: unknown): RequestToolsData | undefined {
|
|
48
|
+
if (
|
|
49
|
+
!isObject(value) ||
|
|
50
|
+
value.kind !== REQUEST_TOOLS_ENTRY_TYPE ||
|
|
51
|
+
value.version !== REQUEST_TOOLS_FORMAT_VERSION ||
|
|
52
|
+
!isString(value.modelId) ||
|
|
53
|
+
!Array.isArray(value["tools"]) ||
|
|
54
|
+
!Array.isArray(value["grammarToolInputProperties"])
|
|
55
|
+
) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const tools: JsonRecord[] = [];
|
|
59
|
+
for (const tool of value["tools"]) {
|
|
60
|
+
if (!isObject(tool) || !isJsonValue(tool)) return undefined;
|
|
61
|
+
tools.push(structuredClone(tool));
|
|
62
|
+
}
|
|
63
|
+
const properties: Array<[string, string]> = [];
|
|
64
|
+
for (const pair of value["grammarToolInputProperties"]) {
|
|
65
|
+
if (!Array.isArray(pair) || pair.length !== 2) return undefined;
|
|
66
|
+
const [name, property] = pair;
|
|
67
|
+
if (!isString(name) || !isString(property)) return undefined;
|
|
68
|
+
properties.push([name, property]);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
kind: REQUEST_TOOLS_ENTRY_TYPE,
|
|
72
|
+
version: REQUEST_TOOLS_FORMAT_VERSION,
|
|
73
|
+
modelId: value.modelId,
|
|
74
|
+
tools,
|
|
75
|
+
grammarToolInputProperties: properties,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The tools of the branch's latest request to a model. Entries that do not
|
|
81
|
+
* parse, such as a format a later version wrote, are skipped.
|
|
82
|
+
*/
|
|
83
|
+
export function latestRequestTools(
|
|
84
|
+
branch: readonly SessionEntry[],
|
|
85
|
+
modelId: string,
|
|
86
|
+
): RequestTools | undefined {
|
|
87
|
+
const data = latestRequestToolsData(branch, modelId);
|
|
88
|
+
return data
|
|
89
|
+
? { tools: data.tools, grammarToolInputProperties: new Map(data.grammarToolInputProperties) }
|
|
90
|
+
: undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function latestRequestToolsData(
|
|
94
|
+
branch: readonly SessionEntry[],
|
|
95
|
+
modelId: string,
|
|
96
|
+
): RequestToolsData | undefined {
|
|
97
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
98
|
+
const entry = branch[index];
|
|
99
|
+
if (entry?.type !== "custom" || entry.customType !== REQUEST_TOOLS_ENTRY_TYPE) continue;
|
|
100
|
+
const data = parseRequestTools(entry.data);
|
|
101
|
+
if (data?.modelId === modelId) return data;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Whether a request's tools differ from the branch's latest saved tools for its model. */
|
|
107
|
+
export function requestToolsChanged(
|
|
108
|
+
branch: readonly SessionEntry[],
|
|
109
|
+
data: RequestToolsData,
|
|
110
|
+
): boolean {
|
|
111
|
+
const latest = latestRequestToolsData(branch, data.modelId);
|
|
112
|
+
return latest === undefined || stableResponsesJson(latest) !== stableResponsesJson(data);
|
|
113
|
+
}
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
CODEX_TOOL_BACKGROUND_SCHEMA,
|
|
18
18
|
CODEX_SHELL_TOOL_SCHEMA,
|
|
19
19
|
IMAGE_DETAIL_SCHEMA,
|
|
20
|
-
REASONING_MODE_SCHEMA,
|
|
21
20
|
REASONING_SUMMARY_SCHEMA,
|
|
22
21
|
TEXT_VERBOSITY_SCHEMA,
|
|
23
22
|
WEB_SEARCH_MODE_SCHEMA,
|
|
@@ -32,7 +31,7 @@ import {
|
|
|
32
31
|
} from "./config.ts";
|
|
33
32
|
import type { ConfigContext } from "./config-context.ts";
|
|
34
33
|
import { selectedRegistryModel } from "./model-context.ts";
|
|
35
|
-
import { isCodexModel
|
|
34
|
+
import { isCodexModel } from "./request-options.ts";
|
|
36
35
|
import { usesResponsesLite } from "./responses-lite.ts";
|
|
37
36
|
|
|
38
37
|
export const settingFields: SettingsField[] = [
|
|
@@ -60,12 +59,6 @@ export const settingFields: SettingsField[] = [
|
|
|
60
59
|
description: "Choose reasoning summary detail, or omit summaries.",
|
|
61
60
|
choices: [...REASONING_SUMMARY_SCHEMA.enum],
|
|
62
61
|
},
|
|
63
|
-
{
|
|
64
|
-
id: "reasoningMode",
|
|
65
|
-
label: "Reasoning mode",
|
|
66
|
-
description: "Standard or pro execution on supported models, independent of effort.",
|
|
67
|
-
choices: [...REASONING_MODE_SCHEMA.enum],
|
|
68
|
-
},
|
|
69
62
|
{
|
|
70
63
|
id: "toolBackground",
|
|
71
64
|
label: "Codex tool background",
|
|
@@ -205,9 +198,7 @@ export default function registerCodexSettings(
|
|
|
205
198
|
const model = selectedRegistryModel(ctx);
|
|
206
199
|
const fields = settingFields.map((field) => {
|
|
207
200
|
const inactive =
|
|
208
|
-
!isCodexModel(model) ||
|
|
209
|
-
(field.id === "responsesLite" && !usesResponsesLite(model.id)) ||
|
|
210
|
-
(field.id === "reasoningMode" && !supportsReasoningMode(model.id));
|
|
201
|
+
!isCodexModel(model) || (field.id === "responsesLite" && !usesResponsesLite(model.id));
|
|
211
202
|
return {
|
|
212
203
|
...field,
|
|
213
204
|
description: `${inactive ? "Inactive on this model. " : ""}${field.description}`,
|
|
@@ -169,6 +169,8 @@ export default function registerWebRun(
|
|
|
169
169
|
parameters: WEB_RUN_PARAMETERS,
|
|
170
170
|
executionMode: "parallel",
|
|
171
171
|
renderShell: "self",
|
|
172
|
+
// Searching and browsing change nothing locally but reach the open web.
|
|
173
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
172
174
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
173
175
|
const model = ctx.model;
|
|
174
176
|
if (!isCodexModel(model)) {
|
|
@@ -57,22 +57,25 @@ export function readSessionSettings(
|
|
|
57
57
|
!object(data["observed"])
|
|
58
58
|
)
|
|
59
59
|
return fail();
|
|
60
|
+
// Newer versions may remove a setting or narrow its values. Skip such fields so a session
|
|
61
|
+
// saved by an older version still resumes; only a malformed record fails.
|
|
60
62
|
const values: SettingValues = {};
|
|
61
63
|
const session: SettingsSessionState = { changes: {} };
|
|
62
64
|
for (const [id, value] of Object.entries(data["values"])) {
|
|
63
65
|
const field = fields.find((candidate) => candidate.id === id);
|
|
64
|
-
if (
|
|
65
|
-
values[id] = value;
|
|
66
|
+
if (field && valid(field, value)) values[id] = value;
|
|
66
67
|
}
|
|
67
68
|
const observed: Record<string, [string, string]> = {};
|
|
68
69
|
for (const [id, change] of Object.entries(data["changes"])) {
|
|
70
|
+
if (!Array.isArray(change) || change.length > 1) return fail();
|
|
69
71
|
const field = fields.find((candidate) => candidate.id === id);
|
|
70
|
-
if (!field || !
|
|
71
|
-
return fail();
|
|
72
|
+
if (!field || !Object.hasOwn(values, id)) continue;
|
|
72
73
|
const value: unknown = change[0];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
let next: SettingValue | undefined;
|
|
75
|
+
if (change.length !== 0) {
|
|
76
|
+
if (!valid(field, value)) continue;
|
|
77
|
+
next = value;
|
|
78
|
+
}
|
|
76
79
|
const hashes = data["observed"][id];
|
|
77
80
|
if (!Array.isArray(hashes) || hashes.length !== 2) return fail();
|
|
78
81
|
const global: unknown = hashes[0];
|
|
@@ -84,6 +87,7 @@ export function readSessionSettings(
|
|
|
84
87
|
!/^[a-f0-9]{64}$/.test(project)
|
|
85
88
|
)
|
|
86
89
|
return fail();
|
|
90
|
+
session.changes[id] = next;
|
|
87
91
|
observed[id] = [global, project];
|
|
88
92
|
}
|
|
89
93
|
if (Object.keys(session.changes).length) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-openai-codex-compat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "OpenAI Codex compatibility for Pi with native compaction, fast mode, and Codex-optimized capabilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@2h2d/oxlint-config": "0.1.1",
|
|
53
53
|
"@2h2d/ts-config": "0.0.1",
|
|
54
|
-
"@earendil-works/pi-ai": "0.
|
|
55
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
56
|
-
"@earendil-works/pi-tui": "0.
|
|
54
|
+
"@earendil-works/pi-ai": "0.99.1",
|
|
55
|
+
"@earendil-works/pi-coding-agent": "0.99.1",
|
|
56
|
+
"@earendil-works/pi-tui": "0.99.1",
|
|
57
57
|
"@types/node": "22.20.1",
|
|
58
58
|
"oxfmt": "0.64.0",
|
|
59
59
|
"oxlint": "1.79.0",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"typescript": "7.0.2"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
|
-
"@earendil-works/pi-ai": ">=0.
|
|
66
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
67
|
-
"@earendil-works/pi-tui": ">=0.
|
|
65
|
+
"@earendil-works/pi-ai": ">=0.99.1 <0.100.0",
|
|
66
|
+
"@earendil-works/pi-coding-agent": ">=0.99.1 <0.100.0",
|
|
67
|
+
"@earendil-works/pi-tui": ">=0.99.1 <0.100.0",
|
|
68
68
|
"typebox": "*"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|