@oai404iao/pi-codex-minimal-tools 1.4.0 → 2.0.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/README.md +44 -17
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/config.schema.json +1 -2
- package/index.ts +2 -0
- package/package.json +14 -13
- package/provenance/openai-codex-ddea03ad-astra.json +54 -0
- package/src/activation.ts +1 -56
- package/src/adapter/compaction/checkpoint.ts +1 -0
- package/src/adapter/compaction/collect.ts +1 -0
- package/src/adapter/compaction/http.ts +1 -0
- package/src/adapter/compaction/request.ts +1 -0
- package/src/adapter/compaction/transport.ts +1 -0
- package/src/adapter/compaction/websocket.ts +1 -0
- package/src/background-image-generation.ts +1 -574
- package/src/capabilities.ts +1 -146
- package/src/codex-http.ts +1 -133
- package/src/codex-identity-extension.ts +1 -339
- package/src/codex-request-profile.ts +1 -45
- package/src/codex-reserved-tools.ts +1 -323
- package/src/codex-wire-identity.ts +1 -596
- package/src/extension/prewarm-snapshot.ts +1 -0
- package/src/extension/provider-presentation.ts +1 -0
- package/src/extension/provider-runtime.ts +1 -0
- package/src/extension/register.ts +36 -0
- package/src/extension/startup-prewarm.ts +1 -0
- package/src/fast-mode.ts +1 -124
- package/src/glyphs.ts +1 -70
- package/src/index.ts +8 -331
- package/src/model-catalog/catalog.ts +1 -636
- package/src/model-catalog/default-models.json +8 -0
- package/src/model-catalog/runtime.ts +1 -113
- package/src/model-catalog/types.ts +1 -95
- package/src/native-compaction.ts +1 -393
- package/src/patch/apply.ts +1 -338
- package/src/patch/parser.ts +1 -224
- package/src/patch/render.ts +1 -201
- package/src/provider-headers.ts +1 -54
- package/src/provider-native-tools.ts +1 -71
- package/src/provider-shim.ts +20 -4511
- package/src/providers/codex-apply-patch-tool.ts +1 -23
- package/src/providers/openai-codex/cache-key.ts +1 -0
- package/src/providers/openai-codex/captured-stream.ts +1 -0
- package/src/providers/openai-codex/constants.ts +1 -0
- package/src/providers/openai-codex/continuation.ts +1 -0
- package/src/providers/openai-codex/errors.ts +1 -0
- package/src/providers/openai-codex/events.ts +1 -0
- package/src/providers/openai-codex/headers.ts +1 -0
- package/src/providers/openai-codex/lite.ts +1 -0
- package/src/providers/openai-codex/message.ts +1 -0
- package/src/providers/openai-codex/prewarm.ts +1 -0
- package/src/providers/openai-codex/proxy.ts +1 -0
- package/src/providers/openai-codex/reasoning.ts +1 -0
- package/src/providers/openai-codex/request-body.ts +1 -0
- package/src/providers/openai-codex/request-context.ts +1 -0
- package/src/providers/openai-codex/request-metadata.ts +1 -0
- package/src/providers/openai-codex/retry.ts +1 -0
- package/src/providers/openai-codex/runtime.ts +1 -0
- package/src/providers/openai-codex/sse.ts +1 -0
- package/src/providers/openai-codex/stream-effects.ts +1 -0
- package/src/providers/openai-codex/stream.ts +1 -0
- package/src/providers/openai-codex/types.ts +1 -0
- package/src/providers/openai-codex/urls.ts +1 -0
- package/src/providers/openai-codex/usage.ts +1 -0
- package/src/providers/openai-codex/websocket-connection.ts +1 -0
- package/src/providers/openai-codex/websocket-events.ts +1 -0
- package/src/providers/openai-codex/websocket-session.ts +1 -0
- package/src/providers/openai-codex/websocket-socket.ts +1 -0
- package/src/providers/openai-codex/websocket-stream.ts +1 -0
- package/src/providers/openai-responses-shared.ts +7 -1463
- package/src/providers/responses/citations.ts +1 -0
- package/src/providers/responses/history.ts +1 -0
- package/src/providers/responses/items.ts +1 -0
- package/src/providers/responses/markdown.ts +1 -0
- package/src/providers/responses/messages.ts +1 -0
- package/src/providers/responses/signatures.ts +1 -0
- package/src/providers/responses/stream-state.ts +1 -0
- package/src/providers/responses/stream.ts +1 -0
- package/src/providers/responses/text-renderer.ts +1 -0
- package/src/providers/responses/text.ts +1 -0
- package/src/providers/responses/tool-identity.ts +1 -0
- package/src/providers/responses/tools.ts +1 -0
- package/src/providers/responses/types.ts +1 -0
- package/src/providers/responses/usage.ts +1 -0
- package/src/settings.ts +1 -247
- package/src/subagent-inline.ts +1 -8
- package/src/tools/apply-patch.ts +1 -84
- package/src/tools/image-generation/capture.ts +1 -0
- package/src/tools/image-generation/display.ts +1 -0
- package/src/tools/image-generation/preview.ts +1 -0
- package/src/tools/image-generation/storage.ts +1 -0
- package/src/tools/image-generation/types.ts +1 -0
- package/src/tools/image-generation.ts +1 -274
- package/src/tools/view-image.ts +1 -98
- package/src/tools/web-search/activity.ts +1 -0
- package/src/tools/web-search/capture.ts +1 -0
- package/src/tools/web-search/render.ts +1 -0
- package/src/tools/web-search.ts +1 -554
- package/src/utils/images.ts +1 -73
- package/src/utils/theme.ts +1 -0
package/src/capabilities.ts
CHANGED
|
@@ -1,146 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { loadModelSettings } from "./model-catalog/runtime.js";
|
|
3
|
-
|
|
4
|
-
export const PACKAGE_TOOL_NAMES = ["image_generation", "view_image", "apply_patch", "web_search"] as const;
|
|
5
|
-
export type PackageToolName = (typeof PACKAGE_TOOL_NAMES)[number];
|
|
6
|
-
export const NATIVE_MUTATION_TOOL_NAMES = ["edit", "write"] as const;
|
|
7
|
-
export type NativeMutationToolName = (typeof NATIVE_MUTATION_TOOL_NAMES)[number];
|
|
8
|
-
|
|
9
|
-
export interface ModelLike {
|
|
10
|
-
provider?: string;
|
|
11
|
-
id?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
api?: string;
|
|
14
|
-
input?: string[];
|
|
15
|
-
capabilities?: {
|
|
16
|
-
input?: string[];
|
|
17
|
-
inputModalities?: string[];
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface ToolCapability {
|
|
22
|
-
enabled: boolean;
|
|
23
|
-
reason: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type ToolCapabilityMap = Record<PackageToolName, ToolCapability>;
|
|
27
|
-
|
|
28
|
-
export function modelKey(model: ModelLike | undefined): string {
|
|
29
|
-
if (!model) return "no model";
|
|
30
|
-
return `${model.provider ?? "unknown"}/${model.id ?? model.name ?? "unknown"}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function supportsImageInput(model: ModelLike | undefined): boolean {
|
|
34
|
-
const inputs = [
|
|
35
|
-
...(model?.input ?? []),
|
|
36
|
-
...(model?.capabilities?.input ?? []),
|
|
37
|
-
...(model?.capabilities?.inputModalities ?? []),
|
|
38
|
-
].map((value) => value.toLowerCase());
|
|
39
|
-
return inputs.includes("image") || inputs.includes("images") || inputs.includes("vision");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function computeToolCapabilities(model: ModelLike | undefined, settings: CodexMinimalToolsSettings): ToolCapabilityMap {
|
|
43
|
-
if (!settings.enabled) {
|
|
44
|
-
return {
|
|
45
|
-
image_generation: { enabled: false, reason: "package disabled" },
|
|
46
|
-
view_image: { enabled: false, reason: "package disabled" },
|
|
47
|
-
apply_patch: { enabled: false, reason: "package disabled" },
|
|
48
|
-
web_search: { enabled: false, reason: "package disabled" },
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const modelSettings = loadModelSettings(model, undefined, settings);
|
|
53
|
-
const profile = modelSettings.modelProfile?.effective;
|
|
54
|
-
if (!profile || !profile.enabled) {
|
|
55
|
-
return {
|
|
56
|
-
image_generation: { enabled: false, reason: "model has no enabled model catalog profile" },
|
|
57
|
-
view_image: { enabled: false, reason: "model has no enabled model catalog profile" },
|
|
58
|
-
apply_patch: { enabled: false, reason: "model has no enabled model catalog profile" },
|
|
59
|
-
web_search: { enabled: false, reason: "model has no enabled model catalog profile" },
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const imageInput = supportsImageInput(model);
|
|
64
|
-
const imageGeneration = profile.tools.imageGeneration;
|
|
65
|
-
const webSearch = profile.tools.webSearch;
|
|
66
|
-
const providerShimActive = modelSettings.providerShimActive;
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
image_generation: imageGeneration === "hosted" && providerShimActive && imageInput
|
|
70
|
-
? { enabled: true, reason: "model profile enables hosted image_generation" }
|
|
71
|
-
: imageGeneration === "standalone" && imageInput
|
|
72
|
-
? { enabled: true, reason: "model profile enables standalone image generation" }
|
|
73
|
-
: profile.tools.imageGeneration !== false && settings.directImageApiFallback
|
|
74
|
-
? { enabled: true, reason: "direct Images API fallback enabled" }
|
|
75
|
-
: { enabled: false, reason: imageGeneration === false ? "image_generation disabled by model profile" : "model does not advertise image input" },
|
|
76
|
-
view_image: profile.tools.viewImage && imageInput
|
|
77
|
-
? { enabled: true, reason: "model profile enables view_image and model accepts image input" }
|
|
78
|
-
: { enabled: false, reason: !profile.tools.viewImage ? "view_image disabled by model profile" : "model does not advertise image input" },
|
|
79
|
-
apply_patch: profile.tools.applyPatch === "custom" && !providerShimActive
|
|
80
|
-
? {
|
|
81
|
-
enabled: false,
|
|
82
|
-
reason: "custom apply_patch requires an OpenAI Responses provider shim API",
|
|
83
|
-
}
|
|
84
|
-
: profile.tools.applyPatch
|
|
85
|
-
? { enabled: true, reason: `model profile enables ${profile.tools.applyPatch} apply_patch` }
|
|
86
|
-
: {
|
|
87
|
-
enabled: false,
|
|
88
|
-
reason: "apply_patch disabled by model profile",
|
|
89
|
-
},
|
|
90
|
-
web_search: webSearch && (webSearch.implementation === "standalone" || providerShimActive)
|
|
91
|
-
? { enabled: true, reason: `model profile enables ${webSearch.implementation} web search` }
|
|
92
|
-
: {
|
|
93
|
-
enabled: false,
|
|
94
|
-
reason: webSearch !== false && webSearch.implementation === "hosted"
|
|
95
|
-
? "hosted web_search requires an OpenAI Responses provider shim API"
|
|
96
|
-
: "web_search disabled by model profile",
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function desiredPackageTools(model: ModelLike | undefined, settings: CodexMinimalToolsSettings): PackageToolName[] {
|
|
102
|
-
const capabilities = computeToolCapabilities(model, settings);
|
|
103
|
-
return PACKAGE_TOOL_NAMES.filter((name) => capabilities[name].enabled);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface ActiveToolSyncResult {
|
|
107
|
-
activeTools: string[];
|
|
108
|
-
added: string[];
|
|
109
|
-
removed: string[];
|
|
110
|
-
preserved: string[];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function computeNextActiveTools(currentActive: readonly string[], model: ModelLike | undefined, settings: CodexMinimalToolsSettings): ActiveToolSyncResult {
|
|
114
|
-
const current = new Set(currentActive);
|
|
115
|
-
const desired = new Set(desiredPackageTools(model, settings));
|
|
116
|
-
const added: string[] = [];
|
|
117
|
-
const removed: string[] = [];
|
|
118
|
-
|
|
119
|
-
for (const tool of PACKAGE_TOOL_NAMES) {
|
|
120
|
-
if (!desired.has(tool) && current.delete(tool)) removed.push(tool);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (settings.enabled && settings.autoEnable) {
|
|
124
|
-
for (const tool of desired) {
|
|
125
|
-
if (!current.has(tool)) {
|
|
126
|
-
current.add(tool);
|
|
127
|
-
added.push(tool);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (current.has("apply_patch")) {
|
|
133
|
-
for (const nativeMutationTool of NATIVE_MUTATION_TOOL_NAMES) {
|
|
134
|
-
if (current.delete(nativeMutationTool)) removed.push(nativeMutationTool);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const activeTools = currentActive.filter((name) => current.has(name));
|
|
139
|
-
for (const name of current) if (!activeTools.includes(name)) activeTools.push(name);
|
|
140
|
-
return {
|
|
141
|
-
activeTools,
|
|
142
|
-
added,
|
|
143
|
-
removed,
|
|
144
|
-
preserved: currentActive.filter((name) => !PACKAGE_TOOL_NAMES.includes(name as PackageToolName) && activeTools.includes(name)),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
1
|
+
export * from "@oai404iao/pi-codex-runtime/internal/capabilities";
|
package/src/codex-http.ts
CHANGED
|
@@ -1,133 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
isProviderHeaderSuppressed,
|
|
4
|
-
mergeProviderHeaders,
|
|
5
|
-
providerHeaderDirective,
|
|
6
|
-
setProviderDefaultHeader,
|
|
7
|
-
setProviderGeneratedHeader,
|
|
8
|
-
} from "./provider-headers.js";
|
|
9
|
-
|
|
10
|
-
const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
11
|
-
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
12
|
-
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
13
|
-
|
|
14
|
-
export interface CodexRequestAuth {
|
|
15
|
-
apiKey?: string;
|
|
16
|
-
headers?: ProviderHeaders;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function bearerToken(headers: Headers): string | undefined {
|
|
20
|
-
const authorization = headers.get("authorization");
|
|
21
|
-
const match = authorization ? /^Bearer\s+(.+)$/i.exec(authorization.trim()) : null;
|
|
22
|
-
return match?.[1]?.trim() || undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function authHeaders(options: {
|
|
26
|
-
modelHeaders?: ProviderHeaders;
|
|
27
|
-
auth: CodexRequestAuth;
|
|
28
|
-
}): Headers {
|
|
29
|
-
return mergeProviderHeaders(options.modelHeaders, options.auth.headers);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function hasCodexRequestAuth(options: {
|
|
33
|
-
modelHeaders?: ProviderHeaders;
|
|
34
|
-
auth: CodexRequestAuth;
|
|
35
|
-
}): boolean {
|
|
36
|
-
const headers = authHeaders(options);
|
|
37
|
-
if (
|
|
38
|
-
options.auth.apiKey
|
|
39
|
-
&& providerHeaderDirective(options.auth.headers, "authorization") === undefined
|
|
40
|
-
&& !isProviderHeaderSuppressed(headers, "authorization")
|
|
41
|
-
) {
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
return ["authorization", "api-key", "x-api-key", "x-openai-actor-authorization"]
|
|
45
|
-
.some((name) => Boolean(headers.get(name)?.trim()));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function resolveCodexRequestAccountId(options: {
|
|
49
|
-
modelHeaders?: ProviderHeaders;
|
|
50
|
-
auth: CodexRequestAuth;
|
|
51
|
-
apiKeyMode: boolean;
|
|
52
|
-
}): string | undefined {
|
|
53
|
-
if (options.apiKeyMode) return undefined;
|
|
54
|
-
const headers = authHeaders(options);
|
|
55
|
-
if (
|
|
56
|
-
headers.get("chatgpt-account-id")?.trim()
|
|
57
|
-
|| headers.get("x-openai-actor-authorization")?.trim()
|
|
58
|
-
|| isProviderHeaderSuppressed(headers, "chatgpt-account-id")
|
|
59
|
-
) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
const requestAuthorization = providerHeaderDirective(options.auth.headers, "authorization");
|
|
63
|
-
const token = requestAuthorization !== undefined
|
|
64
|
-
? typeof requestAuthorization === "string" && requestAuthorization.trim()
|
|
65
|
-
? bearerToken(new Headers({ authorization: requestAuthorization }))
|
|
66
|
-
: undefined
|
|
67
|
-
: isProviderHeaderSuppressed(headers, "authorization")
|
|
68
|
-
? undefined
|
|
69
|
-
: options.auth.apiKey ?? bearerToken(headers);
|
|
70
|
-
return token ? extractCodexAccountId(token) : undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function responseEndpoint(baseUrl: string | undefined, apiKeyMode: boolean): string {
|
|
74
|
-
const raw = baseUrl?.trim() || (apiKeyMode ? DEFAULT_OPENAI_BASE_URL : DEFAULT_CODEX_BASE_URL);
|
|
75
|
-
const normalized = raw.replace(/\/+$/, "");
|
|
76
|
-
if (apiKeyMode) {
|
|
77
|
-
if (normalized.endsWith("/responses")) return normalized;
|
|
78
|
-
return `${normalized}/responses`;
|
|
79
|
-
}
|
|
80
|
-
if (normalized.endsWith("/codex/responses")) return normalized;
|
|
81
|
-
if (normalized.endsWith("/codex")) return `${normalized}/responses`;
|
|
82
|
-
return `${normalized}/codex/responses`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function resolveCodexApiEndpoint(
|
|
86
|
-
baseUrl: string | undefined,
|
|
87
|
-
apiKeyMode: boolean,
|
|
88
|
-
path: string,
|
|
89
|
-
): string {
|
|
90
|
-
const root = responseEndpoint(baseUrl, apiKeyMode).replace(/\/responses$/, "");
|
|
91
|
-
return `${root}/${path.replace(/^\/+/, "")}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function extractCodexAccountId(token: string): string {
|
|
95
|
-
try {
|
|
96
|
-
const parts = token.split(".");
|
|
97
|
-
if (parts.length !== 3) throw new Error("Invalid token");
|
|
98
|
-
const payload = JSON.parse(Buffer.from(parts[1] ?? "", "base64").toString("utf8"));
|
|
99
|
-
const accountId = payload?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
|
|
100
|
-
if (typeof accountId !== "string" || !accountId) throw new Error("No account ID in token");
|
|
101
|
-
return accountId;
|
|
102
|
-
} catch {
|
|
103
|
-
throw new Error("Failed to extract accountId from Codex OAuth token");
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function buildCodexJsonHeaders(options: {
|
|
108
|
-
modelHeaders?: ProviderHeaders;
|
|
109
|
-
auth: CodexRequestAuth;
|
|
110
|
-
apiKeyMode: boolean;
|
|
111
|
-
extraHeaders?: Record<string, string>;
|
|
112
|
-
}): Headers {
|
|
113
|
-
const headers = authHeaders(options);
|
|
114
|
-
for (const [name, value] of Object.entries(options.extraHeaders ?? {})) {
|
|
115
|
-
setProviderGeneratedHeader(headers, name, value);
|
|
116
|
-
}
|
|
117
|
-
const requestAuthorization = providerHeaderDirective(options.auth.headers, "authorization");
|
|
118
|
-
if (requestAuthorization === undefined && options.auth.apiKey) {
|
|
119
|
-
setProviderGeneratedHeader(headers, "Authorization", `Bearer ${options.auth.apiKey}`);
|
|
120
|
-
}
|
|
121
|
-
if (
|
|
122
|
-
!options.apiKeyMode
|
|
123
|
-
&& !headers.has("chatgpt-account-id")
|
|
124
|
-
&& !headers.has("x-openai-actor-authorization")
|
|
125
|
-
) {
|
|
126
|
-
const accountId = resolveCodexRequestAccountId(options);
|
|
127
|
-
if (accountId) setProviderDefaultHeader(headers, "chatgpt-account-id", accountId);
|
|
128
|
-
}
|
|
129
|
-
setProviderDefaultHeader(headers, "originator", "pi");
|
|
130
|
-
setProviderDefaultHeader(headers, "accept", "application/json");
|
|
131
|
-
setProviderDefaultHeader(headers, "content-type", "application/json");
|
|
132
|
-
return headers;
|
|
133
|
-
}
|
|
1
|
+
export * from "@oai404iao/pi-codex-runtime/internal/codex-http";
|
|
@@ -1,339 +1 @@
|
|
|
1
|
-
|
|
2
|
-
SessionManager,
|
|
3
|
-
type ExtensionAPI,
|
|
4
|
-
type InlineExtension,
|
|
5
|
-
type SessionEntry,
|
|
6
|
-
} from "@earendil-works/pi-coding-agent";
|
|
7
|
-
import {
|
|
8
|
-
advanceCodexWindow,
|
|
9
|
-
beginCodexTurn,
|
|
10
|
-
codexThreadIdentityFor,
|
|
11
|
-
createCodexChildIdentity,
|
|
12
|
-
createCodexRootIdentity,
|
|
13
|
-
endCodexTurn,
|
|
14
|
-
parseCodexThreadIdentity,
|
|
15
|
-
registerCodexThreadIdentity,
|
|
16
|
-
type CodexThreadIdentity,
|
|
17
|
-
} from "./codex-wire-identity.js";
|
|
18
|
-
|
|
19
|
-
export const CODEX_IDENTITY_CUSTOM_TYPE = "pi-codex/thread-identity";
|
|
20
|
-
const SUBAGENT_DESCRIPTOR_CUSTOM_TYPE = "pi-subagent/descriptor";
|
|
21
|
-
const SUBAGENT_LINEAGE_CUSTOM_TYPE = "pi-subagent/lineage";
|
|
22
|
-
const IDENTITY_LIFECYCLE_SYMBOL = Symbol.for(
|
|
23
|
-
"@oai404iao/pi-codex/identity-lifecycle/v1",
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
export interface CodexIdentitySessionView {
|
|
27
|
-
getSessionId(): string;
|
|
28
|
-
getSessionFile(): string | undefined;
|
|
29
|
-
getSessionDir(): string;
|
|
30
|
-
getCwd(): string;
|
|
31
|
-
getEntries(): SessionEntry[];
|
|
32
|
-
getBranch?(): SessionEntry[];
|
|
33
|
-
appendCustomEntry?(customType: string, data?: unknown): string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface SubagentLineage {
|
|
37
|
-
agentId: string;
|
|
38
|
-
parentAgentId: string;
|
|
39
|
-
parentPiSessionId: string;
|
|
40
|
-
parentSessionFile?: string;
|
|
41
|
-
relation: "spawn" | "fork";
|
|
42
|
-
agentName?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function record(value: unknown): Record<string, unknown> | undefined {
|
|
46
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
47
|
-
? value as Record<string, unknown>
|
|
48
|
-
: undefined;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function readIdentity(
|
|
52
|
-
entries: readonly SessionEntry[],
|
|
53
|
-
piSessionId?: string,
|
|
54
|
-
): CodexThreadIdentity | undefined {
|
|
55
|
-
for (let index = entries.length - 1; index >= 0; index--) {
|
|
56
|
-
const entry = entries[index];
|
|
57
|
-
if (
|
|
58
|
-
entry?.type !== "custom"
|
|
59
|
-
|| entry.customType !== CODEX_IDENTITY_CUSTOM_TYPE
|
|
60
|
-
) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
const identity = parseCodexThreadIdentity(entry.data);
|
|
65
|
-
if (!piSessionId || identity.piSessionId === piSessionId) {
|
|
66
|
-
return identity;
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
// A newer valid entry can repair a corrupt historical checkpoint.
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function readSubagentLineage(
|
|
76
|
-
entries: readonly SessionEntry[],
|
|
77
|
-
): SubagentLineage | undefined {
|
|
78
|
-
for (let index = entries.length - 1; index >= 0; index--) {
|
|
79
|
-
const entry = entries[index];
|
|
80
|
-
if (
|
|
81
|
-
entry?.type === "custom"
|
|
82
|
-
&& entry.customType === SUBAGENT_LINEAGE_CUSTOM_TYPE
|
|
83
|
-
) {
|
|
84
|
-
const lineage = record(entry.data);
|
|
85
|
-
if (
|
|
86
|
-
lineage?.version !== 1
|
|
87
|
-
|| lineage.openAIIdentity !== true
|
|
88
|
-
|| (lineage.relation !== "spawn" && lineage.relation !== "fork")
|
|
89
|
-
|| typeof lineage.agentId !== "string"
|
|
90
|
-
|| typeof lineage.parentAgentId !== "string"
|
|
91
|
-
|| typeof lineage.parentPiSessionId !== "string"
|
|
92
|
-
) {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
agentId: lineage.agentId,
|
|
97
|
-
parentAgentId: lineage.parentAgentId,
|
|
98
|
-
parentPiSessionId: lineage.parentPiSessionId,
|
|
99
|
-
relation: lineage.relation,
|
|
100
|
-
...(typeof lineage.parentSessionFile === "string"
|
|
101
|
-
? { parentSessionFile: lineage.parentSessionFile }
|
|
102
|
-
: {}),
|
|
103
|
-
...(typeof lineage.agentName === "string"
|
|
104
|
-
? { agentName: lineage.agentName }
|
|
105
|
-
: {}),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
if (
|
|
109
|
-
entry?.type !== "custom"
|
|
110
|
-
|| entry.customType !== SUBAGENT_DESCRIPTOR_CUSTOM_TYPE
|
|
111
|
-
) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const descriptor = record(entry.data);
|
|
115
|
-
const runtime = record(descriptor?.runtime);
|
|
116
|
-
if (
|
|
117
|
-
descriptor?.version !== 2
|
|
118
|
-
|| runtime?.openAIIdentity !== true
|
|
119
|
-
|| (descriptor.provider !== "spawn" && descriptor.provider !== "fork")
|
|
120
|
-
|| typeof descriptor.agentId !== "string"
|
|
121
|
-
|| typeof descriptor.parentAgentId !== "string"
|
|
122
|
-
|| typeof descriptor.parentPiSessionId !== "string"
|
|
123
|
-
) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
const agent = record(descriptor.agent);
|
|
127
|
-
return {
|
|
128
|
-
agentId: descriptor.agentId,
|
|
129
|
-
parentAgentId: descriptor.parentAgentId,
|
|
130
|
-
parentPiSessionId: descriptor.parentPiSessionId,
|
|
131
|
-
relation: descriptor.provider,
|
|
132
|
-
...(typeof descriptor.parentSessionFile === "string"
|
|
133
|
-
? { parentSessionFile: descriptor.parentSessionFile }
|
|
134
|
-
: {}),
|
|
135
|
-
...(typeof agent?.name === "string" ? { agentName: agent.name } : {}),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
return undefined;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function appendIdentity(
|
|
142
|
-
session: CodexIdentitySessionView,
|
|
143
|
-
identity: CodexThreadIdentity,
|
|
144
|
-
appendCurrent?: (identity: CodexThreadIdentity) => void,
|
|
145
|
-
): void {
|
|
146
|
-
if (appendCurrent) {
|
|
147
|
-
appendCurrent(identity);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
session.appendCustomEntry?.(
|
|
151
|
-
CODEX_IDENTITY_CUSTOM_TYPE,
|
|
152
|
-
structuredClone(identity),
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function openParentSession(
|
|
157
|
-
session: CodexIdentitySessionView,
|
|
158
|
-
lineage: SubagentLineage,
|
|
159
|
-
): CodexIdentitySessionView | undefined {
|
|
160
|
-
if (!lineage.parentSessionFile) return undefined;
|
|
161
|
-
try {
|
|
162
|
-
return SessionManager.open(
|
|
163
|
-
lineage.parentSessionFile,
|
|
164
|
-
session.getSessionDir(),
|
|
165
|
-
session.getCwd(),
|
|
166
|
-
);
|
|
167
|
-
} catch {
|
|
168
|
-
return undefined;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function resolveParentIdentity(
|
|
173
|
-
session: CodexIdentitySessionView,
|
|
174
|
-
lineage: SubagentLineage,
|
|
175
|
-
): CodexThreadIdentity {
|
|
176
|
-
const active = codexThreadIdentityFor(lineage.parentPiSessionId);
|
|
177
|
-
if (active) return active;
|
|
178
|
-
|
|
179
|
-
const parent = openParentSession(session, lineage);
|
|
180
|
-
if (parent) {
|
|
181
|
-
const persisted = readIdentity(
|
|
182
|
-
parent.getEntries(),
|
|
183
|
-
lineage.parentPiSessionId,
|
|
184
|
-
);
|
|
185
|
-
if (persisted) return registerCodexThreadIdentity(persisted);
|
|
186
|
-
|
|
187
|
-
// pi-codex-minimal-tools owns the Codex identity even when the parent
|
|
188
|
-
// happened to use a non-Codex model before creating this OpenAI child.
|
|
189
|
-
const root = createCodexRootIdentity(lineage.parentPiSessionId);
|
|
190
|
-
appendIdentity(parent, root);
|
|
191
|
-
return registerCodexThreadIdentity(root);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Ephemeral parents have no file to reopen. They are still represented by a
|
|
195
|
-
// process-local root identity for the lifetime of the child tree.
|
|
196
|
-
const root = createCodexRootIdentity(lineage.parentPiSessionId);
|
|
197
|
-
return registerCodexThreadIdentity(root);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function ensureCodexSessionIdentity(
|
|
201
|
-
session: CodexIdentitySessionView,
|
|
202
|
-
options: {
|
|
203
|
-
sessionStartReason?: string;
|
|
204
|
-
appendCurrent?: (identity: CodexThreadIdentity) => void;
|
|
205
|
-
} = {},
|
|
206
|
-
): CodexThreadIdentity {
|
|
207
|
-
const piSessionId = session.getSessionId();
|
|
208
|
-
const persisted = readIdentity(session.getEntries(), piSessionId);
|
|
209
|
-
if (persisted) return registerCodexThreadIdentity(persisted);
|
|
210
|
-
|
|
211
|
-
const lineage = readSubagentLineage(session.getEntries());
|
|
212
|
-
let identity: CodexThreadIdentity;
|
|
213
|
-
if (lineage) {
|
|
214
|
-
identity = createCodexChildIdentity(
|
|
215
|
-
piSessionId,
|
|
216
|
-
resolveParentIdentity(session, lineage),
|
|
217
|
-
{
|
|
218
|
-
relation: lineage.relation,
|
|
219
|
-
agentName: lineage.agentName,
|
|
220
|
-
},
|
|
221
|
-
);
|
|
222
|
-
} else {
|
|
223
|
-
const copied = readIdentity(session.getEntries());
|
|
224
|
-
identity = createCodexRootIdentity(piSessionId, {
|
|
225
|
-
...(options.sessionStartReason === "fork" && copied
|
|
226
|
-
? { forkedFromThreadId: copied.threadId }
|
|
227
|
-
: {}),
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
appendIdentity(session, identity, options.appendCurrent);
|
|
232
|
-
return registerCodexThreadIdentity(identity);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Install only the session/turn/window lifecycle needed by the Codex provider.
|
|
237
|
-
* It deliberately does not register providers, tools, commands, or renderers.
|
|
238
|
-
*/
|
|
239
|
-
export function installCodexIdentityLifecycle(pi: ExtensionAPI): void {
|
|
240
|
-
const guard = pi as unknown as Record<PropertyKey, unknown>;
|
|
241
|
-
if (guard[IDENTITY_LIFECYCLE_SYMBOL]) return;
|
|
242
|
-
guard[IDENTITY_LIFECYCLE_SYMBOL] = true;
|
|
243
|
-
|
|
244
|
-
const ensure = (
|
|
245
|
-
ctx: {
|
|
246
|
-
sessionManager?: Partial<CodexIdentitySessionView>;
|
|
247
|
-
},
|
|
248
|
-
sessionStartReason?: string,
|
|
249
|
-
): CodexThreadIdentity | undefined => {
|
|
250
|
-
const session = ctx.sessionManager;
|
|
251
|
-
if (!session || typeof session.getSessionId !== "function") {
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
const piSessionId = session.getSessionId();
|
|
255
|
-
if (typeof session.getEntries !== "function") {
|
|
256
|
-
const existing = codexThreadIdentityFor(piSessionId);
|
|
257
|
-
if (existing) return existing;
|
|
258
|
-
return registerCodexThreadIdentity(
|
|
259
|
-
createCodexRootIdentity(piSessionId),
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
return ensureCodexSessionIdentity(session as CodexIdentitySessionView, {
|
|
263
|
-
sessionStartReason,
|
|
264
|
-
appendCurrent: (identity) => {
|
|
265
|
-
pi.appendEntry(
|
|
266
|
-
CODEX_IDENTITY_CUSTOM_TYPE,
|
|
267
|
-
structuredClone(identity),
|
|
268
|
-
);
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
pi.on("session_start", async (event, ctx) => {
|
|
274
|
-
ensure(ctx as unknown as { sessionManager: CodexIdentitySessionView }, event.reason);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
pi.on("before_agent_start", async (_event, ctx) => {
|
|
278
|
-
const typed = ctx as unknown as { sessionManager: CodexIdentitySessionView };
|
|
279
|
-
const identity = ensure(typed);
|
|
280
|
-
if (!identity) return;
|
|
281
|
-
const lineage =
|
|
282
|
-
typeof typed.sessionManager.getEntries === "function"
|
|
283
|
-
? readSubagentLineage(typed.sessionManager.getEntries())
|
|
284
|
-
: undefined;
|
|
285
|
-
beginCodexTurn(identity.piSessionId, {
|
|
286
|
-
...(lineage?.parentPiSessionId
|
|
287
|
-
? { parentPiSessionId: lineage.parentPiSessionId }
|
|
288
|
-
: {}),
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
pi.on("agent_settled", async (_event, ctx) => {
|
|
293
|
-
const piSessionId = ctx.sessionManager?.getSessionId?.();
|
|
294
|
-
if (piSessionId) endCodexTurn(piSessionId);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
pi.on("session_compact", async (event, ctx) => {
|
|
298
|
-
const typed = ctx as unknown as { sessionManager: CodexIdentitySessionView };
|
|
299
|
-
if (!ensure(typed)) return;
|
|
300
|
-
const identity = advanceCodexWindow(
|
|
301
|
-
typed.sessionManager.getSessionId(),
|
|
302
|
-
event.compactionEntry.id,
|
|
303
|
-
);
|
|
304
|
-
pi.appendEntry(
|
|
305
|
-
CODEX_IDENTITY_CUSTOM_TYPE,
|
|
306
|
-
structuredClone(identity),
|
|
307
|
-
);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
pi.on("session_tree", async (_event, ctx) => {
|
|
311
|
-
const typed = ctx as unknown as { sessionManager: CodexIdentitySessionView };
|
|
312
|
-
const piSessionId = typed.sessionManager.getSessionId();
|
|
313
|
-
const branch = typed.sessionManager.getBranch?.()
|
|
314
|
-
?? typed.sessionManager.getEntries();
|
|
315
|
-
const identity = readIdentity(branch, piSessionId)
|
|
316
|
-
?? readIdentity(typed.sessionManager.getEntries(), piSessionId);
|
|
317
|
-
if (identity) registerCodexThreadIdentity(identity);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
pi.on("session_shutdown", async (_event, ctx) => {
|
|
321
|
-
const piSessionId = ctx.sessionManager?.getSessionId?.();
|
|
322
|
-
if (piSessionId) endCodexTurn(piSessionId);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** Named inline extension used by pi-subagent when normal inheritance is off. */
|
|
327
|
-
export function createCodexSubagentInlineExtension(
|
|
328
|
-
options: { parentSessionManager?: CodexIdentitySessionView } = {},
|
|
329
|
-
): InlineExtension {
|
|
330
|
-
if (options.parentSessionManager) {
|
|
331
|
-
ensureCodexSessionIdentity(options.parentSessionManager);
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
name: "pi-codex-subagent-identity",
|
|
335
|
-
factory: (pi) => {
|
|
336
|
-
installCodexIdentityLifecycle(pi);
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
}
|
|
1
|
+
export * from "@oai404iao/pi-codex-runtime/internal/codex-identity-extension";
|
|
@@ -1,45 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export interface CodexRequestProfile {
|
|
4
|
-
responsesMode: "standard" | "lite";
|
|
5
|
-
reasoningSummary: ReasoningSummary;
|
|
6
|
-
systemPromptPlacement: "instructions" | "developer";
|
|
7
|
-
patchTransport: "function" | "custom";
|
|
8
|
-
supportsHostedTools: boolean;
|
|
9
|
-
supportsParallelTools: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Only modes implemented by the current provider shim are configurable.
|
|
14
|
-
* Custom transport remains unavailable until its complete wire parser lands.
|
|
15
|
-
*/
|
|
16
|
-
export interface CodexRequestProfileOverride {
|
|
17
|
-
responsesMode?: "standard" | "lite";
|
|
18
|
-
reasoningSummary?: ReasoningSummary;
|
|
19
|
-
systemPromptPlacement?: "instructions" | "developer";
|
|
20
|
-
patchTransport?: "function" | "custom";
|
|
21
|
-
supportsHostedTools?: boolean;
|
|
22
|
-
supportsParallelTools?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const DEFAULT_CODEX_REQUEST_PROFILE: CodexRequestProfile = {
|
|
26
|
-
responsesMode: "standard",
|
|
27
|
-
reasoningSummary: "auto",
|
|
28
|
-
systemPromptPlacement: "instructions",
|
|
29
|
-
patchTransport: "function",
|
|
30
|
-
supportsHostedTools: true,
|
|
31
|
-
supportsParallelTools: true,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function resolveCodexRequestProfile(override: CodexRequestProfileOverride = {}): CodexRequestProfile {
|
|
35
|
-
const responsesMode = override.responsesMode ?? DEFAULT_CODEX_REQUEST_PROFILE.responsesMode;
|
|
36
|
-
return {
|
|
37
|
-
responsesMode,
|
|
38
|
-
reasoningSummary: override.reasoningSummary
|
|
39
|
-
?? (responsesMode === "lite" ? "none" : DEFAULT_CODEX_REQUEST_PROFILE.reasoningSummary),
|
|
40
|
-
systemPromptPlacement: responsesMode === "lite" ? "developer" : override.systemPromptPlacement ?? DEFAULT_CODEX_REQUEST_PROFILE.systemPromptPlacement,
|
|
41
|
-
patchTransport: override.patchTransport ?? DEFAULT_CODEX_REQUEST_PROFILE.patchTransport,
|
|
42
|
-
supportsHostedTools: responsesMode === "lite" ? false : override.supportsHostedTools ?? DEFAULT_CODEX_REQUEST_PROFILE.supportsHostedTools,
|
|
43
|
-
supportsParallelTools: responsesMode === "lite" ? false : override.supportsParallelTools ?? DEFAULT_CODEX_REQUEST_PROFILE.supportsParallelTools,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
1
|
+
export * from "@oai404iao/pi-codex-runtime/internal/codex-request-profile";
|