pi-openai-toolkit 0.14.8 → 0.14.9

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 (39) hide show
  1. package/README.md +1 -1
  2. package/README.zh.md +1 -1
  3. package/docs/internals.md +18 -0
  4. package/extensions/auto-mode.ts +1 -1
  5. package/extensions/codex-astra.ts +1 -1
  6. package/extensions/compaction.ts +1 -1
  7. package/extensions/image-generation.ts +1 -1
  8. package/extensions/web-search.ts +1 -1
  9. package/package.json +2 -1
  10. package/src/auto-mode/classifier.ts +165 -165
  11. package/src/auto-mode/extension.ts +129 -20
  12. package/src/auto-mode/policy.ts +146 -146
  13. package/src/auto-mode/prompt.ts +279 -279
  14. package/src/auto-mode/reviewer.ts +325 -325
  15. package/src/auto-mode/tool-review-tui.ts +211 -0
  16. package/src/auto-mode/transcript.ts +133 -133
  17. package/src/auto-mode/types.ts +137 -137
  18. package/src/codex-astra/effort-planner.ts +175 -175
  19. package/src/codex-astra/extension.ts +122 -122
  20. package/src/compaction-projection.ts +31 -31
  21. package/src/context-management/messages.ts +112 -112
  22. package/src/context-management/namespace-tools.ts +426 -426
  23. package/src/context-management/tool-contract.ts +23 -23
  24. package/src/context-management/tools.ts +291 -291
  25. package/src/context-management/window-budget.ts +83 -83
  26. package/src/context-management/window-request.ts +125 -125
  27. package/src/image-generation/artifacts.ts +237 -237
  28. package/src/image-generation/client.ts +248 -248
  29. package/src/image-generation/eligibility.ts +18 -18
  30. package/src/image-generation/references.ts +194 -194
  31. package/src/image-generation/render.ts +252 -252
  32. package/src/model-scope.ts +17 -17
  33. package/src/pi-context-hook.ts +116 -116
  34. package/src/provider-headers.ts +23 -23
  35. package/src/web-search/alpha-client.ts +875 -875
  36. package/src/web-search/extension.ts +523 -523
  37. package/src/web-search/payload.ts +295 -295
  38. package/src/web-search/prompt.ts +59 -59
  39. package/src/web-search/types.ts +144 -144
@@ -1,248 +1,248 @@
1
- import { buildResponsesRequestHeaders } from "../responses-headers";
2
- import type { ResponsesRuntime } from "../runtime";
3
- import {
4
- extractProviderErrorMessage,
5
- parseImageGenerationResponse,
6
- type ImageGenerationRequestBody,
7
- } from "./protocol";
8
- import {
9
- IMAGE_REQUEST_TIMEOUT_MS,
10
- MAX_IMAGE_ERROR_BYTES,
11
- MAX_IMAGE_RESPONSE_BYTES,
12
- sanitizeImageDiagnostic,
13
- type ParsedGeneratedImage,
14
- } from "./types";
15
-
16
- export type ImageGenerationClientFailureReason =
17
- | "aborted"
18
- | "timeout"
19
- | "authentication"
20
- | "rate-limit"
21
- | "request-rejected"
22
- | "backend-unavailable"
23
- | "network"
24
- | "oversized-response"
25
- | "malformed-response"
26
- | "no-image";
27
-
28
- export type ImageGenerationClientResult =
29
- | { ok: true; image: ParsedGeneratedImage; status: number }
30
- | {
31
- ok: false;
32
- reason: ImageGenerationClientFailureReason;
33
- status?: number;
34
- errorMessage: string;
35
- };
36
-
37
- export type ImageGenerationFetch = (
38
- input: string | URL | Request,
39
- init?: RequestInit,
40
- ) => Promise<Response>;
41
-
42
- function buildRequestHeaders(runtime: ResponsesRuntime): Headers {
43
- return buildResponsesRequestHeaders(runtime, { accept: "application/json" });
44
- }
45
-
46
- async function readBoundedBody(
47
- response: Response,
48
- maxBytes: number,
49
- ): Promise<{ ok: true; bytes: Uint8Array } | { ok: false }> {
50
- const contentLength = response.headers.get("content-length");
51
- if (contentLength !== null) {
52
- const parsed = Number(contentLength);
53
- if (Number.isFinite(parsed) && parsed > maxBytes) return { ok: false };
54
- }
55
-
56
- if (!response.body) return { ok: true, bytes: new Uint8Array() };
57
- const reader = response.body.getReader();
58
- const chunks: Uint8Array[] = [];
59
- let total = 0;
60
- try {
61
- while (true) {
62
- const next = await reader.read();
63
- if (next.done) break;
64
- if (!next.value) continue;
65
- total += next.value.byteLength;
66
- if (total > maxBytes) {
67
- await reader.cancel().catch(() => undefined);
68
- return { ok: false };
69
- }
70
- chunks.push(next.value);
71
- }
72
- } finally {
73
- reader.releaseLock();
74
- }
75
-
76
- const bytes = new Uint8Array(total);
77
- let offset = 0;
78
- for (const chunk of chunks) {
79
- bytes.set(chunk, offset);
80
- offset += chunk.byteLength;
81
- }
82
- return { ok: true, bytes };
83
- }
84
-
85
- function parseJson(bytes: Uint8Array): unknown {
86
- const text = new TextDecoder().decode(bytes);
87
- return JSON.parse(text) as unknown;
88
- }
89
-
90
- function providerFailureText(value: unknown): string {
91
- if (!value || typeof value !== "object" || Array.isArray(value)) return "";
92
- const candidate = value as Record<string, unknown>;
93
- const error = candidate.error && typeof candidate.error === "object" && !Array.isArray(candidate.error)
94
- ? candidate.error as Record<string, unknown>
95
- : candidate;
96
- return [error.code, error.type, error.message]
97
- .filter((item): item is string => typeof item === "string")
98
- .join(" ")
99
- .toLowerCase();
100
- }
101
-
102
- function mapHttpFailure(status: number, payload?: unknown): ImageGenerationClientFailureReason {
103
- if (status === 401 || status === 403) return "authentication";
104
- const providerFailure = providerFailureText(payload);
105
- if (
106
- status === 429 ||
107
- /(?:rate|usage|billing)[-_ ]?limit|quota|insufficient[-_ ]?(?:quota|credit)/.test(providerFailure)
108
- ) {
109
- return "rate-limit";
110
- }
111
- if (status === 400 || status === 404 || status === 409 || status === 422) {
112
- return "request-rejected";
113
- }
114
- if (status >= 500) return "backend-unavailable";
115
- return "request-rejected";
116
- }
117
-
118
- function defaultHttpMessage(status: number, reason = mapHttpFailure(status)): string {
119
- switch (reason) {
120
- case "authentication":
121
- return "Image generation authentication failed for the current model provider.";
122
- case "rate-limit":
123
- return "Image generation was rate-limited or the provider image quota was exhausted.";
124
- case "backend-unavailable":
125
- return "Image generation is temporarily unavailable at the configured gateway or upstream provider.";
126
- default:
127
- return `Image generation request was rejected (HTTP ${status}).`;
128
- }
129
- }
130
-
131
- export async function requestGeneratedImage(args: {
132
- runtime: ResponsesRuntime;
133
- body: ImageGenerationRequestBody;
134
- signal?: AbortSignal;
135
- fetchFn?: ImageGenerationFetch;
136
- timeoutMs?: number;
137
- }): Promise<ImageGenerationClientResult> {
138
- const fetchFn = args.fetchFn ?? globalThis.fetch;
139
- const timeoutSignal = AbortSignal.timeout(args.timeoutMs ?? IMAGE_REQUEST_TIMEOUT_MS);
140
- const signal = args.signal
141
- ? AbortSignal.any([args.signal, timeoutSignal])
142
- : timeoutSignal;
143
-
144
- let response: Response;
145
- try {
146
- response = await fetchFn(args.runtime.responsesUrl, {
147
- method: "POST",
148
- headers: buildRequestHeaders(args.runtime),
149
- body: JSON.stringify(args.body),
150
- signal,
151
- });
152
- } catch (error) {
153
- if (args.signal?.aborted) {
154
- return { ok: false, reason: "aborted", errorMessage: "Image generation was cancelled." };
155
- }
156
- if (timeoutSignal.aborted) {
157
- return {
158
- ok: false,
159
- reason: "timeout",
160
- errorMessage: "Image generation timed out. The request was not retried automatically.",
161
- };
162
- }
163
- return {
164
- ok: false,
165
- reason: "network",
166
- errorMessage: sanitizeImageDiagnostic(
167
- error instanceof Error ? error.message : error,
168
- "Image generation network request failed. The request was not retried automatically.",
169
- ),
170
- };
171
- }
172
-
173
- let bounded: Awaited<ReturnType<typeof readBoundedBody>>;
174
- try {
175
- bounded = await readBoundedBody(
176
- response,
177
- response.ok ? MAX_IMAGE_RESPONSE_BYTES : MAX_IMAGE_ERROR_BYTES,
178
- );
179
- } catch (error) {
180
- if (args.signal?.aborted) {
181
- return { ok: false, reason: "aborted", errorMessage: "Image generation was cancelled." };
182
- }
183
- if (timeoutSignal.aborted) {
184
- return {
185
- ok: false,
186
- reason: "timeout",
187
- errorMessage: "Image generation timed out. The request was not retried automatically.",
188
- };
189
- }
190
- return {
191
- ok: false,
192
- reason: "network",
193
- status: response.status,
194
- errorMessage: sanitizeImageDiagnostic(
195
- error instanceof Error ? error.message : error,
196
- "Image generation response could not be read. The request was not retried automatically.",
197
- ),
198
- };
199
- }
200
- if (!bounded.ok) {
201
- return {
202
- ok: false,
203
- reason: "oversized-response",
204
- status: response.status,
205
- errorMessage: "Image generation response exceeded the configured size limit.",
206
- };
207
- }
208
-
209
- let payload: unknown;
210
- try {
211
- payload = parseJson(bounded.bytes);
212
- } catch {
213
- return {
214
- ok: false,
215
- reason: "malformed-response",
216
- status: response.status,
217
- errorMessage: response.ok
218
- ? "Image generation returned invalid JSON."
219
- : defaultHttpMessage(response.status),
220
- };
221
- }
222
-
223
- if (!response.ok) {
224
- const reason = mapHttpFailure(response.status, payload);
225
- return {
226
- ok: false,
227
- reason,
228
- status: response.status,
229
- errorMessage: extractProviderErrorMessage(payload, defaultHttpMessage(response.status, reason)),
230
- };
231
- }
232
-
233
- const parsed = parseImageGenerationResponse(payload);
234
- if (!parsed.ok) {
235
- return {
236
- ok: false,
237
- reason: parsed.reason,
238
- status: response.status,
239
- errorMessage: parsed.errorMessage,
240
- };
241
- }
242
- return { ok: true, image: parsed.image, status: response.status };
243
- }
244
-
245
- export const _clientTest = {
246
- buildRequestHeaders,
247
- readBoundedBody,
248
- };
1
+ import { buildResponsesRequestHeaders } from "../responses-headers";
2
+ import type { ResponsesRuntime } from "../runtime";
3
+ import {
4
+ extractProviderErrorMessage,
5
+ parseImageGenerationResponse,
6
+ type ImageGenerationRequestBody,
7
+ } from "./protocol";
8
+ import {
9
+ IMAGE_REQUEST_TIMEOUT_MS,
10
+ MAX_IMAGE_ERROR_BYTES,
11
+ MAX_IMAGE_RESPONSE_BYTES,
12
+ sanitizeImageDiagnostic,
13
+ type ParsedGeneratedImage,
14
+ } from "./types";
15
+
16
+ export type ImageGenerationClientFailureReason =
17
+ | "aborted"
18
+ | "timeout"
19
+ | "authentication"
20
+ | "rate-limit"
21
+ | "request-rejected"
22
+ | "backend-unavailable"
23
+ | "network"
24
+ | "oversized-response"
25
+ | "malformed-response"
26
+ | "no-image";
27
+
28
+ export type ImageGenerationClientResult =
29
+ | { ok: true; image: ParsedGeneratedImage; status: number }
30
+ | {
31
+ ok: false;
32
+ reason: ImageGenerationClientFailureReason;
33
+ status?: number;
34
+ errorMessage: string;
35
+ };
36
+
37
+ export type ImageGenerationFetch = (
38
+ input: string | URL | Request,
39
+ init?: RequestInit,
40
+ ) => Promise<Response>;
41
+
42
+ function buildRequestHeaders(runtime: ResponsesRuntime): Headers {
43
+ return buildResponsesRequestHeaders(runtime, { accept: "application/json" });
44
+ }
45
+
46
+ async function readBoundedBody(
47
+ response: Response,
48
+ maxBytes: number,
49
+ ): Promise<{ ok: true; bytes: Uint8Array } | { ok: false }> {
50
+ const contentLength = response.headers.get("content-length");
51
+ if (contentLength !== null) {
52
+ const parsed = Number(contentLength);
53
+ if (Number.isFinite(parsed) && parsed > maxBytes) return { ok: false };
54
+ }
55
+
56
+ if (!response.body) return { ok: true, bytes: new Uint8Array() };
57
+ const reader = response.body.getReader();
58
+ const chunks: Uint8Array[] = [];
59
+ let total = 0;
60
+ try {
61
+ while (true) {
62
+ const next = await reader.read();
63
+ if (next.done) break;
64
+ if (!next.value) continue;
65
+ total += next.value.byteLength;
66
+ if (total > maxBytes) {
67
+ await reader.cancel().catch(() => undefined);
68
+ return { ok: false };
69
+ }
70
+ chunks.push(next.value);
71
+ }
72
+ } finally {
73
+ reader.releaseLock();
74
+ }
75
+
76
+ const bytes = new Uint8Array(total);
77
+ let offset = 0;
78
+ for (const chunk of chunks) {
79
+ bytes.set(chunk, offset);
80
+ offset += chunk.byteLength;
81
+ }
82
+ return { ok: true, bytes };
83
+ }
84
+
85
+ function parseJson(bytes: Uint8Array): unknown {
86
+ const text = new TextDecoder().decode(bytes);
87
+ return JSON.parse(text) as unknown;
88
+ }
89
+
90
+ function providerFailureText(value: unknown): string {
91
+ if (!value || typeof value !== "object" || Array.isArray(value)) return "";
92
+ const candidate = value as Record<string, unknown>;
93
+ const error = candidate.error && typeof candidate.error === "object" && !Array.isArray(candidate.error)
94
+ ? candidate.error as Record<string, unknown>
95
+ : candidate;
96
+ return [error.code, error.type, error.message]
97
+ .filter((item): item is string => typeof item === "string")
98
+ .join(" ")
99
+ .toLowerCase();
100
+ }
101
+
102
+ function mapHttpFailure(status: number, payload?: unknown): ImageGenerationClientFailureReason {
103
+ if (status === 401 || status === 403) return "authentication";
104
+ const providerFailure = providerFailureText(payload);
105
+ if (
106
+ status === 429 ||
107
+ /(?:rate|usage|billing)[-_ ]?limit|quota|insufficient[-_ ]?(?:quota|credit)/.test(providerFailure)
108
+ ) {
109
+ return "rate-limit";
110
+ }
111
+ if (status === 400 || status === 404 || status === 409 || status === 422) {
112
+ return "request-rejected";
113
+ }
114
+ if (status >= 500) return "backend-unavailable";
115
+ return "request-rejected";
116
+ }
117
+
118
+ function defaultHttpMessage(status: number, reason = mapHttpFailure(status)): string {
119
+ switch (reason) {
120
+ case "authentication":
121
+ return "Image generation authentication failed for the current model provider.";
122
+ case "rate-limit":
123
+ return "Image generation was rate-limited or the provider image quota was exhausted.";
124
+ case "backend-unavailable":
125
+ return "Image generation is temporarily unavailable at the configured gateway or upstream provider.";
126
+ default:
127
+ return `Image generation request was rejected (HTTP ${status}).`;
128
+ }
129
+ }
130
+
131
+ export async function requestGeneratedImage(args: {
132
+ runtime: ResponsesRuntime;
133
+ body: ImageGenerationRequestBody;
134
+ signal?: AbortSignal;
135
+ fetchFn?: ImageGenerationFetch;
136
+ timeoutMs?: number;
137
+ }): Promise<ImageGenerationClientResult> {
138
+ const fetchFn = args.fetchFn ?? globalThis.fetch;
139
+ const timeoutSignal = AbortSignal.timeout(args.timeoutMs ?? IMAGE_REQUEST_TIMEOUT_MS);
140
+ const signal = args.signal
141
+ ? AbortSignal.any([args.signal, timeoutSignal])
142
+ : timeoutSignal;
143
+
144
+ let response: Response;
145
+ try {
146
+ response = await fetchFn(args.runtime.responsesUrl, {
147
+ method: "POST",
148
+ headers: buildRequestHeaders(args.runtime),
149
+ body: JSON.stringify(args.body),
150
+ signal,
151
+ });
152
+ } catch (error) {
153
+ if (args.signal?.aborted) {
154
+ return { ok: false, reason: "aborted", errorMessage: "Image generation was cancelled." };
155
+ }
156
+ if (timeoutSignal.aborted) {
157
+ return {
158
+ ok: false,
159
+ reason: "timeout",
160
+ errorMessage: "Image generation timed out. The request was not retried automatically.",
161
+ };
162
+ }
163
+ return {
164
+ ok: false,
165
+ reason: "network",
166
+ errorMessage: sanitizeImageDiagnostic(
167
+ error instanceof Error ? error.message : error,
168
+ "Image generation network request failed. The request was not retried automatically.",
169
+ ),
170
+ };
171
+ }
172
+
173
+ let bounded: Awaited<ReturnType<typeof readBoundedBody>>;
174
+ try {
175
+ bounded = await readBoundedBody(
176
+ response,
177
+ response.ok ? MAX_IMAGE_RESPONSE_BYTES : MAX_IMAGE_ERROR_BYTES,
178
+ );
179
+ } catch (error) {
180
+ if (args.signal?.aborted) {
181
+ return { ok: false, reason: "aborted", errorMessage: "Image generation was cancelled." };
182
+ }
183
+ if (timeoutSignal.aborted) {
184
+ return {
185
+ ok: false,
186
+ reason: "timeout",
187
+ errorMessage: "Image generation timed out. The request was not retried automatically.",
188
+ };
189
+ }
190
+ return {
191
+ ok: false,
192
+ reason: "network",
193
+ status: response.status,
194
+ errorMessage: sanitizeImageDiagnostic(
195
+ error instanceof Error ? error.message : error,
196
+ "Image generation response could not be read. The request was not retried automatically.",
197
+ ),
198
+ };
199
+ }
200
+ if (!bounded.ok) {
201
+ return {
202
+ ok: false,
203
+ reason: "oversized-response",
204
+ status: response.status,
205
+ errorMessage: "Image generation response exceeded the configured size limit.",
206
+ };
207
+ }
208
+
209
+ let payload: unknown;
210
+ try {
211
+ payload = parseJson(bounded.bytes);
212
+ } catch {
213
+ return {
214
+ ok: false,
215
+ reason: "malformed-response",
216
+ status: response.status,
217
+ errorMessage: response.ok
218
+ ? "Image generation returned invalid JSON."
219
+ : defaultHttpMessage(response.status),
220
+ };
221
+ }
222
+
223
+ if (!response.ok) {
224
+ const reason = mapHttpFailure(response.status, payload);
225
+ return {
226
+ ok: false,
227
+ reason,
228
+ status: response.status,
229
+ errorMessage: extractProviderErrorMessage(payload, defaultHttpMessage(response.status, reason)),
230
+ };
231
+ }
232
+
233
+ const parsed = parseImageGenerationResponse(payload);
234
+ if (!parsed.ok) {
235
+ return {
236
+ ok: false,
237
+ reason: parsed.reason,
238
+ status: response.status,
239
+ errorMessage: parsed.errorMessage,
240
+ };
241
+ }
242
+ return { ok: true, image: parsed.image, status: response.status };
243
+ }
244
+
245
+ export const _clientTest = {
246
+ buildRequestHeaders,
247
+ readBoundedBody,
248
+ };
@@ -1,18 +1,18 @@
1
- import type { ImageGenerationConfig } from "../types";
2
- import { IMAGE_GENERATION_CAPABLE_APIS, type ImageGenerationModel } from "./types";
3
-
4
- /**
5
- * The hosted `image_generation` tool is a capability of the Responses API rather than of a
6
- * specific model, so support is decided by API alone. `enabled` is the single opt-in switch
7
- * and defaults to off because every successful provider request may incur a charge.
8
- */
9
- export function isImageGenerationEnabledForModel(
10
- model: ImageGenerationModel | undefined,
11
- config: ImageGenerationConfig,
12
- ): boolean {
13
- return (
14
- config.enabled &&
15
- typeof model?.api === "string" &&
16
- (IMAGE_GENERATION_CAPABLE_APIS as readonly string[]).includes(model.api)
17
- );
18
- }
1
+ import type { ImageGenerationConfig } from "../types";
2
+ import { IMAGE_GENERATION_CAPABLE_APIS, type ImageGenerationModel } from "./types";
3
+
4
+ /**
5
+ * The hosted `image_generation` tool is a capability of the Responses API rather than of a
6
+ * specific model, so support is decided by API alone. `enabled` is the single opt-in switch
7
+ * and defaults to off because every successful provider request may incur a charge.
8
+ */
9
+ export function isImageGenerationEnabledForModel(
10
+ model: ImageGenerationModel | undefined,
11
+ config: ImageGenerationConfig,
12
+ ): boolean {
13
+ return (
14
+ config.enabled &&
15
+ typeof model?.api === "string" &&
16
+ (IMAGE_GENERATION_CAPABLE_APIS as readonly string[]).includes(model.api)
17
+ );
18
+ }