@howaboua/pi-codex-conversion 1.5.5-dev.22.90056d4 → 1.5.5-dev.26.0f1b086
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 +6 -2
- package/package.json +1 -1
- package/src/adapter/activation.ts +38 -5
- 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 +27 -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/skills.ts +8 -0
- package/src/adapter/tool-set.ts +7 -3
- package/src/adapter/types.ts +220 -0
- package/src/codex-settings/command.ts +16 -3
- package/src/codex-settings/ui.ts +140 -48
- package/src/index.ts +45 -24
- package/src/providers/openai-codex-custom-provider.ts +3 -2
- package/src/providers/openai-responses-shared.ts +2 -0
- package/src/tools/web-search-tool.ts +22 -9
- 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,65 @@
|
|
|
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" | "overrides";
|
|
21
|
+
|
|
22
|
+
const TAB_ORDER: readonly SettingsTab[] = ["general", "compaction", "overrides"];
|
|
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
|
+
...(activeTab === "overrides" ? formatOverridesNotes(theme) : []),
|
|
50
|
+
"",
|
|
51
|
+
...settingsList.render(width),
|
|
52
|
+
rule(width, theme, "borderMuted"),
|
|
53
|
+
...formatLinks(theme),
|
|
54
|
+
rule(width, theme, "accent"),
|
|
55
|
+
theme.fg("dim", " Tab to switch sections · g/c/d/i open links"),
|
|
56
|
+
].map((line) => truncateToWidth(line, width, "")),
|
|
57
|
+
invalidate: () => settingsList.invalidate(),
|
|
64
58
|
handleInput: (data: string) => {
|
|
59
|
+
if (data === "\t") {
|
|
60
|
+
switchTab();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
65
63
|
if (handleLinkKey(data, ctx)) return;
|
|
66
64
|
settingsList.handleInput?.(data);
|
|
67
65
|
tui.requestRender();
|
|
@@ -70,6 +68,100 @@ export async function openCodexSettingsScreen(ctx: ExtensionContext, options: Co
|
|
|
70
68
|
});
|
|
71
69
|
}
|
|
72
70
|
|
|
71
|
+
function formatCompactionNotes(theme: Theme): string[] {
|
|
72
|
+
return [
|
|
73
|
+
theme.fg("dim", " Beta: native OpenAI Responses compaction is experimental. Please report any issues."),
|
|
74
|
+
theme.fg("error", " Warning: do not turn this off mid-session; old context may be much less reliable."),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatOverridesNotes(theme: Theme): string[] {
|
|
79
|
+
return [
|
|
80
|
+
theme.fg("dim", " Advanced tool-surface overrides."),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function rule(width: number, theme: Theme, color: "accent" | "borderMuted"): string {
|
|
85
|
+
return theme.fg(color, "─".repeat(Math.max(0, width)));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createSettingsList(
|
|
89
|
+
tab: SettingsTab,
|
|
90
|
+
draft: CodexConversionConfig,
|
|
91
|
+
options: CodexSettingsScreenOptions,
|
|
92
|
+
onDraftChanged: (draft: CodexConversionConfig) => void,
|
|
93
|
+
done: (value?: void) => void,
|
|
94
|
+
requestRender: () => void,
|
|
95
|
+
): SettingsList {
|
|
96
|
+
let settingsList: SettingsList;
|
|
97
|
+
settingsList = new SettingsList(buildItems(tab, draft), 8, getSettingsListTheme(), (id, value) => {
|
|
98
|
+
const nextDraft = applySettingChange(id, value, draft);
|
|
99
|
+
const previousValue = buildItems(tab, draft).find((item) => item.id === id)?.currentValue;
|
|
100
|
+
if (options.onChange(nextDraft)) {
|
|
101
|
+
onDraftChanged(nextDraft);
|
|
102
|
+
draft = nextDraft;
|
|
103
|
+
} else if (previousValue !== undefined) {
|
|
104
|
+
settingsList.updateValue(id, previousValue);
|
|
105
|
+
}
|
|
106
|
+
requestRender();
|
|
107
|
+
}, () => done(undefined));
|
|
108
|
+
return settingsList;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildItems(tab: SettingsTab, draft: CodexConversionConfig): SettingItem[] {
|
|
112
|
+
if (tab === "compaction") {
|
|
113
|
+
return [
|
|
114
|
+
{ id: "responsesCompaction", label: "Responses compaction", currentValue: (draft.responsesCompaction ?? false) ? "on" : "off", values: ["off", "on"] },
|
|
115
|
+
{ id: "compactionModel", label: "Model", currentValue: draft.compactionModel, values: [...COMPACTION_MODELS] },
|
|
116
|
+
{ id: "compactionReasoning", label: "Reasoning", currentValue: draft.compactionReasoning, values: [...COMPACTION_REASONING_LEVELS] },
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (tab === "overrides") {
|
|
121
|
+
return [
|
|
122
|
+
{ id: "applyPatchOnly", label: "Apply patch only", currentValue: draft.applyPatchOnly ? "on" : "off", values: ["off", "on"] },
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return [
|
|
127
|
+
{ id: "useOnAllModels", label: "Use on all models", currentValue: draft.useOnAllModels ? "on" : "off", values: ["off", "on"] },
|
|
128
|
+
{ id: "statusLine", label: "Statusline", currentValue: draft.statusLine ? "on" : "off", values: ["off", "on"] },
|
|
129
|
+
{ id: "fast", label: "Fast mode", currentValue: draft.fast ? "on" : "off", values: ["off", "on"] },
|
|
130
|
+
{ id: "webSearch", label: "Web search", currentValue: draft.webSearch ? "on" : "off", values: ["off", "on"] },
|
|
131
|
+
{ id: "imageGeneration", label: "Image generation", currentValue: draft.imageGeneration ? "on" : "off", values: ["off", "on"] },
|
|
132
|
+
{ id: "verbosity", label: "Verbosity", currentValue: draft.verbosity, values: ["low", "medium", "high"] },
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function applySettingChange(id: string, value: string, draft: CodexConversionConfig): CodexConversionConfig {
|
|
137
|
+
const nextDraft = { ...draft };
|
|
138
|
+
if (id === "applyPatchOnly") nextDraft.applyPatchOnly = value === "on";
|
|
139
|
+
if (id === "useOnAllModels") nextDraft.useOnAllModels = value === "on";
|
|
140
|
+
if (id === "statusLine") nextDraft.statusLine = value === "on";
|
|
141
|
+
if (id === "fast") nextDraft.fast = value === "on";
|
|
142
|
+
if (id === "webSearch") nextDraft.webSearch = value === "on";
|
|
143
|
+
if (id === "imageGeneration") nextDraft.imageGeneration = value === "on";
|
|
144
|
+
if (id === "responsesCompaction") nextDraft.responsesCompaction = value === "on";
|
|
145
|
+
if (id === "compactionModel") nextDraft.compactionModel = normalizeCompactionModel(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.compactionModel;
|
|
146
|
+
if (id === "compactionReasoning") nextDraft.compactionReasoning = normalizeCompactionReasoning(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.compactionReasoning;
|
|
147
|
+
if (id === "verbosity") nextDraft.verbosity = normalizeCodexVerbosity(value) ?? DEFAULT_CODEX_CONVERSION_CONFIG.verbosity;
|
|
148
|
+
return nextDraft;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function formatTabs(activeTab: SettingsTab, theme: Theme): string {
|
|
152
|
+
const renderTab = (tab: SettingsTab, label: string) => activeTab === tab ? theme.bold(label) : theme.fg("dim", label);
|
|
153
|
+
return ` ${renderTab("general", "General")} ${theme.fg("dim", "/")} ${renderTab("compaction", "Compaction")} ${theme.fg("dim", "/")} ${renderTab("overrides", "Overrides")}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function formatLinks(theme: Theme): string[] {
|
|
157
|
+
return [
|
|
158
|
+
`${theme.bold("g")} github ${theme.fg("dim", GITHUB_URL)}`,
|
|
159
|
+
`${theme.bold("c")} changes ${theme.fg("dim", CHANGELOG_URL)}`,
|
|
160
|
+
`${theme.bold("d")} discord ${theme.fg("dim", DISCORD_URL)}`,
|
|
161
|
+
`${theme.bold("i")} issue ${theme.fg("dim", ISSUE_URL)}`,
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
73
165
|
function handleLinkKey(data: string, ctx: ExtensionContext): boolean {
|
|
74
166
|
const target = getLinkTarget(data);
|
|
75
167
|
if (!target) return false;
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
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";
|
|
21
|
-
import {
|
|
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
|
+
import { getCodexSkillPaths, hasNoSkillsFlag } from "./adapter/skills.ts";
|
|
22
22
|
import type { AdapterState } from "./adapter/state.ts";
|
|
23
23
|
import { registerCodexCommand } from "./codex-settings/command.ts";
|
|
24
|
+
import { WEB_SEARCH_TOOL_NAME } from "./adapter/tool-set.ts";
|
|
24
25
|
|
|
25
26
|
function getCommandArg(args: unknown): string | undefined {
|
|
26
27
|
if (!args || typeof args !== "object" || !("cmd" in args) || typeof args.cmd !== "string") {
|
|
@@ -44,13 +45,16 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
44
45
|
const tracker = createExecCommandTracker();
|
|
45
46
|
const state: AdapterState = { enabled: false, cwd: process.cwd(), promptSkills: [], config: readCodexConversionConfig() };
|
|
46
47
|
const sessions = createExecSessionManager();
|
|
47
|
-
|
|
48
|
+
const registeredNativeWebSearchTools = new Set<string>();
|
|
48
49
|
let nativeImageGenerationRegistered = false;
|
|
49
50
|
|
|
50
51
|
function ensureOptionalNativeToolsRegistered(config = state.config): void {
|
|
51
|
-
if (config.webSearch
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (config.webSearch) {
|
|
53
|
+
const webSearchToolName = WEB_SEARCH_TOOL_NAME;
|
|
54
|
+
if (!registeredNativeWebSearchTools.has(webSearchToolName)) {
|
|
55
|
+
registerWebSearchTool(pi, webSearchToolName);
|
|
56
|
+
registeredNativeWebSearchTools.add(webSearchToolName);
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
if (config.imageGeneration && !nativeImageGenerationRegistered) {
|
|
56
60
|
registerImageGenerationTool(pi);
|
|
@@ -67,6 +71,16 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
67
71
|
ensureOptionalNativeToolsRegistered();
|
|
68
72
|
registerCodexCommand(pi, state, ensureOptionalNativeToolsRegistered);
|
|
69
73
|
|
|
74
|
+
pi.registerMessageRenderer(NATIVE_COMPACTION_DISPLAY_MESSAGE_TYPE, (message, _options, theme) => {
|
|
75
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
76
|
+
box.addChild(new Text(theme.fg("customMessageLabel", theme.bold("[compaction]")), 0, 0));
|
|
77
|
+
const content = typeof message.content === "string" ? message.content : NATIVE_COMPACTION_DISPLAY_TEXT;
|
|
78
|
+
box.addChild(new Text(`\n${theme.fg("customMessageText", content)}`, 0, 0));
|
|
79
|
+
const render = box.render.bind(box);
|
|
80
|
+
box.render = (width) => render(width).map((line) => truncateToWidth(line, width, ""));
|
|
81
|
+
return box;
|
|
82
|
+
});
|
|
83
|
+
|
|
70
84
|
sessions.onSessionExit((sessionId) => {
|
|
71
85
|
tracker.recordSessionFinished(sessionId);
|
|
72
86
|
});
|
|
@@ -83,6 +97,7 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
83
97
|
});
|
|
84
98
|
|
|
85
99
|
pi.on("resources_discover", async (event) => {
|
|
100
|
+
if (hasNoSkillsFlag()) return undefined;
|
|
86
101
|
const skillPaths = getCodexSkillPaths(event.cwd);
|
|
87
102
|
return skillPaths.length > 0 ? { skillPaths } : undefined;
|
|
88
103
|
});
|
|
@@ -124,7 +139,7 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
124
139
|
if (!shouldUseCodexAdapter(ctx, state.config)) {
|
|
125
140
|
return undefined;
|
|
126
141
|
}
|
|
127
|
-
const skills = resolvePromptSkills(event.systemPromptOptions?.skills, state.promptSkills);
|
|
142
|
+
const skills = hasNoSkillsFlag() ? [] : resolvePromptSkills(event.systemPromptOptions?.skills, state.promptSkills);
|
|
128
143
|
return {
|
|
129
144
|
systemPrompt: buildCodexSystemPrompt(event.systemPrompt, {
|
|
130
145
|
skills,
|
|
@@ -138,19 +153,25 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
138
153
|
return rewriteCodexProviderRequest(event.payload, ctx, state);
|
|
139
154
|
});
|
|
140
155
|
|
|
141
|
-
pi.on("
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
157
|
+
state.cwd = ctx.cwd;
|
|
158
|
+
return handleCodexSessionBeforeCompact(event, ctx, state, pi);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
pi.on("session_compact", async (event) => {
|
|
162
|
+
if (!event.fromExtension || !isNativeCompactionDetails(event.compactionEntry.details)) return;
|
|
163
|
+
pi.sendMessage(
|
|
164
|
+
{
|
|
165
|
+
customType: NATIVE_COMPACTION_DISPLAY_MESSAGE_TYPE,
|
|
166
|
+
content: NATIVE_COMPACTION_DISPLAY_TEXT,
|
|
167
|
+
display: true,
|
|
168
|
+
details: { compactionEntryId: event.compactionEntry.id },
|
|
169
|
+
},
|
|
170
|
+
{ triggerTurn: false },
|
|
171
|
+
);
|
|
153
172
|
});
|
|
173
|
+
|
|
174
|
+
pi.on("context", async (event) => ({ messages: event.messages.filter((message) => !isAdapterContextExcludedCustomMessage(message)) }));
|
|
154
175
|
}
|
|
155
176
|
|
|
156
177
|
export { getCodexSkillPaths, mergeAdapterTools, restoreTools, stripAdapterTools };
|
|
@@ -17,8 +17,10 @@ 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";
|
|
23
|
+
import { WEB_SEARCH_TOOL_NAME } from "../adapter/tool-set.ts";
|
|
22
24
|
|
|
23
25
|
const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
24
26
|
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
@@ -28,7 +30,6 @@ const OPENAI_CODEX_IMAGE_DIR = ".pi/openai-codex-images";
|
|
|
28
30
|
const OPENAI_CODEX_LATEST_IMAGE_NAME = "latest.png";
|
|
29
31
|
const MAX_RETRIES = 3;
|
|
30
32
|
const BASE_DELAY_MS = 1000;
|
|
31
|
-
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
32
33
|
const CODEX_RESPONSE_STATUSES = new Set(["completed", "incomplete", "failed", "cancelled", "queued", "in_progress"]);
|
|
33
34
|
const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
34
35
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
@@ -550,7 +551,7 @@ export function buildRequestBody<TApi extends Api>(model: Model<TApi>, context:
|
|
|
550
551
|
|
|
551
552
|
if (context.tools && context.tools.length > 0) {
|
|
552
553
|
body.tools = convertResponsesTools(context.tools, { strict: null });
|
|
553
|
-
const hasWebSearchTool = context.tools.some((tool) => tool.name ===
|
|
554
|
+
const hasWebSearchTool = context.tools.some((tool) => tool.name === WEB_SEARCH_TOOL_NAME);
|
|
554
555
|
if (hasWebSearchTool) {
|
|
555
556
|
body.include.push("web_search_call.action.sources", "web_search_call.results");
|
|
556
557
|
}
|
|
@@ -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;
|
|
@@ -2,10 +2,11 @@ import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-w
|
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { Container, Text } from "@earendil-works/pi-tui";
|
|
4
4
|
import { isOpenAICodexModel } from "../adapter/codex-model.ts";
|
|
5
|
+
import { WEB_SEARCH_TOOL_NAME } from "../adapter/tool-set.ts";
|
|
5
6
|
|
|
6
|
-
export const WEB_SEARCH_UNSUPPORTED_MESSAGE = "
|
|
7
|
+
export const WEB_SEARCH_UNSUPPORTED_MESSAGE = "web.run is only available with the openai-codex provider";
|
|
7
8
|
const WEB_SEARCH_LOCAL_EXECUTION_MESSAGE =
|
|
8
|
-
"
|
|
9
|
+
"web.run is a native openai-codex provider tool and should not execute locally";
|
|
9
10
|
export const WEB_SEARCH_SESSION_NOTE_TYPE = "codex-web-search-session-note";
|
|
10
11
|
const WEB_SEARCH_MULTIMODAL_CONTENT_TYPES = ["text", "image"] as const;
|
|
11
12
|
|
|
@@ -20,6 +21,7 @@ interface FunctionToolPayload {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
interface ResponsesPayload {
|
|
24
|
+
include?: unknown;
|
|
23
25
|
tools?: unknown[];
|
|
24
26
|
[key: string]: unknown;
|
|
25
27
|
}
|
|
@@ -43,7 +45,7 @@ export function supportsMultimodalNativeWebSearch(model: ExtensionContext["model
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
function isWebSearchFunctionTool(tool: unknown): tool is FunctionToolPayload {
|
|
46
|
-
return !!tool && typeof tool === "object" && (tool as FunctionToolPayload).type === "function" && (tool as FunctionToolPayload).name ===
|
|
48
|
+
return !!tool && typeof tool === "object" && (tool as FunctionToolPayload).type === "function" && (tool as FunctionToolPayload).name === WEB_SEARCH_TOOL_NAME;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
function createEmptyResultComponent(): Container {
|
|
@@ -80,17 +82,28 @@ export function rewriteNativeWebSearchTool(payload: unknown, model: ExtensionCon
|
|
|
80
82
|
if (!rewritten) {
|
|
81
83
|
return payload;
|
|
82
84
|
}
|
|
85
|
+
const existingInclude: unknown[] = Array.isArray((payload as ResponsesPayload).include)
|
|
86
|
+
? [...((payload as ResponsesPayload).include as unknown[])]
|
|
87
|
+
: [];
|
|
88
|
+
const include = [
|
|
89
|
+
...existingInclude,
|
|
90
|
+
...[
|
|
91
|
+
"web_search_call.action.sources",
|
|
92
|
+
"web_search_call.results",
|
|
93
|
+
].filter((item) => !existingInclude.includes(item)),
|
|
94
|
+
];
|
|
83
95
|
|
|
84
96
|
return {
|
|
85
97
|
...(payload as ResponsesPayload),
|
|
98
|
+
include,
|
|
86
99
|
tools: nextTools,
|
|
87
100
|
};
|
|
88
101
|
}
|
|
89
102
|
|
|
90
|
-
export function createWebSearchTool(): ToolDefinition<typeof WEB_SEARCH_PARAMETERS> {
|
|
103
|
+
export function createWebSearchTool(name: string = WEB_SEARCH_TOOL_NAME): ToolDefinition<typeof WEB_SEARCH_PARAMETERS> {
|
|
91
104
|
return {
|
|
92
|
-
name
|
|
93
|
-
label:
|
|
105
|
+
name,
|
|
106
|
+
label: name,
|
|
94
107
|
description:
|
|
95
108
|
"Search the web for sources relevant to the current task. Use it when you need up-to-date information, external references, or broader context beyond the workspace.",
|
|
96
109
|
promptSnippet:
|
|
@@ -104,7 +117,7 @@ export function createWebSearchTool(): ToolDefinition<typeof WEB_SEARCH_PARAMETE
|
|
|
104
117
|
throw new Error(WEB_SEARCH_LOCAL_EXECUTION_MESSAGE);
|
|
105
118
|
},
|
|
106
119
|
renderCall(_args, theme) {
|
|
107
|
-
return new Text(`${theme.fg("toolTitle", theme.bold(
|
|
120
|
+
return new Text(`${theme.fg("toolTitle", theme.bold(name))}`, 0, 0);
|
|
108
121
|
},
|
|
109
122
|
renderResult(result, { expanded }, theme) {
|
|
110
123
|
if (!expanded) {
|
|
@@ -117,6 +130,6 @@ export function createWebSearchTool(): ToolDefinition<typeof WEB_SEARCH_PARAMETE
|
|
|
117
130
|
};
|
|
118
131
|
}
|
|
119
132
|
|
|
120
|
-
export function registerWebSearchTool(pi: ExtensionAPI): void {
|
|
121
|
-
pi.registerTool(createWebSearchTool());
|
|
133
|
+
export function registerWebSearchTool(pi: ExtensionAPI, name: string = WEB_SEARCH_TOOL_NAME): void {
|
|
134
|
+
pi.registerTool(createWebSearchTool(name));
|
|
122
135
|
}
|
|
Binary file
|
|
Binary file
|