@howaboua/pi-codex-conversion 1.0.4 → 1.0.5
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 +4 -0
- package/package.json +1 -1
- package/src/index.ts +27 -32
- package/src/tools/codex-rendering.ts +0 -8
- package/src/tools/web-search-tool.ts +15 -26
package/README.md
CHANGED
|
@@ -10,6 +10,9 @@ This package replaces Pi's default Codex/GPT experience with a narrower Codex-li
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
> [!NOTE]
|
|
14
|
+
> Native OpenAI Codex Responses web search runs silently. Pi does not expose native web-search usage events to extensions, so the adapter shows a one-time session notice instead of per-search tool-call history.
|
|
15
|
+
|
|
13
16
|
## Active tools in adapter mode
|
|
14
17
|
|
|
15
18
|
When the adapter is active, the LLM sees these tools:
|
|
@@ -55,6 +58,7 @@ npm run check
|
|
|
55
58
|
- `write_stdin({ session_id, chars: "y\\n" })` renders like `Interacted with background terminal`
|
|
56
59
|
- `view_image({ path: "/absolute/path/to/screenshot.png" })` is available on image-capable models
|
|
57
60
|
- `web_search` is surfaced only on `openai-codex`, and the adapter rewrites it into the native OpenAI Responses `type: "web_search"` payload instead of executing a local function tool
|
|
61
|
+
- when native web search is available, the adapter shows a one-time session notice; individual searches are not surfaced because Pi does not expose native web-search execution events to extensions
|
|
58
62
|
|
|
59
63
|
Raw command output is still available by expanding the tool result.
|
|
60
64
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,12 +8,13 @@ import { createExecSessionManager } from "./tools/exec-session-manager.ts";
|
|
|
8
8
|
import { buildCodexSystemPrompt, extractPiPromptSkills, type PromptSkill } from "./prompt/build-system-prompt.ts";
|
|
9
9
|
import { registerViewImageTool, supportsOriginalImageDetail } from "./tools/view-image-tool.ts";
|
|
10
10
|
import {
|
|
11
|
-
WEB_SEARCH_ACTIVITY_MESSAGE_TYPE,
|
|
12
|
-
payloadContainsWebSearchTool,
|
|
13
|
-
registerWebSearchMessageRenderer,
|
|
14
11
|
registerWebSearchTool,
|
|
12
|
+
registerWebSearchSessionNoteRenderer,
|
|
15
13
|
rewriteNativeWebSearchTool,
|
|
14
|
+
shouldShowWebSearchSessionNote,
|
|
16
15
|
supportsNativeWebSearch,
|
|
16
|
+
WEB_SEARCH_SESSION_NOTE_TEXT,
|
|
17
|
+
WEB_SEARCH_SESSION_NOTE_TYPE,
|
|
17
18
|
} from "./tools/web-search-tool.ts";
|
|
18
19
|
import { registerWriteStdinTool } from "./tools/write-stdin-tool.ts";
|
|
19
20
|
|
|
@@ -21,7 +22,7 @@ interface AdapterState {
|
|
|
21
22
|
enabled: boolean;
|
|
22
23
|
previousToolNames?: string[];
|
|
23
24
|
promptSkills: PromptSkill[];
|
|
24
|
-
|
|
25
|
+
webSearchNoticeShown: boolean;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const ADAPTER_TOOL_NAMES = [...CORE_ADAPTER_TOOL_NAMES, VIEW_IMAGE_TOOL_NAME, WEB_SEARCH_TOOL_NAME];
|
|
@@ -35,20 +36,21 @@ function getCommandArg(args: unknown): string | undefined {
|
|
|
35
36
|
|
|
36
37
|
export default function codexConversion(pi: ExtensionAPI) {
|
|
37
38
|
const tracker = createExecCommandTracker();
|
|
38
|
-
const state: AdapterState = { enabled: false, promptSkills: [],
|
|
39
|
+
const state: AdapterState = { enabled: false, promptSkills: [], webSearchNoticeShown: false };
|
|
39
40
|
const sessions = createExecSessionManager();
|
|
40
41
|
|
|
41
42
|
registerApplyPatchTool(pi);
|
|
42
43
|
registerExecCommandTool(pi, tracker, sessions);
|
|
43
44
|
registerWriteStdinTool(pi, sessions);
|
|
44
45
|
registerWebSearchTool(pi);
|
|
45
|
-
|
|
46
|
+
registerWebSearchSessionNoteRenderer(pi);
|
|
46
47
|
|
|
47
48
|
sessions.onSessionExit((_sessionId, command) => {
|
|
48
49
|
tracker.recordCommandFinished(command);
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
pi.on("session_start", async (_event, ctx) => {
|
|
53
|
+
state.webSearchNoticeShown = false;
|
|
52
54
|
syncAdapter(pi, ctx, state);
|
|
53
55
|
});
|
|
54
56
|
|
|
@@ -84,39 +86,19 @@ export default function codexConversion(pi: ExtensionAPI) {
|
|
|
84
86
|
};
|
|
85
87
|
});
|
|
86
88
|
|
|
87
|
-
pi.on("context", async (event) => {
|
|
88
|
-
return {
|
|
89
|
-
messages: event.messages.filter(
|
|
90
|
-
(message) => !(message.role === "custom" && message.customType === WEB_SEARCH_ACTIVITY_MESSAGE_TYPE),
|
|
91
|
-
),
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
pi.on("turn_start", async () => {
|
|
96
|
-
state.pendingWebSearchCount = 0;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
89
|
pi.on("before_provider_request", async (event, ctx) => {
|
|
100
90
|
if (!isOpenAICodexContext(ctx)) {
|
|
101
91
|
return undefined;
|
|
102
92
|
}
|
|
103
|
-
if (payloadContainsWebSearchTool(event.payload)) {
|
|
104
|
-
state.pendingWebSearchCount += 1;
|
|
105
|
-
}
|
|
106
93
|
return rewriteNativeWebSearchTool(event.payload, ctx.model);
|
|
107
94
|
});
|
|
108
95
|
|
|
109
|
-
pi.on("
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
content: "",
|
|
116
|
-
display: true,
|
|
117
|
-
details: { count: state.pendingWebSearchCount },
|
|
118
|
-
});
|
|
119
|
-
state.pendingWebSearchCount = 0;
|
|
96
|
+
pi.on("context", async (event) => {
|
|
97
|
+
return {
|
|
98
|
+
messages: event.messages.filter(
|
|
99
|
+
(message) => !(message.role === "custom" && message.customType === WEB_SEARCH_SESSION_NOTE_TYPE),
|
|
100
|
+
),
|
|
101
|
+
};
|
|
120
102
|
});
|
|
121
103
|
}
|
|
122
104
|
|
|
@@ -124,6 +106,7 @@ function syncAdapter(pi: ExtensionAPI, ctx: ExtensionContext, state: AdapterStat
|
|
|
124
106
|
state.promptSkills = extractPiPromptSkills(ctx.getSystemPrompt());
|
|
125
107
|
|
|
126
108
|
registerViewImageTool(pi, { allowOriginalDetail: supportsOriginalImageDetail(ctx.model) });
|
|
109
|
+
maybeShowWebSearchSessionNote(pi, ctx, state);
|
|
127
110
|
|
|
128
111
|
if (isCodexLikeContext(ctx)) {
|
|
129
112
|
enableAdapter(pi, ctx, state);
|
|
@@ -190,3 +173,15 @@ export function restoreTools(previousTools: string[], activeTools: string[]): st
|
|
|
190
173
|
function hasAdapterTools(activeTools: string[]): boolean {
|
|
191
174
|
return activeTools.some((toolName) => ADAPTER_TOOL_NAMES.includes(toolName));
|
|
192
175
|
}
|
|
176
|
+
|
|
177
|
+
function maybeShowWebSearchSessionNote(pi: ExtensionAPI, ctx: ExtensionContext, state: AdapterState): void {
|
|
178
|
+
if (!shouldShowWebSearchSessionNote(ctx.model, ctx.hasUI, state.webSearchNoticeShown)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
pi.sendMessage({
|
|
182
|
+
customType: WEB_SEARCH_SESSION_NOTE_TYPE,
|
|
183
|
+
content: WEB_SEARCH_SESSION_NOTE_TEXT,
|
|
184
|
+
display: true,
|
|
185
|
+
});
|
|
186
|
+
state.webSearchNoticeShown = true;
|
|
187
|
+
}
|
|
@@ -32,14 +32,6 @@ export function renderWriteStdinCall(
|
|
|
32
32
|
return text;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function renderWebSearchActivity(count: number, theme: RenderTheme, expanded = false): string {
|
|
36
|
-
let text = `${theme.fg("dim", "•")} ${theme.bold("Searched the web")}`;
|
|
37
|
-
if (expanded && count > 1) {
|
|
38
|
-
text += `\n${theme.fg("dim", " └ ")}${theme.fg("muted", `${count} web searches in this turn`)}`;
|
|
39
|
-
}
|
|
40
|
-
return text;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
35
|
function renderExplorationText(actions: ShellAction[], state: ExecCommandStatus, theme: RenderTheme): string {
|
|
44
36
|
const header = state === "running" ? "Exploring" : "Explored";
|
|
45
37
|
let text = `${theme.fg("dim", "•")} ${theme.bold(header)}`;
|
|
@@ -2,12 +2,13 @@ import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@mariozechn
|
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { Box, Text } from "@mariozechner/pi-tui";
|
|
4
4
|
import { isOpenAICodexModel } from "../adapter/codex-model.ts";
|
|
5
|
-
import { renderWebSearchActivity } from "./codex-rendering.ts";
|
|
6
5
|
|
|
7
6
|
export const WEB_SEARCH_UNSUPPORTED_MESSAGE = "web_search is only available with the openai-codex provider";
|
|
8
7
|
const WEB_SEARCH_LOCAL_EXECUTION_MESSAGE =
|
|
9
8
|
"web_search is a native openai-codex provider tool and should not execute locally";
|
|
10
|
-
export const
|
|
9
|
+
export const WEB_SEARCH_SESSION_NOTE_TYPE = "codex-web-search-session-note";
|
|
10
|
+
export const WEB_SEARCH_SESSION_NOTE_TEXT =
|
|
11
|
+
"Native OpenAI Codex web search is enabled for this session. Search runs silently and is not surfaced as a separate tool call.";
|
|
11
12
|
|
|
12
13
|
const WEB_SEARCH_PARAMETERS = Type.Object({}, { additionalProperties: false });
|
|
13
14
|
|
|
@@ -21,14 +22,18 @@ interface ResponsesPayload {
|
|
|
21
22
|
[key: string]: unknown;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export interface WebSearchActivityDetails {
|
|
25
|
-
count: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
25
|
export function supportsNativeWebSearch(model: ExtensionContext["model"]): boolean {
|
|
29
26
|
return isOpenAICodexModel(model);
|
|
30
27
|
}
|
|
31
28
|
|
|
29
|
+
export function shouldShowWebSearchSessionNote(
|
|
30
|
+
model: ExtensionContext["model"],
|
|
31
|
+
hasUI: boolean,
|
|
32
|
+
alreadyShown: boolean,
|
|
33
|
+
): boolean {
|
|
34
|
+
return hasUI && !alreadyShown && supportsNativeWebSearch(model);
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
function isWebSearchFunctionTool(tool: unknown): tool is FunctionToolPayload {
|
|
33
38
|
return !!tool && typeof tool === "object" && (tool as FunctionToolPayload).type === "function" && (tool as FunctionToolPayload).name === "web_search";
|
|
34
39
|
}
|
|
@@ -66,22 +71,6 @@ export function rewriteNativeWebSearchTool(payload: unknown, model: ExtensionCon
|
|
|
66
71
|
};
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
export function payloadContainsWebSearchTool(payload: unknown): boolean {
|
|
70
|
-
if (!payload || typeof payload !== "object") {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
const tools = (payload as ResponsesPayload).tools;
|
|
74
|
-
if (!Array.isArray(tools)) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
return tools.some(
|
|
78
|
-
(tool) =>
|
|
79
|
-
!!tool &&
|
|
80
|
-
typeof tool === "object" &&
|
|
81
|
-
(("type" in tool && (tool as { type?: unknown }).type === "web_search") || isWebSearchFunctionTool(tool)),
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
74
|
export function createWebSearchTool(): ToolDefinition<typeof WEB_SEARCH_PARAMETERS> {
|
|
86
75
|
return {
|
|
87
76
|
name: "web_search",
|
|
@@ -113,11 +102,11 @@ export function registerWebSearchTool(pi: ExtensionAPI): void {
|
|
|
113
102
|
pi.registerTool(createWebSearchTool());
|
|
114
103
|
}
|
|
115
104
|
|
|
116
|
-
export function
|
|
117
|
-
pi.registerMessageRenderer
|
|
118
|
-
const count = typeof message.details?.count === "number" ? message.details.count : 1;
|
|
105
|
+
export function registerWebSearchSessionNoteRenderer(pi: ExtensionAPI): void {
|
|
106
|
+
pi.registerMessageRenderer(WEB_SEARCH_SESSION_NOTE_TYPE, (_message, _options, theme) => {
|
|
119
107
|
const box = new Box(1, 1, (text) => theme.bg("toolSuccessBg", text));
|
|
120
|
-
box.addChild(new Text(
|
|
108
|
+
box.addChild(new Text(theme.bold("Web search enabled"), 0, 0));
|
|
109
|
+
box.addChild(new Text(`\n${theme.fg("dim", WEB_SEARCH_SESSION_NOTE_TEXT)}`, 0, 0));
|
|
121
110
|
return box;
|
|
122
111
|
});
|
|
123
112
|
}
|