pi-openai-codex-compat 0.0.1-alpha.0
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/CHANGELOG.md +64 -0
- package/LICENSE +20 -0
- package/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/pi-ai-MIT.txt +21 -0
- package/README.md +331 -0
- package/THIRD_PARTY_NOTICES.md +21 -0
- package/extensions/openai-codex-compat/apply-patch-diff-render.ts +436 -0
- package/extensions/openai-codex-compat/apply-patch-engine.ts +1004 -0
- package/extensions/openai-codex-compat/apply-patch-render.ts +133 -0
- package/extensions/openai-codex-compat/apply-patch.ts +142 -0
- package/extensions/openai-codex-compat/codex-protocol.ts +598 -0
- package/extensions/openai-codex-compat/codex-provider.ts +740 -0
- package/extensions/openai-codex-compat/codex-stream.ts +444 -0
- package/extensions/openai-codex-compat/codex-tool-surface.ts +186 -0
- package/extensions/openai-codex-compat/codex-transport.ts +855 -0
- package/extensions/openai-codex-compat/compaction-checkpoint.ts +304 -0
- package/extensions/openai-codex-compat/config.ts +268 -0
- package/extensions/openai-codex-compat/footer.ts +99 -0
- package/extensions/openai-codex-compat/image-generation-render.ts +166 -0
- package/extensions/openai-codex-compat/image-generation.ts +355 -0
- package/extensions/openai-codex-compat/index.ts +65 -0
- package/extensions/openai-codex-compat/model-policy.ts +67 -0
- package/extensions/openai-codex-compat/namespaced-tools.ts +43 -0
- package/extensions/openai-codex-compat/native-history.ts +78 -0
- package/extensions/openai-codex-compat/remote-compaction.ts +198 -0
- package/extensions/openai-codex-compat/request-options.ts +121 -0
- package/extensions/openai-codex-compat/responses-replay.ts +33 -0
- package/extensions/openai-codex-compat/settings-pane.ts +298 -0
- package/extensions/openai-codex-compat/tool-runtime.ts +32 -0
- package/extensions/openai-codex-compat/tools.ts +70 -0
- package/extensions/openai-codex-compat/vendor/pi-ai/README.md +15 -0
- package/extensions/openai-codex-compat/vendor/pi-ai/openai-responses-serialization.ts +660 -0
- package/extensions/openai-codex-compat/web-run-description.txt +105 -0
- package/extensions/openai-codex-compat/web-run-output.ts +172 -0
- package/extensions/openai-codex-compat/web-run-render.ts +681 -0
- package/extensions/openai-codex-compat/web-run-schema.ts +301 -0
- package/extensions/openai-codex-compat/web-run.ts +164 -0
- package/package.json +63 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type Component, Container, Text } from "@earendil-works/pi-tui";
|
|
3
|
+
import { type ApplyPatchDetails, previewPatch } from "./apply-patch-engine.ts";
|
|
4
|
+
import { ApplyPatchDiffComponent, isApplyPatchDetails } from "./apply-patch-diff-render.ts";
|
|
5
|
+
import {
|
|
6
|
+
CodexToolSurfaceComponent,
|
|
7
|
+
type CodexToolBackgroundResolver,
|
|
8
|
+
} from "./codex-tool-surface.ts";
|
|
9
|
+
import { DEFAULT_CONFIG } from "./config.ts";
|
|
10
|
+
|
|
11
|
+
export { formatApplyPatchRenderText } from "./apply-patch-diff-render.ts";
|
|
12
|
+
|
|
13
|
+
type ApplyPatchArgs = {
|
|
14
|
+
patch: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ApplyPatchRenderState = {
|
|
18
|
+
previewKey?: string;
|
|
19
|
+
previewPending?: boolean;
|
|
20
|
+
preview?: ApplyPatchDetails;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type ApplyPatchRenderContext = {
|
|
24
|
+
args: ApplyPatchArgs;
|
|
25
|
+
toolCallId: string;
|
|
26
|
+
invalidate: () => void;
|
|
27
|
+
lastComponent: unknown;
|
|
28
|
+
state: ApplyPatchRenderState;
|
|
29
|
+
cwd: string;
|
|
30
|
+
executionStarted: boolean;
|
|
31
|
+
argsComplete: boolean;
|
|
32
|
+
isPartial: boolean;
|
|
33
|
+
expanded: boolean;
|
|
34
|
+
showImages: boolean;
|
|
35
|
+
isError: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type ApplyPatchResult = {
|
|
39
|
+
content: Array<{ type: string; text?: string }>;
|
|
40
|
+
details?: unknown;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export function renderApplyPatchCall(
|
|
44
|
+
args: ApplyPatchArgs,
|
|
45
|
+
theme: Theme,
|
|
46
|
+
context: ApplyPatchRenderContext,
|
|
47
|
+
resolveBackground: CodexToolBackgroundResolver = () => DEFAULT_CONFIG.toolBackground,
|
|
48
|
+
): Component {
|
|
49
|
+
const container = new Container();
|
|
50
|
+
container.addChild(new Text(theme.fg("toolTitle", theme.bold("apply_patch")), 0, 0));
|
|
51
|
+
|
|
52
|
+
if (context.isPartial) {
|
|
53
|
+
const state = context.state;
|
|
54
|
+
const patch = typeof args.patch === "string" ? args.patch : "";
|
|
55
|
+
if (context.argsComplete && patch) {
|
|
56
|
+
const previewKey = `${context.cwd}\0${patch}`;
|
|
57
|
+
if (state.previewKey !== previewKey) {
|
|
58
|
+
state.previewKey = previewKey;
|
|
59
|
+
delete state.preview;
|
|
60
|
+
state.previewPending = false;
|
|
61
|
+
}
|
|
62
|
+
if (!state.preview && !state.previewPending) {
|
|
63
|
+
state.previewPending = true;
|
|
64
|
+
void previewPatch(context.cwd, patch)
|
|
65
|
+
.then((preview) => {
|
|
66
|
+
if (state.previewKey === previewKey) state.preview = preview;
|
|
67
|
+
})
|
|
68
|
+
.catch(() => {})
|
|
69
|
+
.finally(() => {
|
|
70
|
+
if (state.previewKey === previewKey) {
|
|
71
|
+
state.previewPending = false;
|
|
72
|
+
context.invalidate();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isApplyPatchDetails(state.preview)) {
|
|
79
|
+
container.addChild(
|
|
80
|
+
new ApplyPatchDiffComponent(state.preview, theme, context.cwd, context.expanded),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return new CodexToolSurfaceComponent(container, theme, {
|
|
86
|
+
background: resolveBackground,
|
|
87
|
+
status: context.isPartial ? "pending" : context.isError ? "error" : "success",
|
|
88
|
+
top: true,
|
|
89
|
+
bottom: context.isPartial,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function renderApplyPatchResult(
|
|
94
|
+
result: ApplyPatchResult,
|
|
95
|
+
options: { isPartial: boolean },
|
|
96
|
+
theme: Theme,
|
|
97
|
+
context: ApplyPatchRenderContext,
|
|
98
|
+
resolveBackground: CodexToolBackgroundResolver = () => DEFAULT_CONFIG.toolBackground,
|
|
99
|
+
): Component {
|
|
100
|
+
if (options.isPartial) return new Container();
|
|
101
|
+
|
|
102
|
+
const details = isApplyPatchDetails(result.details) ? result.details : undefined;
|
|
103
|
+
const preview = isApplyPatchDetails(context.state.preview) ? context.state.preview : undefined;
|
|
104
|
+
const renderDetails =
|
|
105
|
+
details?.status === "failed" && preview
|
|
106
|
+
? {
|
|
107
|
+
...preview,
|
|
108
|
+
status: "failed" as const,
|
|
109
|
+
...(details.error !== undefined ? { error: details.error } : {}),
|
|
110
|
+
}
|
|
111
|
+
: details;
|
|
112
|
+
|
|
113
|
+
if (renderDetails) {
|
|
114
|
+
return new CodexToolSurfaceComponent(
|
|
115
|
+
new ApplyPatchDiffComponent(renderDetails, theme, context.cwd, context.expanded),
|
|
116
|
+
theme,
|
|
117
|
+
{
|
|
118
|
+
background: resolveBackground,
|
|
119
|
+
status: context.isError ? "error" : "success",
|
|
120
|
+
top: false,
|
|
121
|
+
bottom: true,
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const text = context.isError ? theme.bold(theme.fg("error", "✘ Failed to apply patch")) : "";
|
|
126
|
+
if (!text) return new Container();
|
|
127
|
+
return new CodexToolSurfaceComponent(new Text(text, 0, 0), theme, {
|
|
128
|
+
background: resolveBackground,
|
|
129
|
+
status: "error",
|
|
130
|
+
top: false,
|
|
131
|
+
bottom: true,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import type { CodexToolBackgroundResolver } from "./codex-tool-surface.ts";
|
|
5
|
+
import { DEFAULT_CONFIG } from "./config.ts";
|
|
6
|
+
import {
|
|
7
|
+
applyPatch,
|
|
8
|
+
type ApplyPatchDetails,
|
|
9
|
+
ApplyPatchExecutionError,
|
|
10
|
+
formatApplyPatchModelOutput,
|
|
11
|
+
formatApplyPatchSummary,
|
|
12
|
+
} from "./apply-patch-engine.ts";
|
|
13
|
+
import { renderApplyPatchCall, renderApplyPatchResult } from "./apply-patch-render.ts";
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
applyPatch,
|
|
17
|
+
type ApplyPatchDetails,
|
|
18
|
+
type AppliedPatchChange,
|
|
19
|
+
ApplyPatchExecutionError,
|
|
20
|
+
ApplyPatchInputError,
|
|
21
|
+
ApplyPatchParseError,
|
|
22
|
+
ApplyPatchVerificationError,
|
|
23
|
+
type ParsedPatch,
|
|
24
|
+
type PatchOperation,
|
|
25
|
+
type UpdateChunk,
|
|
26
|
+
parsePatch,
|
|
27
|
+
parsePatchDocument,
|
|
28
|
+
previewPatch,
|
|
29
|
+
} from "./apply-patch-engine.ts";
|
|
30
|
+
|
|
31
|
+
export const APPLY_PATCH_TOOL_NAME = "apply_patch";
|
|
32
|
+
export const APPLY_PATCH_INPUT_PROPERTY = "patch";
|
|
33
|
+
|
|
34
|
+
// Adapted from OpenAI Codex's Apache-2.0 apply_patch grammar; see
|
|
35
|
+
// THIRD_PARTY_NOTICES.md. Pi serializes it as a native custom grammar tool for
|
|
36
|
+
// capable models and falls back to an ordinary function tool elsewhere.
|
|
37
|
+
export const APPLY_PATCH_LARK_GRAMMAR = String.raw`start: begin_patch hunk+ end_patch
|
|
38
|
+
begin_patch: "*** Begin Patch" LF
|
|
39
|
+
end_patch: "*** End Patch" LF?
|
|
40
|
+
|
|
41
|
+
hunk: add_hunk | delete_hunk | update_hunk
|
|
42
|
+
add_hunk: "*** Add File: " filename LF add_line+
|
|
43
|
+
delete_hunk: "*** Delete File: " filename LF
|
|
44
|
+
update_hunk: "*** Update File: " filename LF change_move? change?
|
|
45
|
+
|
|
46
|
+
filename: /(.+)/
|
|
47
|
+
add_line: "+" /(.*)/ LF -> line
|
|
48
|
+
|
|
49
|
+
change_move: "*** Move to: " filename LF
|
|
50
|
+
change: (change_context | change_line)+ eof_line?
|
|
51
|
+
change_context: ("@@" | "@@ " /(.+)/) LF
|
|
52
|
+
change_line: ("+" | "-" | " ") /(.*)/ LF
|
|
53
|
+
eof_line: "*** End of File" LF
|
|
54
|
+
|
|
55
|
+
%import common.LF`;
|
|
56
|
+
|
|
57
|
+
export default function registerApplyPatch(
|
|
58
|
+
pi: ExtensionAPI,
|
|
59
|
+
resolveToolBackground: CodexToolBackgroundResolver = () => DEFAULT_CONFIG.toolBackground,
|
|
60
|
+
): void {
|
|
61
|
+
const failedDetails = new Map<string, ApplyPatchDetails>();
|
|
62
|
+
|
|
63
|
+
pi.on("tool_result", (event) => {
|
|
64
|
+
if (event.toolName !== APPLY_PATCH_TOOL_NAME) return;
|
|
65
|
+
const details = failedDetails.get(event.toolCallId);
|
|
66
|
+
if (!details) return;
|
|
67
|
+
failedDetails.delete(event.toolCallId);
|
|
68
|
+
return { details };
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
pi.registerTool({
|
|
72
|
+
name: APPLY_PATCH_TOOL_NAME,
|
|
73
|
+
label: APPLY_PATCH_TOOL_NAME,
|
|
74
|
+
description:
|
|
75
|
+
"The `apply_patch` tool can be used to edit files. This is a FREEFORM tool, so do not wrap the patch in JSON.",
|
|
76
|
+
promptSnippet: "Apply Codex-format patches to add, edit, move, or delete files",
|
|
77
|
+
promptGuidelines: [
|
|
78
|
+
"Use apply_patch for local file edits.",
|
|
79
|
+
"Do not create or edit files with cat or other shell write tricks.",
|
|
80
|
+
"Formatting commands and bulk mechanical rewrites do not need apply_patch.",
|
|
81
|
+
"Do not use Python to read or write files when a simple shell command or apply_patch is enough.",
|
|
82
|
+
],
|
|
83
|
+
parameters: Type.Object({
|
|
84
|
+
patch: Type.String({ description: "Raw patch text beginning with *** Begin Patch" }),
|
|
85
|
+
}),
|
|
86
|
+
constrainedSampling: {
|
|
87
|
+
type: "grammar",
|
|
88
|
+
variants: { openai_lark: APPLY_PATCH_LARK_GRAMMAR },
|
|
89
|
+
},
|
|
90
|
+
executionMode: "sequential",
|
|
91
|
+
renderShell: "self",
|
|
92
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
93
|
+
let executionStartedAt: number | undefined;
|
|
94
|
+
const executionDurationMs = (): number => {
|
|
95
|
+
if (executionStartedAt === undefined) {
|
|
96
|
+
throw new Error("apply_patch execution timing was not initialized.");
|
|
97
|
+
}
|
|
98
|
+
return performance.now() - executionStartedAt;
|
|
99
|
+
};
|
|
100
|
+
try {
|
|
101
|
+
const details = await applyPatch(ctx.cwd, params.patch, signal, {
|
|
102
|
+
onExecutionStart() {
|
|
103
|
+
executionStartedAt = performance.now();
|
|
104
|
+
},
|
|
105
|
+
onProgress(partialDetails) {
|
|
106
|
+
onUpdate?.({
|
|
107
|
+
content: [{ type: "text", text: "" }],
|
|
108
|
+
details: partialDetails,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: formatApplyPatchModelOutput(
|
|
117
|
+
0,
|
|
118
|
+
executionDurationMs(),
|
|
119
|
+
formatApplyPatchSummary(details),
|
|
120
|
+
),
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
details,
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error instanceof ApplyPatchExecutionError) {
|
|
127
|
+
failedDetails.set(toolCallId, error.details);
|
|
128
|
+
throw new Error(
|
|
129
|
+
formatApplyPatchModelOutput(1, executionDurationMs(), `${error.message}\n`),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
renderCall(args, theme, context) {
|
|
136
|
+
return renderApplyPatchCall(args, theme, context, resolveToolBackground);
|
|
137
|
+
},
|
|
138
|
+
renderResult(result, options, theme, context) {
|
|
139
|
+
return renderApplyPatchResult(result, options, theme, context, resolveToolBackground);
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|