@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.
Files changed (99) hide show
  1. package/README.md +44 -17
  2. package/THIRD_PARTY_NOTICES.md +20 -0
  3. package/config.schema.json +1 -2
  4. package/index.ts +2 -0
  5. package/package.json +14 -13
  6. package/provenance/openai-codex-ddea03ad-astra.json +54 -0
  7. package/src/activation.ts +1 -56
  8. package/src/adapter/compaction/checkpoint.ts +1 -0
  9. package/src/adapter/compaction/collect.ts +1 -0
  10. package/src/adapter/compaction/http.ts +1 -0
  11. package/src/adapter/compaction/request.ts +1 -0
  12. package/src/adapter/compaction/transport.ts +1 -0
  13. package/src/adapter/compaction/websocket.ts +1 -0
  14. package/src/background-image-generation.ts +1 -574
  15. package/src/capabilities.ts +1 -146
  16. package/src/codex-http.ts +1 -133
  17. package/src/codex-identity-extension.ts +1 -339
  18. package/src/codex-request-profile.ts +1 -45
  19. package/src/codex-reserved-tools.ts +1 -323
  20. package/src/codex-wire-identity.ts +1 -596
  21. package/src/extension/prewarm-snapshot.ts +1 -0
  22. package/src/extension/provider-presentation.ts +1 -0
  23. package/src/extension/provider-runtime.ts +1 -0
  24. package/src/extension/register.ts +36 -0
  25. package/src/extension/startup-prewarm.ts +1 -0
  26. package/src/fast-mode.ts +1 -124
  27. package/src/glyphs.ts +1 -70
  28. package/src/index.ts +8 -331
  29. package/src/model-catalog/catalog.ts +1 -636
  30. package/src/model-catalog/default-models.json +8 -0
  31. package/src/model-catalog/runtime.ts +1 -113
  32. package/src/model-catalog/types.ts +1 -95
  33. package/src/native-compaction.ts +1 -393
  34. package/src/patch/apply.ts +1 -338
  35. package/src/patch/parser.ts +1 -224
  36. package/src/patch/render.ts +1 -201
  37. package/src/provider-headers.ts +1 -54
  38. package/src/provider-native-tools.ts +1 -71
  39. package/src/provider-shim.ts +20 -4511
  40. package/src/providers/codex-apply-patch-tool.ts +1 -23
  41. package/src/providers/openai-codex/cache-key.ts +1 -0
  42. package/src/providers/openai-codex/captured-stream.ts +1 -0
  43. package/src/providers/openai-codex/constants.ts +1 -0
  44. package/src/providers/openai-codex/continuation.ts +1 -0
  45. package/src/providers/openai-codex/errors.ts +1 -0
  46. package/src/providers/openai-codex/events.ts +1 -0
  47. package/src/providers/openai-codex/headers.ts +1 -0
  48. package/src/providers/openai-codex/lite.ts +1 -0
  49. package/src/providers/openai-codex/message.ts +1 -0
  50. package/src/providers/openai-codex/prewarm.ts +1 -0
  51. package/src/providers/openai-codex/proxy.ts +1 -0
  52. package/src/providers/openai-codex/reasoning.ts +1 -0
  53. package/src/providers/openai-codex/request-body.ts +1 -0
  54. package/src/providers/openai-codex/request-context.ts +1 -0
  55. package/src/providers/openai-codex/request-metadata.ts +1 -0
  56. package/src/providers/openai-codex/retry.ts +1 -0
  57. package/src/providers/openai-codex/runtime.ts +1 -0
  58. package/src/providers/openai-codex/sse.ts +1 -0
  59. package/src/providers/openai-codex/stream-effects.ts +1 -0
  60. package/src/providers/openai-codex/stream.ts +1 -0
  61. package/src/providers/openai-codex/types.ts +1 -0
  62. package/src/providers/openai-codex/urls.ts +1 -0
  63. package/src/providers/openai-codex/usage.ts +1 -0
  64. package/src/providers/openai-codex/websocket-connection.ts +1 -0
  65. package/src/providers/openai-codex/websocket-events.ts +1 -0
  66. package/src/providers/openai-codex/websocket-session.ts +1 -0
  67. package/src/providers/openai-codex/websocket-socket.ts +1 -0
  68. package/src/providers/openai-codex/websocket-stream.ts +1 -0
  69. package/src/providers/openai-responses-shared.ts +7 -1463
  70. package/src/providers/responses/citations.ts +1 -0
  71. package/src/providers/responses/history.ts +1 -0
  72. package/src/providers/responses/items.ts +1 -0
  73. package/src/providers/responses/markdown.ts +1 -0
  74. package/src/providers/responses/messages.ts +1 -0
  75. package/src/providers/responses/signatures.ts +1 -0
  76. package/src/providers/responses/stream-state.ts +1 -0
  77. package/src/providers/responses/stream.ts +1 -0
  78. package/src/providers/responses/text-renderer.ts +1 -0
  79. package/src/providers/responses/text.ts +1 -0
  80. package/src/providers/responses/tool-identity.ts +1 -0
  81. package/src/providers/responses/tools.ts +1 -0
  82. package/src/providers/responses/types.ts +1 -0
  83. package/src/providers/responses/usage.ts +1 -0
  84. package/src/settings.ts +1 -247
  85. package/src/subagent-inline.ts +1 -8
  86. package/src/tools/apply-patch.ts +1 -84
  87. package/src/tools/image-generation/capture.ts +1 -0
  88. package/src/tools/image-generation/display.ts +1 -0
  89. package/src/tools/image-generation/preview.ts +1 -0
  90. package/src/tools/image-generation/storage.ts +1 -0
  91. package/src/tools/image-generation/types.ts +1 -0
  92. package/src/tools/image-generation.ts +1 -274
  93. package/src/tools/view-image.ts +1 -98
  94. package/src/tools/web-search/activity.ts +1 -0
  95. package/src/tools/web-search/capture.ts +1 -0
  96. package/src/tools/web-search/render.ts +1 -0
  97. package/src/tools/web-search.ts +1 -554
  98. package/src/utils/images.ts +1 -73
  99. package/src/utils/theme.ts +1 -0
@@ -1,146 +1 @@
1
- import type { CodexMinimalToolsSettings } from "./settings.js";
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
- import type { ProviderHeaders } from "@earendil-works/pi-ai";
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
- import {
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
- import type { ReasoningSummary } from "./model-catalog/types.js";
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";