@howaboua/pi-codex-conversion 1.5.10-dev.39.e39a154 → 1.5.10-dev.41.7e2d8d2
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/README.md +3 -1
- package/package.json +1 -1
- package/src/adapter/compaction.ts +79 -17
- package/src/adapter/provider-request.ts +3 -1
- package/src/adapter/state.ts +11 -0
- package/src/index.ts +1 -0
- package/vendor/apply-patch/win32-arm64/apply_patch.exe +0 -0
- package/vendor/apply-patch/win32-x64/apply_patch.exe +0 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ GPT/Codex models are strongest when the tool surface looks like the Codex CLI th
|
|
|
9
9
|
|
|
10
10
|
The point is to give the model tools it already knows how to use well: shell-first inspection, resumable command sessions, and large one-shot patch edits instead of piecemeal read/edit/write steps.
|
|
11
11
|
|
|
12
|
-
You can also opt into using the adapter on every provider/model. YMMV: Codex-tuned models are still the best fit, but the shell/patch workflow can help elsewhere too. The extension also has a small `/codex` settings UI for toggling adapter behavior, web search, image generation, fast mode, and verbosity. See [Settings](#settings).
|
|
12
|
+
You can also opt into using the adapter on every provider/model. YMMV: Codex-tuned models are still the best fit, but the shell/patch workflow can help elsewhere too. The extension also has a small `/codex` settings UI for toggling adapter behavior, web search, image generation, fast mode, native Responses compaction, and verbosity. See [Settings](#settings).
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -55,6 +55,8 @@ The settings UI also has **Usage**, **Overrides**, and **About** tabs. **Usage**
|
|
|
55
55
|
|
|
56
56
|
- add only the Pi `apply_patch` tool for GPT/Codex models while keeping Pi's default toolkit, prompt, provider behavior, and compaction flow
|
|
57
57
|
|
|
58
|
+
The **Compaction** tab can enable native OpenAI Responses compaction and choose the compaction model/reasoning. If native compaction fails, the extension falls back to Pi's normal compaction flow; when an older native compacted window exists, it is included in that Pi fallback summarization request so OpenAI can still use the prior opaque context server-side.
|
|
59
|
+
|
|
58
60
|
When `all` is on, non-Codex providers get the shell, patch, skill, and prompt-adapter behavior, but keep their normal Pi provider path. Native web search, native image generation, and priority service tier stay limited to the OpenAI Codex provider. Verbosity is applied to Responses API providers.
|
|
59
61
|
|
|
60
62
|
The footer shows the active state, for example:
|
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@ import type { ExtensionAPI, ExtensionContext, SessionBeforeCompactEvent } from "
|
|
|
2
2
|
import { clampThinkingLevel, type ModelThinkingLevel, type Tool } from "@earendil-works/pi-ai";
|
|
3
3
|
import { executeNativeCompaction } from "./compact-client.ts";
|
|
4
4
|
import { extractCompactionSummaryText, hasCompactionOutputItem, sanitizeCompactedWindow, summarizeCompactionOutputForDiagnostics } from "./compaction-output.ts";
|
|
5
|
-
import { resolveLatestNativeCompactionEntry } from "./details-store.ts";
|
|
5
|
+
import { findLatestNativeCompactionEntry, findLatestNativeCompactionEntryIndex, resolveLatestNativeCompactionEntry } from "./details-store.ts";
|
|
6
6
|
import { rewriteResponsesPayloadWithNativeReplay, serializeLiveTailToResponsesInput } from "./payload-rewrite.ts";
|
|
7
|
-
import { resolveNativeCompactionEnvironment } from "./compaction-runtime.ts";
|
|
7
|
+
import { isResponsesCompatiblePayload, resolveNativeCompactionEnvironment, type ResponsesCompatibleRequestPayload } from "./compaction-runtime.ts";
|
|
8
8
|
import { convertResponsesTools } from "../providers/openai-responses-shared.ts";
|
|
9
9
|
import {
|
|
10
10
|
serializeCompactionPreparationToRequest,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type NativeCompactionRequestOptions,
|
|
13
13
|
type ResponsesInputItem,
|
|
14
14
|
} from "./serializer.ts";
|
|
15
|
-
import { createNativeCompactionDetails, createNativeCompactionShimResult, isNativeCompactionDetails, NATIVE_COMPACTION_SHIM_SUMMARY } from "./types.ts";
|
|
15
|
+
import { createNativeCompactionDetails, createNativeCompactionShimResult, isNativeCompactionDetails, NATIVE_COMPACTION_SHIM_SUMMARY, type NativeCompactionEntry } from "./types.ts";
|
|
16
16
|
import { isOpenAICodexContext, isResponsesContext } from "./codex-model.ts";
|
|
17
17
|
import { shouldUseCodexAdapter } from "./activation.ts";
|
|
18
18
|
import type { AdapterState } from "./state.ts";
|
|
@@ -23,6 +23,31 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
23
23
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function stashLatestNativeWindowForPiCompactionFallback(
|
|
27
|
+
ctx: ExtensionContext,
|
|
28
|
+
branchEntries: ReturnType<ExtensionContext["sessionManager"]["getBranch"]>,
|
|
29
|
+
runtime: { provider: string; api: string; baseUrl: string },
|
|
30
|
+
state: AdapterState,
|
|
31
|
+
): boolean {
|
|
32
|
+
state.pendingPiCompactionNativeWindow = undefined;
|
|
33
|
+
const nativeEntry = findLatestNativeCompactionEntry(branchEntries, {
|
|
34
|
+
provider: runtime.provider,
|
|
35
|
+
api: runtime.api,
|
|
36
|
+
baseUrl: runtime.baseUrl,
|
|
37
|
+
});
|
|
38
|
+
const compactedWindow = cloneCompactedWindow(nativeEntry?.details?.compactedWindow ?? []);
|
|
39
|
+
if (!compactedWindow || compactedWindow.length === 0) return false;
|
|
40
|
+
state.pendingPiCompactionNativeWindow = {
|
|
41
|
+
window: compactedWindow,
|
|
42
|
+
provider: runtime.provider,
|
|
43
|
+
api: runtime.api,
|
|
44
|
+
baseUrl: runtime.baseUrl,
|
|
45
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
46
|
+
sourceCompactionEntryId: nativeEntry?.id,
|
|
47
|
+
};
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
26
51
|
function cloneCompactedWindow(window: readonly unknown[]): ResponsesInputItem[] | undefined {
|
|
27
52
|
if (!window.every(isRecord)) return undefined;
|
|
28
53
|
return window.map((item) => structuredClone(item));
|
|
@@ -94,7 +119,7 @@ function formatCompactFailureMessage(compactResult: Awaited<ReturnType<typeof ex
|
|
|
94
119
|
const status = compactResult.status ? ` HTTP ${compactResult.status}` : "";
|
|
95
120
|
const response = compactResult.responseText?.trim();
|
|
96
121
|
const detail = response ? `: ${response.slice(0, 500)}` : compactResult.errorMessage ? `: ${compactResult.errorMessage}` : "";
|
|
97
|
-
return `OpenAI native compaction failed (${compactResult.reason}${status})${detail}
|
|
122
|
+
return `OpenAI native compaction failed (${compactResult.reason}${status})${detail}`;
|
|
98
123
|
}
|
|
99
124
|
|
|
100
125
|
function formatCompactRequestDiagnostics(request: NativeCompactionRequestBody): string {
|
|
@@ -104,6 +129,11 @@ function formatCompactRequestDiagnostics(request: NativeCompactionRequestBody):
|
|
|
104
129
|
return `model=${request.model}, input=${request.input.length}, tools=${tools}, reasoning=${reasoning}, service_tier=${serviceTier}`;
|
|
105
130
|
}
|
|
106
131
|
|
|
132
|
+
function notifyNativeCompactionFallback(ctx: ExtensionContext, state: AdapterState, branchEntries: ReturnType<ExtensionContext["sessionManager"]["getBranch"]>, runtime: { provider: string; api: string; baseUrl: string }, message: string): void {
|
|
133
|
+
const stashed = stashLatestNativeWindowForPiCompactionFallback(ctx, branchEntries, runtime, state);
|
|
134
|
+
ctx.ui.notify(`${message}; Pi compaction will run.${stashed ? " Previous native compacted window will be included in Pi compaction fallback." : ""}`, "error");
|
|
135
|
+
}
|
|
136
|
+
|
|
107
137
|
export async function handleCodexSessionBeforeCompact(event: SessionBeforeCompactEvent, ctx: ExtensionContext, state: AdapterState, pi: ExtensionAPI) {
|
|
108
138
|
if (!state.config.responsesCompaction || !shouldUseCodexAdapter(ctx, state.config)) {
|
|
109
139
|
return undefined;
|
|
@@ -203,23 +233,23 @@ async function handleCodexSessionBeforeCompactInner(event: SessionBeforeCompactE
|
|
|
203
233
|
const compactResult = await executeNativeCompaction({ runtime, request, signal: event.signal });
|
|
204
234
|
if (!compactResult.ok) {
|
|
205
235
|
if (compactResult.reason !== "aborted") {
|
|
206
|
-
ctx
|
|
236
|
+
notifyNativeCompactionFallback(ctx, state, branchEntries, runtime, formatCompactFailureMessage(compactResult));
|
|
207
237
|
}
|
|
208
|
-
return { cancel: true };
|
|
238
|
+
return compactResult.reason === "aborted" ? { cancel: true } : undefined;
|
|
209
239
|
}
|
|
210
240
|
const compactedWindow = sanitizeCompactedWindow(compactResult.compactedWindow);
|
|
211
241
|
if (compactedWindow.length === 0) {
|
|
212
|
-
ctx
|
|
213
|
-
return
|
|
242
|
+
notifyNativeCompactionFallback(ctx, state, branchEntries, runtime, `OpenAI native compaction returned no installable compacted context. Request: ${formatCompactRequestDiagnostics(request)}. Output: ${summarizeCompactionOutputForDiagnostics(compactResult.compactedWindow, compactedWindow)}`);
|
|
243
|
+
return undefined;
|
|
214
244
|
}
|
|
215
245
|
if (!hasCompactionOutputItem(compactedWindow)) {
|
|
216
|
-
ctx
|
|
217
|
-
return
|
|
246
|
+
notifyNativeCompactionFallback(ctx, state, branchEntries, runtime, `OpenAI native compaction did not return a compaction item. Response=${compactResult.compactResponseId ?? "<none>"}. Request: ${formatCompactRequestDiagnostics(request)}. Output: ${summarizeCompactionOutputForDiagnostics(compactResult.compactedWindow, compactedWindow)}`);
|
|
247
|
+
return undefined;
|
|
218
248
|
}
|
|
219
249
|
const encryptedSummary = extractCompactionSummaryText(compactedWindow);
|
|
220
250
|
if (!encryptedSummary) {
|
|
221
|
-
ctx
|
|
222
|
-
return
|
|
251
|
+
notifyNativeCompactionFallback(ctx, state, branchEntries, runtime, `OpenAI native compaction returned compacted context without a displayable summary. Response=${compactResult.compactResponseId ?? "<none>"}. Request: ${formatCompactRequestDiagnostics(request)}. Output: ${summarizeCompactionOutputForDiagnostics(compactResult.compactedWindow, compactedWindow)}`);
|
|
252
|
+
return undefined;
|
|
223
253
|
}
|
|
224
254
|
try {
|
|
225
255
|
const details = createNativeCompactionDetails({
|
|
@@ -234,8 +264,8 @@ async function handleCodexSessionBeforeCompactInner(event: SessionBeforeCompactE
|
|
|
234
264
|
});
|
|
235
265
|
return { compaction: createNativeCompactionShimResult({ summary: NATIVE_COMPACTION_SHIM_SUMMARY, firstKeptEntryId: event.preparation.firstKeptEntryId, tokensBefore: event.preparation.tokensBefore, details }) };
|
|
236
266
|
} catch {
|
|
237
|
-
ctx
|
|
238
|
-
return
|
|
267
|
+
notifyNativeCompactionFallback(ctx, state, branchEntries, runtime, "OpenAI native compaction produced details Pi could not store");
|
|
268
|
+
return undefined;
|
|
239
269
|
}
|
|
240
270
|
}
|
|
241
271
|
|
|
@@ -245,17 +275,49 @@ export async function rewriteCodexCompactedProviderRequest(payload: unknown, ctx
|
|
|
245
275
|
if (!resolution.ok) return undefined;
|
|
246
276
|
const runtime = resolution.runtime;
|
|
247
277
|
const branchEntries = ctx.sessionManager.getBranch();
|
|
248
|
-
const
|
|
278
|
+
const latestNativeCompactionIndex = findLatestNativeCompactionEntryIndex(branchEntries, {
|
|
249
279
|
provider: runtime.provider,
|
|
250
280
|
api: runtime.api,
|
|
251
281
|
baseUrl: runtime.baseUrl,
|
|
252
282
|
});
|
|
253
|
-
if (
|
|
283
|
+
if (latestNativeCompactionIndex === undefined) return undefined;
|
|
254
284
|
if (!runtime.payload) return undefined;
|
|
255
|
-
const rewrite = rewriteResponsesPayloadWithNativeReplay({ model: runtime.currentModel, payload: runtime.payload, branchEntries, compactionEntry:
|
|
285
|
+
const rewrite = rewriteResponsesPayloadWithNativeReplay({ model: runtime.currentModel, payload: runtime.payload, branchEntries, compactionEntry: branchEntries[latestNativeCompactionIndex] as NativeCompactionEntry });
|
|
256
286
|
if (rewrite.ok) return rewrite.rewrittenPayload;
|
|
257
287
|
const detail = rewrite.parity?.mismatches.slice(0, 3).join("; ");
|
|
258
288
|
const message = `OpenAI native compaction replay failed (${rewrite.reason})${detail ? `: ${detail}` : ""}; request was not sent with placeholder compaction context.`;
|
|
259
289
|
ctx.ui.notify(message, "error");
|
|
260
290
|
throw new Error(message);
|
|
261
291
|
}
|
|
292
|
+
|
|
293
|
+
export async function injectPendingNativeWindowIntoPiCompactionRequest(payload: unknown, ctx: ExtensionContext, state: AdapterState): Promise<unknown | undefined> {
|
|
294
|
+
const pending = state.pendingPiCompactionNativeWindow;
|
|
295
|
+
if (!pending || pending.window.length === 0) return undefined;
|
|
296
|
+
if (!isResponsesCompatiblePayload(payload)) return undefined;
|
|
297
|
+
if (pending.sessionId !== ctx.sessionManager.getSessionId()) {
|
|
298
|
+
state.pendingPiCompactionNativeWindow = undefined;
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const resolution = await resolveNativeCompactionEnvironment(ctx, { enabled: true }, payload);
|
|
303
|
+
if (!resolution.ok) return undefined;
|
|
304
|
+
const runtime = resolution.runtime;
|
|
305
|
+
if (pending.provider !== runtime.provider || pending.api !== runtime.api || pending.baseUrl !== runtime.baseUrl) return undefined;
|
|
306
|
+
|
|
307
|
+
const input = [...payload.input];
|
|
308
|
+
let insertAt = 0;
|
|
309
|
+
while (insertAt < input.length) {
|
|
310
|
+
const item = input[insertAt];
|
|
311
|
+
if (!isRecord(item) || (item.role !== "system" && item.role !== "developer")) break;
|
|
312
|
+
insertAt++;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
...payload,
|
|
317
|
+
input: [
|
|
318
|
+
...input.slice(0, insertAt),
|
|
319
|
+
...pending.window.map((item) => structuredClone(item)),
|
|
320
|
+
...input.slice(insertAt),
|
|
321
|
+
],
|
|
322
|
+
};
|
|
323
|
+
}
|
|
@@ -5,7 +5,7 @@ import type { AdapterState } from "./state.ts";
|
|
|
5
5
|
import { rewriteNativeImageGenerationTool } from "../tools/image-generation-tool.ts";
|
|
6
6
|
import { rewriteNativeWebSearchTool } from "../tools/web-search-tool.ts";
|
|
7
7
|
import { shouldUseCodexAdapter } from "./activation.ts";
|
|
8
|
-
import { rewriteCodexCompactedProviderRequest } from "./compaction.ts";
|
|
8
|
+
import { injectPendingNativeWindowIntoPiCompactionRequest, rewriteCodexCompactedProviderRequest } from "./compaction.ts";
|
|
9
9
|
|
|
10
10
|
export async function rewriteCodexProviderRequest(payload: unknown, ctx: ExtensionContext, state: AdapterState): Promise<unknown | undefined> {
|
|
11
11
|
if (!shouldUseCodexAdapter(ctx, state.config) || (!isOpenAICodexContext(ctx) && !isResponsesContext(ctx))) {
|
|
@@ -21,5 +21,7 @@ export async function rewriteCodexProviderRequest(payload: unknown, ctx: Extensi
|
|
|
21
21
|
serviceTier: isOpenAICodex,
|
|
22
22
|
verbosity: true,
|
|
23
23
|
});
|
|
24
|
+
const piCompactionPayload = await injectPendingNativeWindowIntoPiCompactionRequest(configuredPayload, ctx, state);
|
|
25
|
+
if (piCompactionPayload !== undefined) return piCompactionPayload;
|
|
24
26
|
return (await rewriteCodexCompactedProviderRequest(configuredPayload, ctx, state)) ?? configuredPayload;
|
|
25
27
|
}
|
package/src/adapter/state.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { PromptSkill } from "../prompt/build-system-prompt.ts";
|
|
2
2
|
import type { CodexConversionConfig } from "./config.ts";
|
|
3
|
+
import type { ResponsesInputItem } from "./serializer.ts";
|
|
4
|
+
|
|
5
|
+
export interface PendingPiCompactionNativeWindow {
|
|
6
|
+
window: ResponsesInputItem[];
|
|
7
|
+
provider: string;
|
|
8
|
+
api: string;
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
sourceCompactionEntryId?: string;
|
|
12
|
+
}
|
|
3
13
|
|
|
4
14
|
export interface AdapterState {
|
|
5
15
|
enabled: boolean;
|
|
@@ -8,4 +18,5 @@ export interface AdapterState {
|
|
|
8
18
|
previousToolNames?: string[];
|
|
9
19
|
promptSkills: PromptSkill[];
|
|
10
20
|
config: CodexConversionConfig;
|
|
21
|
+
pendingPiCompactionNativeWindow?: PendingPiCompactionNativeWindow;
|
|
11
22
|
}
|
package/src/index.ts
CHANGED
|
@@ -163,6 +163,7 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
163
163
|
});
|
|
164
164
|
|
|
165
165
|
pi.on("session_compact", async (event) => {
|
|
166
|
+
state.pendingPiCompactionNativeWindow = undefined;
|
|
166
167
|
if (!event.fromExtension || !isNativeCompactionDetails(event.compactionEntry.details)) return;
|
|
167
168
|
pi.sendMessage(
|
|
168
169
|
{
|
|
Binary file
|
|
Binary file
|