@howaboua/pi-codex-conversion 1.5.5-dev.22.90056d4 → 1.5.5-dev.25.f80a775
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/package.json +1 -1
- package/src/adapter/activation.ts +1 -0
- package/src/adapter/compact-client.ts +257 -0
- package/src/adapter/compaction-output.ts +80 -0
- package/src/adapter/compaction-runtime.ts +272 -0
- package/src/adapter/compaction.ts +261 -0
- package/src/adapter/config.ts +24 -0
- package/src/adapter/context-filter.ts +20 -0
- package/src/adapter/details-store.ts +151 -0
- package/src/adapter/payload-rewrite.ts +550 -0
- package/src/adapter/provider-request.ts +4 -2
- package/src/adapter/serializer.ts +288 -0
- package/src/adapter/tool-set.ts +2 -1
- package/src/adapter/types.ts +220 -0
- package/src/codex-settings/command.ts +16 -3
- package/src/codex-settings/ui.ts +126 -48
- package/src/index.ts +34 -18
- package/src/providers/openai-codex-custom-provider.ts +1 -1
- package/src/providers/openai-responses-shared.ts +2 -0
- package/vendor/apply-patch/win32-arm64/apply_patch.exe +0 -0
- package/vendor/apply-patch/win32-x64/apply_patch.exe +0 -0
package/src/codex-settings/ui.ts
CHANGED
|
@@ -1,67 +1,64 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { getSettingsListTheme, type ExtensionContext, type Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { SettingsList, truncateToWidth, type SettingItem } from "@earendil-works/pi-tui";
|
|
3
|
+
import {
|
|
4
|
+
COMPACTION_MODELS,
|
|
5
|
+
COMPACTION_REASONING_LEVELS,
|
|
6
|
+
DEFAULT_CODEX_CONVERSION_CONFIG,
|
|
7
|
+
normalizeCodexVerbosity,
|
|
8
|
+
normalizeCompactionModel,
|
|
9
|
+
normalizeCompactionReasoning,
|
|
10
|
+
type CodexConversionConfig,
|
|
11
|
+
} from "../adapter/config.ts";
|
|
4
12
|
import { CHANGELOG_URL, DISCORD_URL, GITHUB_URL, ISSUE_URL, openExternalUrl } from "./links.ts";
|
|
5
13
|
|
|
6
14
|
export interface CodexSettingsScreenOptions {
|
|
7
15
|
initialConfig: CodexConversionConfig;
|
|
8
16
|
onChange: (nextConfig: CodexConversionConfig) => boolean;
|
|
17
|
+
initialTab?: SettingsTab;
|
|
9
18
|
}
|
|
10
19
|
|
|
20
|
+
type SettingsTab = "general" | "compaction";
|
|
21
|
+
|
|
22
|
+
const TAB_ORDER: readonly SettingsTab[] = ["general", "compaction"];
|
|
23
|
+
|
|
11
24
|
export async function openCodexSettingsScreen(ctx: ExtensionContext, options: CodexSettingsScreenOptions): Promise<void> {
|
|
12
25
|
let draft = { ...options.initialConfig };
|
|
26
|
+
let activeTab: SettingsTab = options.initialTab ?? "general";
|
|
27
|
+
|
|
13
28
|
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ id: "fast", label: "Fast mode", currentValue: draft.fast ? "on" : "off", values: ["off", "on"] },
|
|
18
|
-
{ id: "webSearch", label: "Web search", currentValue: draft.webSearch ? "on" : "off", values: ["off", "on"] },
|
|
19
|
-
{ id: "imageGeneration", label: "Image generation", currentValue: draft.imageGeneration ? "on" : "off", values: ["off", "on"] },
|
|
20
|
-
{ id: "verbosity", label: "Verbosity", currentValue: draft.verbosity, values: ["low", "medium", "high"] },
|
|
21
|
-
];
|
|
29
|
+
let settingsList = createSettingsList(activeTab, draft, options, (nextDraft) => {
|
|
30
|
+
draft = nextDraft;
|
|
31
|
+
}, done, () => tui.requestRender());
|
|
22
32
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
settingsList = new SettingsList(buildItems(), 6, getSettingsListTheme(), (id, value) => {
|
|
28
|
-
const nextDraft = { ...draft };
|
|
29
|
-
const previousValue = buildItems().find((item) => item.id === id)?.currentValue;
|
|
30
|
-
if (id === "useOnAllModels") nextDraft.useOnAllModels = value === "on";
|
|
31
|
-
if (id === "statusLine") nextDraft.statusLine = value === "on";
|
|
32
|
-
if (id === "fast") nextDraft.fast = value === "on";
|
|
33
|
-
if (id === "webSearch") nextDraft.webSearch = value === "on";
|
|
34
|
-
if (id === "imageGeneration") nextDraft.imageGeneration = value === "on";
|
|
35
|
-
if (id === "verbosity") nextDraft.verbosity = normalizeCodexVerbosity(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.verbosity;
|
|
36
|
-
if (options.onChange(nextDraft)) {
|
|
33
|
+
const switchTab = () => {
|
|
34
|
+
const currentIndex = TAB_ORDER.indexOf(activeTab);
|
|
35
|
+
activeTab = TAB_ORDER[(currentIndex + 1) % TAB_ORDER.length] ?? "general";
|
|
36
|
+
settingsList = createSettingsList(activeTab, draft, options, (nextDraft) => {
|
|
37
37
|
draft = nextDraft;
|
|
38
|
-
}
|
|
39
|
-
settingsList.updateValue(id, previousValue);
|
|
40
|
-
}
|
|
38
|
+
}, done, () => tui.requestRender());
|
|
41
39
|
tui.requestRender();
|
|
42
|
-
}
|
|
43
|
-
panel.addChild(settingsList);
|
|
44
|
-
panel.addChild(new DynamicBorder((text) => theme.fg("dim", text)));
|
|
45
|
-
panel.addChild(
|
|
46
|
-
new Text(
|
|
47
|
-
[
|
|
48
|
-
`${theme.bold("g")} github ${theme.fg("dim", GITHUB_URL)}`,
|
|
49
|
-
`${theme.bold("c")} changes ${theme.fg("dim", CHANGELOG_URL)}`,
|
|
50
|
-
`${theme.bold("d")} discord ${theme.fg("dim", DISCORD_URL)}`,
|
|
51
|
-
`${theme.bold("i")} issue ${theme.fg("dim", ISSUE_URL)}`,
|
|
52
|
-
].join("\n"),
|
|
53
|
-
0,
|
|
54
|
-
0,
|
|
55
|
-
),
|
|
56
|
-
);
|
|
57
|
-
panel.addChild(new DynamicBorder((text) => theme.fg("accent", text)));
|
|
58
|
-
container.addChild(new Spacer(1));
|
|
59
|
-
container.addChild(panel);
|
|
40
|
+
};
|
|
60
41
|
|
|
61
42
|
return {
|
|
62
|
-
render: (width: number) =>
|
|
63
|
-
|
|
43
|
+
render: (width: number) =>
|
|
44
|
+
[
|
|
45
|
+
rule(width, theme, "accent"),
|
|
46
|
+
formatTabs(activeTab, theme),
|
|
47
|
+
rule(width, theme, "borderMuted"),
|
|
48
|
+
...(activeTab === "compaction" ? formatCompactionNotes(theme) : []),
|
|
49
|
+
"",
|
|
50
|
+
...settingsList.render(width),
|
|
51
|
+
rule(width, theme, "borderMuted"),
|
|
52
|
+
...formatLinks(theme),
|
|
53
|
+
rule(width, theme, "accent"),
|
|
54
|
+
theme.fg("dim", " Tab to switch sections · g/c/d/i open links"),
|
|
55
|
+
].map((line) => truncateToWidth(line, width, "")),
|
|
56
|
+
invalidate: () => settingsList.invalidate(),
|
|
64
57
|
handleInput: (data: string) => {
|
|
58
|
+
if (data === "\t") {
|
|
59
|
+
switchTab();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
65
62
|
if (handleLinkKey(data, ctx)) return;
|
|
66
63
|
settingsList.handleInput?.(data);
|
|
67
64
|
tui.requestRender();
|
|
@@ -70,6 +67,87 @@ export async function openCodexSettingsScreen(ctx: ExtensionContext, options: Co
|
|
|
70
67
|
});
|
|
71
68
|
}
|
|
72
69
|
|
|
70
|
+
function formatCompactionNotes(theme: Theme): string[] {
|
|
71
|
+
return [
|
|
72
|
+
theme.fg("dim", " Beta: native OpenAI Responses compaction is experimental. Please report any issues."),
|
|
73
|
+
theme.fg("error", " Warning: do not turn this off mid-session; old context may be much less reliable."),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function rule(width: number, theme: Theme, color: "accent" | "borderMuted"): string {
|
|
78
|
+
return theme.fg(color, "─".repeat(Math.max(0, width)));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createSettingsList(
|
|
82
|
+
tab: SettingsTab,
|
|
83
|
+
draft: CodexConversionConfig,
|
|
84
|
+
options: CodexSettingsScreenOptions,
|
|
85
|
+
onDraftChanged: (draft: CodexConversionConfig) => void,
|
|
86
|
+
done: (value?: void) => void,
|
|
87
|
+
requestRender: () => void,
|
|
88
|
+
): SettingsList {
|
|
89
|
+
let settingsList: SettingsList;
|
|
90
|
+
settingsList = new SettingsList(buildItems(tab, draft), 8, getSettingsListTheme(), (id, value) => {
|
|
91
|
+
const nextDraft = applySettingChange(id, value, draft);
|
|
92
|
+
const previousValue = buildItems(tab, draft).find((item) => item.id === id)?.currentValue;
|
|
93
|
+
if (options.onChange(nextDraft)) {
|
|
94
|
+
onDraftChanged(nextDraft);
|
|
95
|
+
draft = nextDraft;
|
|
96
|
+
} else if (previousValue !== undefined) {
|
|
97
|
+
settingsList.updateValue(id, previousValue);
|
|
98
|
+
}
|
|
99
|
+
requestRender();
|
|
100
|
+
}, () => done(undefined));
|
|
101
|
+
return settingsList;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildItems(tab: SettingsTab, draft: CodexConversionConfig): SettingItem[] {
|
|
105
|
+
if (tab === "compaction") {
|
|
106
|
+
return [
|
|
107
|
+
{ id: "responsesCompaction", label: "Responses compaction", currentValue: (draft.responsesCompaction ?? false) ? "on" : "off", values: ["off", "on"] },
|
|
108
|
+
{ id: "compactionModel", label: "Model", currentValue: draft.compactionModel, values: [...COMPACTION_MODELS] },
|
|
109
|
+
{ id: "compactionReasoning", label: "Reasoning", currentValue: draft.compactionReasoning, values: [...COMPACTION_REASONING_LEVELS] },
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return [
|
|
114
|
+
{ id: "useOnAllModels", label: "Use on all models", currentValue: draft.useOnAllModels ? "on" : "off", values: ["off", "on"] },
|
|
115
|
+
{ id: "statusLine", label: "Statusline", currentValue: draft.statusLine ? "on" : "off", values: ["off", "on"] },
|
|
116
|
+
{ id: "fast", label: "Fast mode", currentValue: draft.fast ? "on" : "off", values: ["off", "on"] },
|
|
117
|
+
{ id: "webSearch", label: "Web search", currentValue: draft.webSearch ? "on" : "off", values: ["off", "on"] },
|
|
118
|
+
{ id: "imageGeneration", label: "Image generation", currentValue: draft.imageGeneration ? "on" : "off", values: ["off", "on"] },
|
|
119
|
+
{ id: "verbosity", label: "Verbosity", currentValue: draft.verbosity, values: ["low", "medium", "high"] },
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function applySettingChange(id: string, value: string, draft: CodexConversionConfig): CodexConversionConfig {
|
|
124
|
+
const nextDraft = { ...draft };
|
|
125
|
+
if (id === "useOnAllModels") nextDraft.useOnAllModels = value === "on";
|
|
126
|
+
if (id === "statusLine") nextDraft.statusLine = value === "on";
|
|
127
|
+
if (id === "fast") nextDraft.fast = value === "on";
|
|
128
|
+
if (id === "webSearch") nextDraft.webSearch = value === "on";
|
|
129
|
+
if (id === "imageGeneration") nextDraft.imageGeneration = value === "on";
|
|
130
|
+
if (id === "responsesCompaction") nextDraft.responsesCompaction = value === "on";
|
|
131
|
+
if (id === "compactionModel") nextDraft.compactionModel = normalizeCompactionModel(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.compactionModel;
|
|
132
|
+
if (id === "compactionReasoning") nextDraft.compactionReasoning = normalizeCompactionReasoning(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.compactionReasoning;
|
|
133
|
+
if (id === "verbosity") nextDraft.verbosity = normalizeCodexVerbosity(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.verbosity;
|
|
134
|
+
return nextDraft;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function formatTabs(activeTab: SettingsTab, theme: Theme): string {
|
|
138
|
+
const renderTab = (tab: SettingsTab, label: string) => activeTab === tab ? theme.bold(label) : theme.fg("dim", label);
|
|
139
|
+
return ` ${renderTab("general", "General")} ${theme.fg("dim", "/")} ${renderTab("compaction", "Compaction")}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function formatLinks(theme: Theme): string[] {
|
|
143
|
+
return [
|
|
144
|
+
`${theme.bold("g")} github ${theme.fg("dim", GITHUB_URL)}`,
|
|
145
|
+
`${theme.bold("c")} changes ${theme.fg("dim", CHANGELOG_URL)}`,
|
|
146
|
+
`${theme.bold("d")} discord ${theme.fg("dim", DISCORD_URL)}`,
|
|
147
|
+
`${theme.bold("i")} issue ${theme.fg("dim", ISSUE_URL)}`,
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
|
|
73
151
|
function handleLinkKey(data: string, ctx: ExtensionContext): boolean {
|
|
74
152
|
const target = getLinkTarget(data);
|
|
75
153
|
if (!target) return false;
|
package/src/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Box, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
3
|
import { getCodexRuntimeShell } from "./adapter/runtime-shell.ts";
|
|
3
4
|
import { clearApplyPatchRenderState, registerApplyPatchTool } from "./tools/apply-patch-tool.ts";
|
|
4
5
|
import { createExecCommandTracker } from "./tools/exec-command-state.ts";
|
|
5
6
|
import { registerExecCommandTool } from "./tools/exec-command-tool.ts";
|
|
6
7
|
import { createExecSessionManager } from "./tools/exec-session-manager.ts";
|
|
7
|
-
import {
|
|
8
|
-
IMAGE_SAVE_DISPLAY_MESSAGE_TYPE,
|
|
9
|
-
WEB_SEARCH_ACTIVITY_MESSAGE_TYPE,
|
|
10
|
-
registerOpenAICodexCustomProvider,
|
|
11
|
-
} from "./providers/openai-codex-custom-provider.ts";
|
|
8
|
+
import { registerOpenAICodexCustomProvider } from "./providers/openai-codex-custom-provider.ts";
|
|
12
9
|
import { registerImageGenerationTool } from "./tools/image-generation-tool.ts";
|
|
13
10
|
import { buildCodexSystemPrompt, extractPiPromptSkills, resolvePromptSkills } from "./prompt/build-system-prompt.ts";
|
|
14
11
|
import { registerViewImageTool, supportsOriginalImageDetail } from "./tools/view-image-tool.ts";
|
|
15
|
-
import { registerWebSearchTool
|
|
12
|
+
import { registerWebSearchTool } from "./tools/web-search-tool.ts";
|
|
16
13
|
import { registerWriteStdinTool } from "./tools/write-stdin-tool.ts";
|
|
17
14
|
import { ensureBundledApplyPatchOnPath } from "./tools/apply-patch-binary.ts";
|
|
18
15
|
import { readCodexConversionConfig } from "./adapter/config.ts";
|
|
19
16
|
import { syncAdapter, mergeAdapterTools, restoreTools, stripAdapterTools, shouldUseCodexAdapter } from "./adapter/activation.ts";
|
|
20
17
|
import { rewriteCodexProviderRequest } from "./adapter/provider-request.ts";
|
|
18
|
+
import { handleCodexSessionBeforeCompact } from "./adapter/compaction.ts";
|
|
19
|
+
import { isNativeCompactionDetails, NATIVE_COMPACTION_DISPLAY_MESSAGE_TYPE, NATIVE_COMPACTION_DISPLAY_TEXT } from "./adapter/types.ts";
|
|
20
|
+
import { isAdapterContextExcludedCustomMessage } from "./adapter/context-filter.ts";
|
|
21
21
|
import { getCodexSkillPaths } from "./adapter/skills.ts";
|
|
22
22
|
import type { AdapterState } from "./adapter/state.ts";
|
|
23
23
|
import { registerCodexCommand } from "./codex-settings/command.ts";
|
|
@@ -67,6 +67,16 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
67
67
|
ensureOptionalNativeToolsRegistered();
|
|
68
68
|
registerCodexCommand(pi, state, ensureOptionalNativeToolsRegistered);
|
|
69
69
|
|
|
70
|
+
pi.registerMessageRenderer(NATIVE_COMPACTION_DISPLAY_MESSAGE_TYPE, (message, _options, theme) => {
|
|
71
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
72
|
+
box.addChild(new Text(theme.fg("customMessageLabel", theme.bold("[compaction]")), 0, 0));
|
|
73
|
+
const content = typeof message.content === "string" ? message.content : NATIVE_COMPACTION_DISPLAY_TEXT;
|
|
74
|
+
box.addChild(new Text(`\n${theme.fg("customMessageText", content)}`, 0, 0));
|
|
75
|
+
const render = box.render.bind(box);
|
|
76
|
+
box.render = (width) => render(width).map((line) => truncateToWidth(line, width, ""));
|
|
77
|
+
return box;
|
|
78
|
+
});
|
|
79
|
+
|
|
70
80
|
sessions.onSessionExit((sessionId) => {
|
|
71
81
|
tracker.recordSessionFinished(sessionId);
|
|
72
82
|
});
|
|
@@ -138,19 +148,25 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
138
148
|
return rewriteCodexProviderRequest(event.payload, ctx, state);
|
|
139
149
|
});
|
|
140
150
|
|
|
141
|
-
pi.on("
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
152
|
+
state.cwd = ctx.cwd;
|
|
153
|
+
return handleCodexSessionBeforeCompact(event, ctx, state, pi);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
pi.on("session_compact", async (event) => {
|
|
157
|
+
if (!event.fromExtension || !isNativeCompactionDetails(event.compactionEntry.details)) return;
|
|
158
|
+
pi.sendMessage(
|
|
159
|
+
{
|
|
160
|
+
customType: NATIVE_COMPACTION_DISPLAY_MESSAGE_TYPE,
|
|
161
|
+
content: NATIVE_COMPACTION_DISPLAY_TEXT,
|
|
162
|
+
display: true,
|
|
163
|
+
details: { compactionEntryId: event.compactionEntry.id },
|
|
164
|
+
},
|
|
165
|
+
{ triggerTurn: false },
|
|
166
|
+
);
|
|
153
167
|
});
|
|
168
|
+
|
|
169
|
+
pi.on("context", async (event) => ({ messages: event.messages.filter((message) => !isAdapterContextExcludedCustomMessage(message)) }));
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
export { getCodexSkillPaths, mergeAdapterTools, restoreTools, stripAdapterTools };
|
|
@@ -17,6 +17,7 @@ import type { ResponseCreateParamsStreaming } from "openai/resources/responses/r
|
|
|
17
17
|
import {
|
|
18
18
|
convertResponsesMessages,
|
|
19
19
|
convertResponsesTools,
|
|
20
|
+
CODEX_TOOL_CALL_PROVIDERS,
|
|
20
21
|
processResponsesStream,
|
|
21
22
|
} from "./openai-responses-shared.ts";
|
|
22
23
|
|
|
@@ -28,7 +29,6 @@ const OPENAI_CODEX_IMAGE_DIR = ".pi/openai-codex-images";
|
|
|
28
29
|
const OPENAI_CODEX_LATEST_IMAGE_NAME = "latest.png";
|
|
29
30
|
const MAX_RETRIES = 3;
|
|
30
31
|
const BASE_DELAY_MS = 1000;
|
|
31
|
-
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
32
32
|
const CODEX_RESPONSE_STATUSES = new Set(["completed", "incomplete", "failed", "cancelled", "queued", "in_progress"]);
|
|
33
33
|
const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
34
34
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
@@ -40,6 +40,8 @@ interface ConvertResponsesToolsOptions {
|
|
|
40
40
|
strict?: boolean | null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
44
|
+
|
|
43
45
|
function shortHash(str: string): string {
|
|
44
46
|
let h1 = 0xdeadbeef;
|
|
45
47
|
let h2 = 0x41c6ce57;
|
|
Binary file
|
|
Binary file
|