pi-openai-toolkit 0.13.0 → 0.14.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 +246 -379
- package/README.zh.md +246 -0
- package/package.json +3 -3
- package/src/codex-astra/extension.ts +7 -13
- package/src/config.ts +582 -620
- package/src/context-management/codex-provider.ts +6 -5
- package/src/context-management/history-notes.ts +2 -8
- package/src/context-management/tools.ts +3 -7
- package/src/context-management/window-manager.ts +2 -8
- package/src/extension-runtime.ts +10 -13
- package/src/types.ts +6 -16
- package/README_zh.md +0 -377
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type CodexContextProvider,
|
|
9
9
|
type CodexContextProviderResolution,
|
|
10
10
|
} from "./types";
|
|
11
|
+
import { ASTRA_MODEL_ID } from "../types";
|
|
11
12
|
|
|
12
13
|
const CODEX_PROVIDER = "openai-codex";
|
|
13
14
|
const CODEX_API = "openai-codex-responses";
|
|
@@ -31,11 +32,12 @@ export function isNativeCodexModel(
|
|
|
31
32
|
|
|
32
33
|
export function isCodexGatewayModel(
|
|
33
34
|
model: ExtensionContext["model"] | undefined,
|
|
34
|
-
allowlist: readonly string[] = [],
|
|
35
35
|
): boolean {
|
|
36
|
+
// Only the Astra SKU has a verified Codex-compatible gateway relay path
|
|
37
|
+
// (NEWapi + CLIProxyAPI passthrough); other gateway models stay on remote
|
|
38
|
+
// compaction v2. Matching is on the bare model id, by design.
|
|
36
39
|
if (!model || model.provider !== GATEWAY_PROVIDER || model.api !== GATEWAY_API) return false;
|
|
37
|
-
|
|
38
|
-
return allowlist.some((entry) => entry.trim() === spec);
|
|
40
|
+
return model.id === ASTRA_MODEL_ID;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export function normalizeCodexBackendBaseUrl(baseUrl: string | undefined | null): string | undefined {
|
|
@@ -170,7 +172,6 @@ export function codexContextProviderHeaders(
|
|
|
170
172
|
export async function resolveCodexContextProvider(
|
|
171
173
|
ctx: ExtensionContext,
|
|
172
174
|
modelOverride: ExtensionContext["model"] = ctx.model,
|
|
173
|
-
gatewayAllowlist: readonly string[] = [],
|
|
174
175
|
): Promise<CodexContextProviderResolution> {
|
|
175
176
|
const model = modelOverride;
|
|
176
177
|
const descriptor = {
|
|
@@ -182,7 +183,7 @@ export async function resolveCodexContextProvider(
|
|
|
182
183
|
if (!model) return { ok: false, reason: "unsupported-model" };
|
|
183
184
|
|
|
184
185
|
const isNative = isNativeCodexModel(model);
|
|
185
|
-
const isGateway = isCodexGatewayModel(model
|
|
186
|
+
const isGateway = isCodexGatewayModel(model);
|
|
186
187
|
if (!isNative && !isGateway) {
|
|
187
188
|
return {
|
|
188
189
|
ok: false,
|
|
@@ -78,12 +78,11 @@ export async function callHistoryNotesBackend(
|
|
|
78
78
|
mode: "tokens",
|
|
79
79
|
limit: TOOL_OUTPUT_TOKEN_LIMIT,
|
|
80
80
|
},
|
|
81
|
-
gatewayAllowlist: readonly string[] = [],
|
|
82
81
|
): Promise<HistoryNotesResult> {
|
|
83
82
|
const timeoutSignal = AbortSignal.timeout(BACKEND_TIMEOUT_MS);
|
|
84
83
|
if (signal?.aborted) return { ok: false, reason: "aborted" };
|
|
85
84
|
const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
86
|
-
const providerResult = await resolveProviderWithDeadline(ctx, requestSignal, timeoutSignal
|
|
85
|
+
const providerResult = await resolveProviderWithDeadline(ctx, requestSignal, timeoutSignal);
|
|
87
86
|
if (providerResult === "aborted") return { ok: false, reason: "aborted" };
|
|
88
87
|
if (providerResult === "timeout") return { ok: false, reason: "backend-timeout" };
|
|
89
88
|
const provider = providerResult;
|
|
@@ -135,7 +134,6 @@ async function resolveProviderWithDeadline(
|
|
|
135
134
|
ctx: ExtensionContext,
|
|
136
135
|
requestSignal: AbortSignal,
|
|
137
136
|
timeoutSignal: AbortSignal,
|
|
138
|
-
gatewayAllowlist: readonly string[],
|
|
139
137
|
): Promise<Awaited<ReturnType<typeof resolveCodexContextProvider>> | "aborted" | "timeout"> {
|
|
140
138
|
if (requestSignal.aborted) return timeoutSignal.aborted ? "timeout" : "aborted";
|
|
141
139
|
return new Promise((resolve) => {
|
|
@@ -148,7 +146,7 @@ async function resolveProviderWithDeadline(
|
|
|
148
146
|
};
|
|
149
147
|
const onAbort = () => finish(timeoutSignal.aborted ? "timeout" : "aborted");
|
|
150
148
|
requestSignal.addEventListener("abort", onAbort, { once: true });
|
|
151
|
-
resolveCodexContextProvider(ctx, ctx.model
|
|
149
|
+
resolveCodexContextProvider(ctx, ctx.model).then(
|
|
152
150
|
(value) => finish(value),
|
|
153
151
|
() => finish({ ok: false, reason: "auth-resolution-failed" }),
|
|
154
152
|
);
|
|
@@ -158,7 +156,6 @@ async function resolveProviderWithDeadline(
|
|
|
158
156
|
export async function loadHistoryNotesThreadHint(
|
|
159
157
|
ctx: ExtensionContext,
|
|
160
158
|
signal?: AbortSignal,
|
|
161
|
-
gatewayAllowlist: readonly string[] = [],
|
|
162
159
|
): Promise<string | undefined> {
|
|
163
160
|
const result = await callHistoryNotesBackend(
|
|
164
161
|
THREAD_HINT_ENDPOINT,
|
|
@@ -166,7 +163,6 @@ export async function loadHistoryNotesThreadHint(
|
|
|
166
163
|
ctx,
|
|
167
164
|
signal,
|
|
168
165
|
{ mode: "bytes", limit: THREAD_HINT_MAX_BYTES },
|
|
169
|
-
gatewayAllowlist,
|
|
170
166
|
);
|
|
171
167
|
if (!result.ok) {
|
|
172
168
|
if (result.reason === "aborted") throw new Error("Remote context hint request was aborted");
|
|
@@ -182,7 +178,6 @@ export async function executeHistoryNotesTool(
|
|
|
182
178
|
params: Record<string, unknown>,
|
|
183
179
|
ctx: ExtensionContext,
|
|
184
180
|
signal?: AbortSignal,
|
|
185
|
-
gatewayAllowlist: readonly string[] = [],
|
|
186
181
|
): Promise<AgentToolResult<CodexHistoryNotesDetails>> {
|
|
187
182
|
const endpoint = namespace === "history"
|
|
188
183
|
? HISTORY_ENDPOINTS[action as HistoryAction]
|
|
@@ -195,7 +190,6 @@ export async function executeHistoryNotesTool(
|
|
|
195
190
|
ctx,
|
|
196
191
|
signal,
|
|
197
192
|
undefined,
|
|
198
|
-
gatewayAllowlist,
|
|
199
193
|
);
|
|
200
194
|
if (!result.ok) throw new Error(formatHistoryNotesFailure(result.reason, result.status));
|
|
201
195
|
const modelResult = { ...result.value };
|
|
@@ -85,10 +85,7 @@ export function createContextManagementTools(
|
|
|
85
85
|
pi: ExtensionAPI,
|
|
86
86
|
manager: CodexContextWindowManager,
|
|
87
87
|
isActive: (ctx: ExtensionContext) => Promise<boolean> | boolean,
|
|
88
|
-
gatewayAllowlist: readonly string[] | (() => readonly string[]) = [],
|
|
89
88
|
): ContextManagementTools {
|
|
90
|
-
const getGatewayAllowlist = (): readonly string[] =>
|
|
91
|
-
typeof gatewayAllowlist === "function" ? gatewayAllowlist() : gatewayAllowlist;
|
|
92
89
|
const assertActive = async (ctx: ExtensionContext): Promise<void> => {
|
|
93
90
|
if (!(await isActive(ctx))) throw new Error("remote-context-inactive");
|
|
94
91
|
};
|
|
@@ -145,7 +142,7 @@ export function createContextManagementTools(
|
|
|
145
142
|
],
|
|
146
143
|
execute: async (_id, params, _signal, _update, ctx) => {
|
|
147
144
|
await assertActive(ctx);
|
|
148
|
-
return executeHistoryNotesTool("history", params.action, params as Record<string, unknown>, ctx, _signal
|
|
145
|
+
return executeHistoryNotesTool("history", params.action, params as Record<string, unknown>, ctx, _signal);
|
|
149
146
|
},
|
|
150
147
|
};
|
|
151
148
|
const notes: ToolDefinition<typeof NOTES_PARAMETERS, CodexHistoryNotesDetails> = {
|
|
@@ -164,7 +161,7 @@ export function createContextManagementTools(
|
|
|
164
161
|
executionMode: "sequential",
|
|
165
162
|
execute: async (_id, params, _signal, _update, ctx) => {
|
|
166
163
|
await assertActive(ctx);
|
|
167
|
-
return executeHistoryNotesTool("notes", params.action, params as Record<string, unknown>, ctx, _signal
|
|
164
|
+
return executeHistoryNotesTool("notes", params.action, params as Record<string, unknown>, ctx, _signal);
|
|
168
165
|
},
|
|
169
166
|
};
|
|
170
167
|
return { newContext, getContextRemaining, history, notes };
|
|
@@ -296,10 +293,9 @@ export function registerContextManagementTools(
|
|
|
296
293
|
pi: ExtensionAPI,
|
|
297
294
|
manager: CodexContextWindowManager,
|
|
298
295
|
isActive: (ctx: ExtensionContext) => Promise<boolean> | boolean,
|
|
299
|
-
gatewayAllowlist: readonly string[] | (() => readonly string[]) = [],
|
|
300
296
|
): ContextManagementToolController {
|
|
301
297
|
const controller = new ContextManagementToolController(pi);
|
|
302
|
-
controller.register(createContextManagementTools(pi, manager, isActive
|
|
298
|
+
controller.register(createContextManagementTools(pi, manager, isActive));
|
|
303
299
|
return controller;
|
|
304
300
|
}
|
|
305
301
|
|
|
@@ -58,15 +58,9 @@ export class CodexContextWindowManager {
|
|
|
58
58
|
private rolloverPending = false;
|
|
59
59
|
private trimPendingWindowId: string | undefined;
|
|
60
60
|
private readonly loadThreadHint: ThreadHintLoader;
|
|
61
|
-
private readonly getGatewayAllowlist: () => readonly string[];
|
|
62
61
|
|
|
63
|
-
constructor(
|
|
64
|
-
loadThreadHint
|
|
65
|
-
gatewayAllowlist: readonly string[] | (() => readonly string[]) = [],
|
|
66
|
-
) {
|
|
67
|
-
this.getGatewayAllowlist = typeof gatewayAllowlist === "function" ? gatewayAllowlist : () => gatewayAllowlist;
|
|
68
|
-
this.loadThreadHint = loadThreadHint ?? ((ctx, signal) =>
|
|
69
|
-
loadHistoryNotesThreadHint(ctx, signal, this.getGatewayAllowlist()));
|
|
62
|
+
constructor(loadThreadHint?: ThreadHintLoader) {
|
|
63
|
+
this.loadThreadHint = loadThreadHint ?? ((ctx, signal) => loadHistoryNotesThreadHint(ctx, signal));
|
|
70
64
|
}
|
|
71
65
|
|
|
72
66
|
reset(): void {
|
package/src/extension-runtime.ts
CHANGED
|
@@ -109,7 +109,7 @@ async function isRemoteContextActive(
|
|
|
109
109
|
model: ExtensionContext["model"] = ctx.model,
|
|
110
110
|
): Promise<boolean> {
|
|
111
111
|
if (!config.enabled || config.contextManagement !== "remote") return false;
|
|
112
|
-
const resolution = await resolveCodexContextProvider(ctx, model
|
|
112
|
+
const resolution = await resolveCodexContextProvider(ctx, model);
|
|
113
113
|
return resolution.ok;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -117,7 +117,8 @@ function isCodexContextModel(
|
|
|
117
117
|
model: ExtensionContext["model"] | undefined,
|
|
118
118
|
config: CompactionConfig,
|
|
119
119
|
): boolean {
|
|
120
|
-
return
|
|
120
|
+
return config.contextManagement === "remote"
|
|
121
|
+
&& (isNativeCodexModel(model) || isCodexGatewayModel(model));
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
function notifyRemoteContextFailure(ctx: ExtensionContext, reason: string): void {
|
|
@@ -369,7 +370,7 @@ async function handleSessionBeforeCompact(
|
|
|
369
370
|
|
|
370
371
|
// Remote Context management owns this eligible Codex session. It persists a
|
|
371
372
|
// no-summary boundary and deliberately never calls remote_compaction_v2.
|
|
372
|
-
if (
|
|
373
|
+
if (isCodexContextModel(ctx.model, config)) {
|
|
373
374
|
if (await remoteContextActive(ctx, config)) {
|
|
374
375
|
try {
|
|
375
376
|
dependencies.contextWindows.synchronize(ctx);
|
|
@@ -598,7 +599,7 @@ async function handleBeforeProviderRequest(
|
|
|
598
599
|
return undefined;
|
|
599
600
|
}
|
|
600
601
|
}
|
|
601
|
-
if (
|
|
602
|
+
if (isCodexContextModel(ctx.model, config)) {
|
|
602
603
|
// Keep Codex Remote mutually exclusive with the legacy replay
|
|
603
604
|
// pipeline when authentication or tool ownership is unavailable.
|
|
604
605
|
return undefined;
|
|
@@ -758,10 +759,7 @@ export default function registerCompactionExtension(
|
|
|
758
759
|
overrides: Partial<CompactionDependencies> = {},
|
|
759
760
|
) {
|
|
760
761
|
const loadConfig = overrides.loadConfig ?? loadToolkitConfig;
|
|
761
|
-
const contextWindows = overrides.contextWindows ?? new CodexContextWindowManager(
|
|
762
|
-
undefined,
|
|
763
|
-
() => loadConfig().config.compaction.codexGatewayModels,
|
|
764
|
-
);
|
|
762
|
+
const contextWindows = overrides.contextWindows ?? new CodexContextWindowManager();
|
|
765
763
|
const dependencies: CompactionDependencies = {
|
|
766
764
|
loadConfig,
|
|
767
765
|
remoteCompact: executeRemoteV2Compaction,
|
|
@@ -777,7 +775,6 @@ export default function registerCompactionExtension(
|
|
|
777
775
|
const config = dependencies.loadConfig().config.compaction;
|
|
778
776
|
return tools.isRegistered && await isRemoteContextActive(ctx, config);
|
|
779
777
|
},
|
|
780
|
-
() => dependencies.loadConfig().config.compaction.codexGatewayModels,
|
|
781
778
|
);
|
|
782
779
|
const remoteContextActive: RemoteContextActive = async (ctx, config, model = ctx.model) =>
|
|
783
780
|
tools.isRegistered && await isRemoteContextActive(ctx, config, model);
|
|
@@ -804,7 +801,7 @@ export default function registerCompactionExtension(
|
|
|
804
801
|
activationReason = "tool-name-conflict";
|
|
805
802
|
notifyRemoteContextFailure(ctx, activationReason);
|
|
806
803
|
} else {
|
|
807
|
-
const remoteResolution = await resolveCodexContextProvider(ctx, ctx.model
|
|
804
|
+
const remoteResolution = await resolveCodexContextProvider(ctx, ctx.model);
|
|
808
805
|
activationReason = remoteResolution.ok ? "codex-context-unavailable" : remoteResolution.reason;
|
|
809
806
|
notifyRemoteContextFailure(ctx, activationReason);
|
|
810
807
|
}
|
|
@@ -858,9 +855,9 @@ export default function registerCompactionExtension(
|
|
|
858
855
|
pi.on("before_provider_request", (event, ctx) => handleBeforeProviderRequest(event, ctx, dependencies.loadConfig, contextWindows, remoteContextActive));
|
|
859
856
|
pi.on("before_provider_headers", async (event, ctx) => {
|
|
860
857
|
const config = dependencies.loadConfig().config.compaction;
|
|
861
|
-
if (
|
|
858
|
+
if (!isCodexContextModel(ctx.model, config)) return;
|
|
862
859
|
if (!(await remoteContextActive(ctx, config))) return;
|
|
863
|
-
const provider = await resolveCodexContextProvider(ctx, ctx.model
|
|
860
|
+
const provider = await resolveCodexContextProvider(ctx, ctx.model);
|
|
864
861
|
if (provider.ok && provider.provider.kind === "codex-gateway") {
|
|
865
862
|
const sessionId = getSessionId(ctx);
|
|
866
863
|
const gatewayHeaders = codexContextProviderHeaders(provider.provider, {
|
|
@@ -891,7 +888,7 @@ export default function registerCompactionExtension(
|
|
|
891
888
|
});
|
|
892
889
|
pi.on("message_end", (event, ctx) => {
|
|
893
890
|
const config = dependencies.loadConfig().config.compaction;
|
|
894
|
-
if (
|
|
891
|
+
if (!isCodexContextModel(ctx.model, config) || !tools.isRegistered) return undefined;
|
|
895
892
|
const message = routeContextNamespaceToolMessage(event.message);
|
|
896
893
|
return message === event.message ? undefined : { message };
|
|
897
894
|
});
|
package/src/types.ts
CHANGED
|
@@ -13,6 +13,12 @@ export const REDACTED_VALUE = "[REDACTED]";
|
|
|
13
13
|
* `compaction.responsesApis` in config.json may only narrow this set.
|
|
14
14
|
*/
|
|
15
15
|
export const RESPONSES_COMPACT_CAPABLE_APIS = ["openai-responses", "openai-codex-responses"] as const;
|
|
16
|
+
/**
|
|
17
|
+
* The Codex model whose gateways are verified to speak the alpha window and
|
|
18
|
+
* Astra compatibility protocols. Remote Context and the Astra layer match on
|
|
19
|
+
* this bare model id; no operator allowlist is consulted.
|
|
20
|
+
*/
|
|
21
|
+
export const ASTRA_MODEL_ID = "gpt-6-astra";
|
|
16
22
|
export const LEGACY_NATIVE_COMPACTION_STRATEGY = "openai-native-compact-v1";
|
|
17
23
|
export const REMOTE_V2_COMPACTION_STRATEGY = "openai-remote-compaction-v2";
|
|
18
24
|
export const NATIVE_COMPACTION_STRATEGY = REMOTE_V2_COMPACTION_STRATEGY;
|
|
@@ -58,8 +64,6 @@ export type CompactionConfig = {
|
|
|
58
64
|
enabled: boolean;
|
|
59
65
|
/** Optional Codex Remote Context management. Disabled by default for compatibility. */
|
|
60
66
|
contextManagement: ContextManagementMode;
|
|
61
|
-
/** Exact provider/model specs allowed to use the extension's CPA-backed Codex gateway. */
|
|
62
|
-
codexGatewayModels: string[];
|
|
63
67
|
/**
|
|
64
68
|
* Allow a Responses session whose latest compaction was not created by this extension
|
|
65
69
|
* to restart native compaction from Pi's current serialized session context.
|
|
@@ -99,12 +103,6 @@ export type ImageGenerationConfig = {
|
|
|
99
103
|
enabled: boolean;
|
|
100
104
|
};
|
|
101
105
|
|
|
102
|
-
export type CodexAstraConfig = {
|
|
103
|
-
enabled: boolean;
|
|
104
|
-
/** Exact provider/model keys that receive the stable-effort `configuration_update` rewrite. */
|
|
105
|
-
models: string[];
|
|
106
|
-
};
|
|
107
|
-
|
|
108
106
|
/** `side-effect` reviews bash/write/edit plus extras; `all` reviews every tool call. */
|
|
109
107
|
export type AutoModeGate = "side-effect" | "all";
|
|
110
108
|
|
|
@@ -170,7 +168,6 @@ export type ToolkitConfig = {
|
|
|
170
168
|
webSearch: WebSearchConfig;
|
|
171
169
|
imageGeneration: ImageGenerationConfig;
|
|
172
170
|
autoMode: AutoModeConfig;
|
|
173
|
-
codexAstra: CodexAstraConfig;
|
|
174
171
|
};
|
|
175
172
|
|
|
176
173
|
export type LoadedToolkitConfig = {
|
|
@@ -505,7 +502,6 @@ export const DEFAULT_NATIVE_FALLBACK_CONFIG: NativeFallbackConfig = {
|
|
|
505
502
|
export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
|
|
506
503
|
enabled: true,
|
|
507
504
|
contextManagement: "off",
|
|
508
|
-
codexGatewayModels: [],
|
|
509
505
|
allowCompactionContinuityBreak: false,
|
|
510
506
|
remoteCompactModel: undefined,
|
|
511
507
|
nativeFallback: { ...DEFAULT_NATIVE_FALLBACK_CONFIG },
|
|
@@ -528,11 +524,6 @@ export const DEFAULT_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {
|
|
|
528
524
|
enabled: false,
|
|
529
525
|
};
|
|
530
526
|
|
|
531
|
-
export const DEFAULT_CODEX_ASTRA_CONFIG: CodexAstraConfig = {
|
|
532
|
-
enabled: false,
|
|
533
|
-
models: [],
|
|
534
|
-
};
|
|
535
|
-
|
|
536
527
|
export const DEFAULT_AUTO_MODE_CONFIG: AutoModeConfig = {
|
|
537
528
|
enabled: true,
|
|
538
529
|
models: [],
|
|
@@ -561,5 +552,4 @@ export const DEFAULT_TOOLKIT_CONFIG: ToolkitConfig = {
|
|
|
561
552
|
webSearch: DEFAULT_WEB_SEARCH_CONFIG,
|
|
562
553
|
imageGeneration: DEFAULT_IMAGE_GENERATION_CONFIG,
|
|
563
554
|
autoMode: DEFAULT_AUTO_MODE_CONFIG,
|
|
564
|
-
codexAstra: DEFAULT_CODEX_ASTRA_CONFIG,
|
|
565
555
|
};
|