pi-openai-codex-compat 0.0.7-alpha.0 → 0.0.8
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/APPLY_PATCH_INSTRUCTION_FEEDBACK.md +617 -0
- package/CHANGELOG.md +46 -0
- package/LICENSES/tree-sitter-wasms-MIT.txt +21 -0
- package/LICENSES/web-tree-sitter-MIT.txt +21 -0
- package/README.md +31 -9
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/extensions/openai-codex-compat/apply-patch-diff-render.ts +499 -20
- package/extensions/openai-codex-compat/apply-patch-engine.ts +4085 -491
- package/extensions/openai-codex-compat/apply-patch-matcher.ts +1535 -0
- package/extensions/openai-codex-compat/apply-patch-render.ts +85 -19
- package/extensions/openai-codex-compat/apply-patch.ts +41 -5
- package/extensions/openai-codex-compat/codex-provider.ts +256 -21
- package/extensions/openai-codex-compat/codex-stream.ts +83 -79
- package/extensions/openai-codex-compat/codex-transport.ts +0 -10
- package/extensions/openai-codex-compat/compaction-checkpoint.ts +28 -0
- package/extensions/openai-codex-compat/config.ts +18 -0
- package/extensions/openai-codex-compat/footer.ts +4 -8
- package/extensions/openai-codex-compat/index.ts +4 -1
- package/extensions/openai-codex-compat/remote-compaction.ts +4 -0
- package/extensions/openai-codex-compat/settings-pane.ts +11 -0
- package/extensions/openai-codex-compat/tools.ts +2 -1
- package/package.json +9 -4
|
@@ -141,11 +141,6 @@ function appendCustomInput(
|
|
|
141
141
|
return delta;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
function responseItems(value: unknown): JsonRecord[] {
|
|
145
|
-
if (!Array.isArray(value)) return [];
|
|
146
|
-
return value.filter(isObject);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
144
|
function itemContentText(item: JsonRecord): string {
|
|
150
145
|
if (!Array.isArray(item.content)) return "";
|
|
151
146
|
return item.content
|
|
@@ -209,7 +204,8 @@ export async function processCodexStream(
|
|
|
209
204
|
): Promise<void> {
|
|
210
205
|
let terminal = false;
|
|
211
206
|
const slots = new Map<number, OutputSlot>();
|
|
212
|
-
const
|
|
207
|
+
const completedOutputItems = new Set<string>();
|
|
208
|
+
const completedToolCallContentIndexes = new Set<number>();
|
|
213
209
|
const applyMessagePhaseStopReason = (item: JsonRecord): void => {
|
|
214
210
|
if (item.type === "message" && item["phase"] === "final_answer") {
|
|
215
211
|
output.stopReason = "stop";
|
|
@@ -318,6 +314,80 @@ export async function processCodexStream(
|
|
|
318
314
|
const slotFor = (index: number, item: JsonRecord): OutputSlot | undefined =>
|
|
319
315
|
slots.get(index) ?? createSlot(index, item);
|
|
320
316
|
|
|
317
|
+
const outputItemKey = (index: number, item: JsonRecord): string => {
|
|
318
|
+
if (typeof item.id === "string") return `id:${item.id}`;
|
|
319
|
+
if (typeof item["call_id"] === "string") {
|
|
320
|
+
return `call:${String(item.type)}:${item["call_id"]}`;
|
|
321
|
+
}
|
|
322
|
+
return `index:${String(index)}:${String(item.type)}`;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const completeOutputItem = (index: number, item: JsonRecord): void => {
|
|
326
|
+
const key = outputItemKey(index, item);
|
|
327
|
+
if (completedOutputItems.has(key)) return;
|
|
328
|
+
completedOutputItems.add(key);
|
|
329
|
+
applyMessagePhaseStopReason(item);
|
|
330
|
+
const slot = slotFor(index, item);
|
|
331
|
+
if (item.type === "reasoning" && slot?.type === "thinking") {
|
|
332
|
+
slot.block.thinking = reasoningText(item) || slot.block.thinking;
|
|
333
|
+
slot.block.thinkingSignature = JSON.stringify(item);
|
|
334
|
+
stream.push({
|
|
335
|
+
type: "thinking_end",
|
|
336
|
+
contentIndex: slot.contentIndex,
|
|
337
|
+
content: slot.block.thinking,
|
|
338
|
+
partial: output,
|
|
339
|
+
});
|
|
340
|
+
trackCompleted(slot);
|
|
341
|
+
slots.delete(index);
|
|
342
|
+
} else if (item.type === "message" && slot?.type === "text") {
|
|
343
|
+
slot.block.text = itemContentText(item);
|
|
344
|
+
if (typeof item.id === "string") {
|
|
345
|
+
slot.block.textSignature = encodeTextSignature(item.id, item["phase"]);
|
|
346
|
+
}
|
|
347
|
+
stream.push({
|
|
348
|
+
type: "text_end",
|
|
349
|
+
contentIndex: slot.contentIndex,
|
|
350
|
+
content: slot.block.text,
|
|
351
|
+
partial: output,
|
|
352
|
+
});
|
|
353
|
+
trackCompleted(slot);
|
|
354
|
+
slots.delete(index);
|
|
355
|
+
} else if (
|
|
356
|
+
item.type === "function_call" &&
|
|
357
|
+
slot?.type === "toolCall" &&
|
|
358
|
+
slot.block.partialJson !== undefined
|
|
359
|
+
) {
|
|
360
|
+
slot.block.name = piToolCallName(item);
|
|
361
|
+
const argumentsJson =
|
|
362
|
+
typeof item.arguments === "string" ? item.arguments : slot.block.partialJson || "{}";
|
|
363
|
+
slot.block.arguments = parseStreamingJson(argumentsJson);
|
|
364
|
+
delete slot.block.partialJson;
|
|
365
|
+
stream.push({
|
|
366
|
+
type: "toolcall_end",
|
|
367
|
+
contentIndex: slot.contentIndex,
|
|
368
|
+
toolCall: slot.block,
|
|
369
|
+
partial: output,
|
|
370
|
+
});
|
|
371
|
+
trackCompleted(slot);
|
|
372
|
+
completedToolCallContentIndexes.add(slot.contentIndex);
|
|
373
|
+
slots.delete(index);
|
|
374
|
+
} else if (item.type === "custom_tool_call" && slot?.type === "toolCall") {
|
|
375
|
+
slot.block.name = piToolCallName(item);
|
|
376
|
+
const input = typeof item["input"] === "string" ? item["input"] : customInput(slot.block);
|
|
377
|
+
pushToolDelta(slot, appendCustomInput(slot.block, input, true));
|
|
378
|
+
delete slot.block.customInput;
|
|
379
|
+
stream.push({
|
|
380
|
+
type: "toolcall_end",
|
|
381
|
+
contentIndex: slot.contentIndex,
|
|
382
|
+
toolCall: slot.block,
|
|
383
|
+
partial: output,
|
|
384
|
+
});
|
|
385
|
+
trackCompleted(slot);
|
|
386
|
+
completedToolCallContentIndexes.add(slot.contentIndex);
|
|
387
|
+
slots.delete(index);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
321
391
|
const finalize = (response: JsonRecord): void => {
|
|
322
392
|
terminal = true;
|
|
323
393
|
if (typeof response.id === "string") output.responseId = response.id;
|
|
@@ -350,18 +420,6 @@ export async function processCodexStream(
|
|
|
350
420
|
output.usage,
|
|
351
421
|
typeof response.service_tier === "string" ? response.service_tier : undefined,
|
|
352
422
|
);
|
|
353
|
-
for (const item of responseItems(response["output"])) {
|
|
354
|
-
if (item.type !== "reasoning" || typeof item.id !== "string") continue;
|
|
355
|
-
const block = reasoningById.get(item.id);
|
|
356
|
-
if (!block?.thinkingSignature || typeof item.encrypted_content !== "string") continue;
|
|
357
|
-
const stored = JSON.parse(block.thinkingSignature) as JsonRecord;
|
|
358
|
-
if (typeof stored.encrypted_content !== "string") {
|
|
359
|
-
block.thinkingSignature = JSON.stringify({
|
|
360
|
-
...stored,
|
|
361
|
-
encrypted_content: item.encrypted_content,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
423
|
const status = normalizeCodexStatus(response["status"]);
|
|
366
424
|
const incompleteDetails = isObject(response["incomplete_details"])
|
|
367
425
|
? response["incomplete_details"]
|
|
@@ -376,7 +434,12 @@ export async function processCodexStream(
|
|
|
376
434
|
output.stopReason = mappedStop.stopReason;
|
|
377
435
|
if (mappedStop.errorMessage === undefined) delete output.errorMessage;
|
|
378
436
|
else output.errorMessage = mappedStop.errorMessage;
|
|
379
|
-
if (
|
|
437
|
+
if (
|
|
438
|
+
output.stopReason === "stop" &&
|
|
439
|
+
[...completedToolCallContentIndexes].some(
|
|
440
|
+
(contentIndex) => output.content[contentIndex]?.type === "toolCall",
|
|
441
|
+
)
|
|
442
|
+
) {
|
|
380
443
|
output.stopReason = "toolUse";
|
|
381
444
|
}
|
|
382
445
|
};
|
|
@@ -455,66 +518,7 @@ export async function processCodexStream(
|
|
|
455
518
|
if (!slot || typeof event["input"] !== "string") continue;
|
|
456
519
|
pushToolDelta(slot, appendCustomInput(slot.block, event["input"], true));
|
|
457
520
|
} else if (event.type === "response.output_item.done" && isObject(event.item)) {
|
|
458
|
-
|
|
459
|
-
applyMessagePhaseStopReason(item);
|
|
460
|
-
const slot = slotFor(index, item);
|
|
461
|
-
if (item.type === "reasoning" && slot?.type === "thinking") {
|
|
462
|
-
slot.block.thinking = reasoningText(item) || slot.block.thinking;
|
|
463
|
-
slot.block.thinkingSignature = JSON.stringify(item);
|
|
464
|
-
if (typeof item.id === "string") reasoningById.set(item.id, slot.block);
|
|
465
|
-
stream.push({
|
|
466
|
-
type: "thinking_end",
|
|
467
|
-
contentIndex: slot.contentIndex,
|
|
468
|
-
content: slot.block.thinking,
|
|
469
|
-
partial: output,
|
|
470
|
-
});
|
|
471
|
-
trackCompleted(slot);
|
|
472
|
-
slots.delete(index);
|
|
473
|
-
} else if (item.type === "message" && slot?.type === "text") {
|
|
474
|
-
slot.block.text = itemContentText(item);
|
|
475
|
-
if (typeof item.id === "string") {
|
|
476
|
-
slot.block.textSignature = encodeTextSignature(item.id, item["phase"]);
|
|
477
|
-
}
|
|
478
|
-
stream.push({
|
|
479
|
-
type: "text_end",
|
|
480
|
-
contentIndex: slot.contentIndex,
|
|
481
|
-
content: slot.block.text,
|
|
482
|
-
partial: output,
|
|
483
|
-
});
|
|
484
|
-
trackCompleted(slot);
|
|
485
|
-
slots.delete(index);
|
|
486
|
-
} else if (
|
|
487
|
-
item.type === "function_call" &&
|
|
488
|
-
slot?.type === "toolCall" &&
|
|
489
|
-
slot.block.partialJson !== undefined
|
|
490
|
-
) {
|
|
491
|
-
slot.block.name = piToolCallName(item);
|
|
492
|
-
const argumentsJson =
|
|
493
|
-
typeof item.arguments === "string" ? item.arguments : slot.block.partialJson || "{}";
|
|
494
|
-
slot.block.arguments = parseStreamingJson(argumentsJson);
|
|
495
|
-
delete slot.block.partialJson;
|
|
496
|
-
stream.push({
|
|
497
|
-
type: "toolcall_end",
|
|
498
|
-
contentIndex: slot.contentIndex,
|
|
499
|
-
toolCall: slot.block,
|
|
500
|
-
partial: output,
|
|
501
|
-
});
|
|
502
|
-
trackCompleted(slot);
|
|
503
|
-
slots.delete(index);
|
|
504
|
-
} else if (item.type === "custom_tool_call" && slot?.type === "toolCall") {
|
|
505
|
-
slot.block.name = piToolCallName(item);
|
|
506
|
-
const input = typeof item["input"] === "string" ? item["input"] : customInput(slot.block);
|
|
507
|
-
pushToolDelta(slot, appendCustomInput(slot.block, input, true));
|
|
508
|
-
delete slot.block.customInput;
|
|
509
|
-
stream.push({
|
|
510
|
-
type: "toolcall_end",
|
|
511
|
-
contentIndex: slot.contentIndex,
|
|
512
|
-
toolCall: slot.block,
|
|
513
|
-
partial: output,
|
|
514
|
-
});
|
|
515
|
-
trackCompleted(slot);
|
|
516
|
-
slots.delete(index);
|
|
517
|
-
}
|
|
521
|
+
completeOutputItem(index, event.item);
|
|
518
522
|
} else if (
|
|
519
523
|
(event.type === "response.completed" || event.type === "response.incomplete") &&
|
|
520
524
|
isObject(event.response)
|
|
@@ -1910,16 +1910,6 @@ async function* requestWebSocket(
|
|
|
1910
1910
|
responseCompleted = true;
|
|
1911
1911
|
}
|
|
1912
1912
|
if (typeof event.response.id === "string") responseId = event.response.id;
|
|
1913
|
-
if (Array.isArray(event.response["output"])) {
|
|
1914
|
-
const terminalItems = event.response["output"].filter(isObject);
|
|
1915
|
-
if (terminalItems.length > 0) {
|
|
1916
|
-
responseItems.splice(
|
|
1917
|
-
0,
|
|
1918
|
-
responseItems.length,
|
|
1919
|
-
...terminalItems.map((item) => structuredClone(item)),
|
|
1920
|
-
);
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
1913
|
}
|
|
1924
1914
|
const normalized = normalizeEvent(event);
|
|
1925
1915
|
if (!normalized) continue;
|
|
@@ -25,11 +25,17 @@ import { convertResponsesMessages } from "./vendor/pi-ai/openai-responses-serial
|
|
|
25
25
|
export const CHECKPOINT_ENTRY_TYPE = "openai-codex-compat-remote-compaction";
|
|
26
26
|
export const CHECKPOINT_FORMAT_VERSION = 1;
|
|
27
27
|
|
|
28
|
+
export type CompactionDecision = {
|
|
29
|
+
reason: "manual" | "threshold" | "overflow" | "provider-boundary";
|
|
30
|
+
willRetry: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
28
33
|
export type CheckpointData = {
|
|
29
34
|
kind: typeof CHECKPOINT_ENTRY_TYPE;
|
|
30
35
|
version: typeof CHECKPOINT_FORMAT_VERSION;
|
|
31
36
|
modelId: string;
|
|
32
37
|
history: ResponsesItem[];
|
|
38
|
+
compactionDecision?: CompactionDecision;
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
export type CheckpointSearch =
|
|
@@ -180,11 +186,31 @@ export function parseCheckpoint(value: unknown): CheckpointData | undefined {
|
|
|
180
186
|
return undefined;
|
|
181
187
|
}
|
|
182
188
|
|
|
189
|
+
const rawDecision = value["compactionDecision"];
|
|
190
|
+
let compactionDecision: CompactionDecision | undefined;
|
|
191
|
+
if (rawDecision !== undefined) {
|
|
192
|
+
if (
|
|
193
|
+
!isObject(rawDecision) ||
|
|
194
|
+
(rawDecision["reason"] !== "manual" &&
|
|
195
|
+
rawDecision["reason"] !== "threshold" &&
|
|
196
|
+
rawDecision["reason"] !== "overflow" &&
|
|
197
|
+
rawDecision["reason"] !== "provider-boundary") ||
|
|
198
|
+
typeof rawDecision["willRetry"] !== "boolean"
|
|
199
|
+
) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
compactionDecision = {
|
|
203
|
+
reason: rawDecision["reason"],
|
|
204
|
+
willRetry: rawDecision["willRetry"],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
183
208
|
return {
|
|
184
209
|
kind: CHECKPOINT_ENTRY_TYPE,
|
|
185
210
|
version: CHECKPOINT_FORMAT_VERSION,
|
|
186
211
|
modelId: value.modelId,
|
|
187
212
|
history,
|
|
213
|
+
...(compactionDecision ? { compactionDecision } : {}),
|
|
188
214
|
};
|
|
189
215
|
}
|
|
190
216
|
|
|
@@ -229,6 +255,7 @@ export function checkpointData(
|
|
|
229
255
|
inputHistory: readonly ResponsesItem[],
|
|
230
256
|
compactionItem: ResponsesItem,
|
|
231
257
|
postCompactionTail: readonly ResponsesItem[] = [],
|
|
258
|
+
compactionDecision?: CompactionDecision,
|
|
232
259
|
): CheckpointData {
|
|
233
260
|
return {
|
|
234
261
|
kind: CHECKPOINT_ENTRY_TYPE,
|
|
@@ -238,6 +265,7 @@ export function checkpointData(
|
|
|
238
265
|
...installCompactionItem(inputHistory, compactionItem),
|
|
239
266
|
...postCompactionTail.map((item) => structuredClone(item)),
|
|
240
267
|
],
|
|
268
|
+
...(compactionDecision ? { compactionDecision: { ...compactionDecision } } : {}),
|
|
241
269
|
};
|
|
242
270
|
}
|
|
243
271
|
|
|
@@ -20,6 +20,8 @@ export interface CodexCompatConfig {
|
|
|
20
20
|
responsesLite: boolean;
|
|
21
21
|
/** Replace Pi's active edit and write tools with the extension's apply_patch tool. */
|
|
22
22
|
applyPatch: boolean;
|
|
23
|
+
/** Show exact model-facing apply_patch feedback while its TUI result is collapsed. */
|
|
24
|
+
applyPatchDebug: boolean;
|
|
23
25
|
/** Select the shared background surface for extension-owned Codex tools. */
|
|
24
26
|
toolBackground: CodexToolBackground;
|
|
25
27
|
/** Expose the standalone Codex image-generation namespace tool. */
|
|
@@ -44,6 +46,7 @@ export const CONFIG_ENVIRONMENT_VARIABLES = {
|
|
|
44
46
|
fastMode: `${ENV_PREFIX}FAST_MODE`,
|
|
45
47
|
responsesLite: `${ENV_PREFIX}RESPONSES_LITE`,
|
|
46
48
|
applyPatch: `${ENV_PREFIX}APPLY_PATCH`,
|
|
49
|
+
applyPatchDebug: `${ENV_PREFIX}APPLY_PATCH_DEBUG`,
|
|
47
50
|
toolBackground: `${ENV_PREFIX}TOOL_BACKGROUND`,
|
|
48
51
|
imageGeneration: `${ENV_PREFIX}IMAGE_GENERATION`,
|
|
49
52
|
imageDetail: `${ENV_PREFIX}IMAGE_DETAIL`,
|
|
@@ -59,6 +62,7 @@ export type ConfigLayer = {
|
|
|
59
62
|
fastMode?: boolean;
|
|
60
63
|
responsesLite?: boolean;
|
|
61
64
|
applyPatch?: boolean;
|
|
65
|
+
applyPatchDebug?: boolean;
|
|
62
66
|
toolBackground?: CodexToolBackground;
|
|
63
67
|
imageGeneration?: boolean;
|
|
64
68
|
imageDetail?: ImageDetail;
|
|
@@ -75,6 +79,7 @@ export const DEFAULT_CONFIG: CodexCompatConfig = {
|
|
|
75
79
|
fastMode: false,
|
|
76
80
|
responsesLite: false,
|
|
77
81
|
applyPatch: true,
|
|
82
|
+
applyPatchDebug: false,
|
|
78
83
|
toolBackground: "subtle",
|
|
79
84
|
imageGeneration: true,
|
|
80
85
|
imageDetail: "auto",
|
|
@@ -152,6 +157,12 @@ export function parseEnvironmentConfig(environment: Environment = process.env):
|
|
|
152
157
|
const applyPatch = environmentBoolean(environment, CONFIG_ENVIRONMENT_VARIABLES.applyPatch);
|
|
153
158
|
if (applyPatch !== undefined) layer.applyPatch = applyPatch;
|
|
154
159
|
|
|
160
|
+
const applyPatchDebug = environmentBoolean(
|
|
161
|
+
environment,
|
|
162
|
+
CONFIG_ENVIRONMENT_VARIABLES.applyPatchDebug,
|
|
163
|
+
);
|
|
164
|
+
if (applyPatchDebug !== undefined) layer.applyPatchDebug = applyPatchDebug;
|
|
165
|
+
|
|
155
166
|
const toolBackground = environmentEnum(
|
|
156
167
|
environment,
|
|
157
168
|
CONFIG_ENVIRONMENT_VARIABLES.toolBackground,
|
|
@@ -239,6 +250,9 @@ export function parseConfig(value: unknown): ConfigLayer {
|
|
|
239
250
|
const applyPatch = value["applyPatch"];
|
|
240
251
|
if (typeof applyPatch === "boolean") layer.applyPatch = applyPatch;
|
|
241
252
|
|
|
253
|
+
const applyPatchDebug = value["applyPatchDebug"];
|
|
254
|
+
if (typeof applyPatchDebug === "boolean") layer.applyPatchDebug = applyPatchDebug;
|
|
255
|
+
|
|
242
256
|
const toolBackground = value["toolBackground"];
|
|
243
257
|
if (
|
|
244
258
|
typeof toolBackground === "string" &&
|
|
@@ -318,6 +332,9 @@ export function resolveConfig(
|
|
|
318
332
|
...(typeof merged.fastMode === "boolean" ? { fastMode: merged.fastMode } : {}),
|
|
319
333
|
...(typeof merged.responsesLite === "boolean" ? { responsesLite: merged.responsesLite } : {}),
|
|
320
334
|
...(typeof merged.applyPatch === "boolean" ? { applyPatch: merged.applyPatch } : {}),
|
|
335
|
+
...(typeof merged.applyPatchDebug === "boolean"
|
|
336
|
+
? { applyPatchDebug: merged.applyPatchDebug }
|
|
337
|
+
: {}),
|
|
321
338
|
...(merged.toolBackground ? { toolBackground: merged.toolBackground } : {}),
|
|
322
339
|
...(typeof merged.imageGeneration === "boolean"
|
|
323
340
|
? { imageGeneration: merged.imageGeneration }
|
|
@@ -363,6 +380,7 @@ export function configLayer(config: CodexCompatConfig): ConfigLayer {
|
|
|
363
380
|
fastMode: config.fastMode,
|
|
364
381
|
responsesLite: config.responsesLite,
|
|
365
382
|
applyPatch: config.applyPatch,
|
|
383
|
+
applyPatchDebug: config.applyPatchDebug,
|
|
366
384
|
toolBackground: config.toolBackground,
|
|
367
385
|
imageGeneration: config.imageGeneration,
|
|
368
386
|
imageDetail: config.imageDetail,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type ExtensionContext,
|
|
5
5
|
type ReadonlyFooterDataProvider,
|
|
6
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
|
-
import type { Component
|
|
7
|
+
import type { Component } from "@earendil-works/pi-tui";
|
|
8
8
|
import type { CodexCompatConfig } from "./config.ts";
|
|
9
9
|
import { isCodexModel } from "./request-options.ts";
|
|
10
10
|
|
|
@@ -74,16 +74,15 @@ function footerSession(ctx: ExtensionContext, resolveConfig: ConfigResolver): Fo
|
|
|
74
74
|
|
|
75
75
|
class CodexFooter implements Component {
|
|
76
76
|
private readonly footer: FooterComponent;
|
|
77
|
-
private readonly unsubscribe: () => void;
|
|
78
77
|
|
|
79
78
|
constructor(
|
|
80
|
-
tui: TUI,
|
|
81
79
|
footerData: ReadonlyFooterDataProvider,
|
|
82
80
|
ctx: ExtensionContext,
|
|
83
81
|
resolveConfig: ConfigResolver,
|
|
84
82
|
) {
|
|
85
83
|
this.footer = new FooterComponent(footerSession(ctx, resolveConfig), footerData);
|
|
86
|
-
|
|
84
|
+
// Pi does not expose its live auto-compaction state to extension footers.
|
|
85
|
+
this.footer.setAutoCompactEnabled(false);
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
render(width: number): string[] {
|
|
@@ -95,14 +94,11 @@ class CodexFooter implements Component {
|
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
dispose(): void {
|
|
98
|
-
this.unsubscribe();
|
|
99
97
|
this.footer.dispose();
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
export function installCodexFooter(ctx: ExtensionContext, resolveConfig: ConfigResolver): void {
|
|
104
102
|
if (ctx.mode !== "tui") return;
|
|
105
|
-
ctx.ui.setFooter(
|
|
106
|
-
(tui, _theme, footerData) => new CodexFooter(tui, footerData, ctx, resolveConfig),
|
|
107
|
-
);
|
|
103
|
+
ctx.ui.setFooter((_tui, _theme, footerData) => new CodexFooter(footerData, ctx, resolveConfig));
|
|
108
104
|
}
|
|
@@ -32,6 +32,7 @@ function settingsSummary(ctx: ExtensionContext, config: CodexCompatConfig): stri
|
|
|
32
32
|
`reasoning mode: ${config.reasoningMode}`,
|
|
33
33
|
`Codex tool background: ${config.toolBackground}`,
|
|
34
34
|
`apply_patch: ${config.applyPatch ? "on" : "off"}`,
|
|
35
|
+
`apply_patch debug output: ${config.applyPatchDebug ? "on" : "off"}`,
|
|
35
36
|
`image_gen.imagegen: ${config.imageGeneration ? "on" : "off"}`,
|
|
36
37
|
`image result detail: ${config.imageDetail}`,
|
|
37
38
|
`web.run: ${config.webRun ? "on" : "off"}`,
|
|
@@ -51,6 +52,8 @@ export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
|
51
52
|
return activeConfig;
|
|
52
53
|
};
|
|
53
54
|
const resolveToolBackground = () => activeConfig?.toolBackground ?? DEFAULT_CONFIG.toolBackground;
|
|
55
|
+
const resolveApplyPatchDebug = () =>
|
|
56
|
+
activeConfig?.applyPatchDebug ?? DEFAULT_CONFIG.applyPatchDebug;
|
|
54
57
|
|
|
55
58
|
pi.on("session_start", (event, ctx) => {
|
|
56
59
|
activeConfig = loadConfig(ctx.cwd, ctx.isProjectTrusted());
|
|
@@ -60,7 +63,7 @@ export default function registerOpenAICodexCompat(pi: ExtensionAPI): void {
|
|
|
60
63
|
}
|
|
61
64
|
});
|
|
62
65
|
|
|
63
|
-
registerCodexTools(pi, resolveConfig, resolveToolBackground);
|
|
66
|
+
registerCodexTools(pi, resolveConfig, resolveToolBackground, resolveApplyPatchDebug);
|
|
64
67
|
registerCodexThreadLineage(pi);
|
|
65
68
|
const codexProvider = registerCodexProvider(pi, resolveConfig);
|
|
66
69
|
registerCodexRequestOptions(pi, resolveConfig);
|
|
@@ -188,6 +188,10 @@ export default function registerRemoteCompaction(
|
|
|
188
188
|
template,
|
|
189
189
|
priority: config.fastMode,
|
|
190
190
|
compactionMetadata: compactionMetadata(event.reason),
|
|
191
|
+
compactionDecision: {
|
|
192
|
+
reason: event.reason,
|
|
193
|
+
willRetry: event.willRetry,
|
|
194
|
+
},
|
|
191
195
|
});
|
|
192
196
|
|
|
193
197
|
return {
|
|
@@ -34,6 +34,7 @@ type SettingId =
|
|
|
34
34
|
| "reasoningMode"
|
|
35
35
|
| "toolBackground"
|
|
36
36
|
| "applyPatch"
|
|
37
|
+
| "applyPatchDebug"
|
|
37
38
|
| "imageGeneration"
|
|
38
39
|
| "imageDetail"
|
|
39
40
|
| "webRun"
|
|
@@ -108,6 +109,13 @@ export function settingItems(
|
|
|
108
109
|
currentValue: toggleValue(config.applyPatch),
|
|
109
110
|
values: ["off", "on"],
|
|
110
111
|
},
|
|
112
|
+
{
|
|
113
|
+
id: "applyPatchDebug",
|
|
114
|
+
label: "apply_patch debug output",
|
|
115
|
+
description: "Show exact model feedback while apply_patch output is collapsed.",
|
|
116
|
+
currentValue: toggleValue(config.applyPatchDebug),
|
|
117
|
+
values: ["off", "on"],
|
|
118
|
+
},
|
|
111
119
|
{
|
|
112
120
|
id: "imageGeneration",
|
|
113
121
|
label: "image_gen.imagegen tool",
|
|
@@ -185,6 +193,8 @@ export function settingPatch(id: string, value: string): ConfigLayer | undefined
|
|
|
185
193
|
return undefined;
|
|
186
194
|
case "applyPatch":
|
|
187
195
|
return value === "on" || value === "off" ? { applyPatch: value === "on" } : undefined;
|
|
196
|
+
case "applyPatchDebug":
|
|
197
|
+
return value === "on" || value === "off" ? { applyPatchDebug: value === "on" } : undefined;
|
|
188
198
|
case "imageGeneration":
|
|
189
199
|
return value === "on" || value === "off" ? { imageGeneration: value === "on" } : undefined;
|
|
190
200
|
case "imageDetail":
|
|
@@ -214,6 +224,7 @@ function applySettingPatch(config: CodexCompatConfig, patch: ConfigLayer): Codex
|
|
|
214
224
|
if (typeof patch.fastMode === "boolean") next.fastMode = patch.fastMode;
|
|
215
225
|
if (typeof patch.responsesLite === "boolean") next.responsesLite = patch.responsesLite;
|
|
216
226
|
if (typeof patch.applyPatch === "boolean") next.applyPatch = patch.applyPatch;
|
|
227
|
+
if (typeof patch.applyPatchDebug === "boolean") next.applyPatchDebug = patch.applyPatchDebug;
|
|
217
228
|
if (patch.toolBackground) next.toolBackground = patch.toolBackground;
|
|
218
229
|
if (typeof patch.imageGeneration === "boolean") {
|
|
219
230
|
next.imageGeneration = patch.imageGeneration;
|
|
@@ -63,8 +63,9 @@ export default function registerCodexTools(
|
|
|
63
63
|
pi: ExtensionAPI,
|
|
64
64
|
resolveConfig: ConfigResolver,
|
|
65
65
|
resolveToolBackground: CodexToolBackgroundResolver = () => DEFAULT_CONFIG.toolBackground,
|
|
66
|
+
resolveApplyPatchDebug: () => boolean = () => DEFAULT_CONFIG.applyPatchDebug,
|
|
66
67
|
): void {
|
|
67
|
-
registerApplyPatch(pi, resolveToolBackground);
|
|
68
|
+
registerApplyPatch(pi, resolveToolBackground, resolveApplyPatchDebug);
|
|
68
69
|
registerImageGeneration(pi, resolveConfig, resolveToolBackground);
|
|
69
70
|
registerWebRun(pi, resolveConfig, resolveToolBackground);
|
|
70
71
|
}
|
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.8",
|
|
4
4
|
"description": "OpenAI Codex compatibility for Pi with native compaction, fast mode, and Codex-optimized capabilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"extensions",
|
|
24
|
+
"APPLY_PATCH_INSTRUCTION_FEEDBACK.md",
|
|
24
25
|
"README.md",
|
|
25
26
|
"CHANGELOG.md",
|
|
26
27
|
"LICENSE",
|
|
@@ -38,15 +39,19 @@
|
|
|
38
39
|
"lint": "oxlint",
|
|
39
40
|
"lint:fix": "oxlint --fix"
|
|
40
41
|
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@2h2d/tree-sitter-wasms": "0.2.1",
|
|
44
|
+
"web-tree-sitter": "0.26.11"
|
|
45
|
+
},
|
|
41
46
|
"devDependencies": {
|
|
42
47
|
"@earendil-works/pi-ai": "0.84.1",
|
|
43
48
|
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
44
49
|
"@earendil-works/pi-tui": "0.84.1",
|
|
45
50
|
"@types/node": "22.20.1",
|
|
46
|
-
"oxfmt": "0.
|
|
47
|
-
"oxlint": "1.
|
|
51
|
+
"oxfmt": "0.62.0",
|
|
52
|
+
"oxlint": "1.77.0",
|
|
48
53
|
"oxlint-tsgolint": "7.0.2001",
|
|
49
|
-
"typebox": "1.3.
|
|
54
|
+
"typebox": "1.3.11",
|
|
50
55
|
"typescript": "7.0.2"
|
|
51
56
|
},
|
|
52
57
|
"peerDependencies": {
|