pi-openai-toolkit 0.14.4 → 0.14.6

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 (43) hide show
  1. package/README.md +119 -147
  2. package/README.zh.md +119 -151
  3. package/docs/internals.md +42 -21
  4. package/package.json +127 -127
  5. package/src/auto-mode/classifier.ts +165 -165
  6. package/src/auto-mode/policy.ts +146 -146
  7. package/src/auto-mode/prompt.ts +279 -279
  8. package/src/auto-mode/reviewer.ts +325 -325
  9. package/src/auto-mode/transcript.ts +133 -133
  10. package/src/auto-mode/types.ts +137 -137
  11. package/src/codex-astra/effort-planner.ts +175 -175
  12. package/src/codex-astra/extension.ts +122 -122
  13. package/src/config.ts +621 -592
  14. package/src/context-management/codex-provider.ts +260 -260
  15. package/src/context-management/history-notes.ts +357 -325
  16. package/src/context-management/messages.ts +112 -106
  17. package/src/context-management/namespace-tools.ts +426 -426
  18. package/src/context-management/tool-contract.ts +23 -23
  19. package/src/context-management/tools.ts +306 -311
  20. package/src/context-management/types.ts +168 -161
  21. package/src/context-management/window-budget.ts +83 -83
  22. package/src/context-management/window-manager.ts +60 -51
  23. package/src/context-management/window-request.ts +125 -125
  24. package/src/debug.ts +1 -1
  25. package/src/extension-runtime.ts +34 -9
  26. package/src/image-generation/artifacts.ts +237 -237
  27. package/src/image-generation/client.ts +248 -248
  28. package/src/image-generation/eligibility.ts +18 -18
  29. package/src/image-generation/extension.ts +19 -2
  30. package/src/image-generation/protocol.ts +473 -405
  31. package/src/image-generation/references.ts +194 -194
  32. package/src/image-generation/render.ts +252 -252
  33. package/src/image-generation/service.ts +249 -243
  34. package/src/image-generation/types.ts +189 -184
  35. package/src/model-scope.ts +17 -17
  36. package/src/provider-headers.ts +23 -23
  37. package/src/responses-headers.ts +173 -108
  38. package/src/runtime.ts +513 -477
  39. package/src/types.ts +11 -0
  40. package/src/web-search/extension.ts +80 -80
  41. package/src/web-search/payload.ts +96 -96
  42. package/src/web-search/prompt.ts +20 -20
  43. package/src/web-search/types.ts +45 -45
@@ -1,252 +1,252 @@
1
- import { readFileSync, realpathSync, statSync } from "node:fs";
2
- import { isAbsolute, relative, resolve, sep } from "node:path";
3
- import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
- import {
5
- getAgentDir,
6
- type Theme,
7
- type ToolRenderResultOptions,
8
- } from "@earendil-works/pi-coding-agent";
9
- import {
10
- Container,
11
- getCapabilities,
12
- Image,
13
- Spacer,
14
- Text,
15
- type Component,
16
- } from "@earendil-works/pi-tui";
17
- import { getGeneratedImagesRoot } from "./artifacts";
18
- import { isValidPng, readPngDimensions } from "./protocol";
19
- import {
20
- IMAGE_GENERATION_MIME_TYPE,
21
- MAX_GENERATED_IMAGE_BYTES,
22
- isImageGenerationDetails,
23
- type GenerateImageParams,
24
- type ImageGenerationDetails,
25
- } from "./types";
26
-
27
- export type ImageGenerationRenderState = Record<string, never>;
28
-
29
- export type ImageGenerationRenderContext = {
30
- lastComponent: Component | undefined;
31
- showImages: boolean;
32
- isError: boolean;
33
- isPartial: boolean;
34
- };
35
-
36
- export type ImageRendererDependencies = {
37
- getAgentDir: () => string;
38
- getCapabilities: typeof getCapabilities;
39
- readFileSync: typeof readFileSync;
40
- realpathSync: typeof realpathSync;
41
- statSync: typeof statSync;
42
- };
43
-
44
- const DEFAULT_RENDER_DEPS: ImageRendererDependencies = {
45
- getAgentDir,
46
- getCapabilities,
47
- readFileSync,
48
- realpathSync,
49
- statSync,
50
- };
51
-
52
- function isPathInsideOrEqual(root: string, candidate: string): boolean {
53
- const rel = relative(root, candidate);
54
- return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
55
- }
56
-
57
- function resultText(result: AgentToolResult<ImageGenerationDetails>): string {
58
- return result.content
59
- .filter((block): block is { type: "text"; text: string } => block.type === "text")
60
- .map((block) => block.text)
61
- .join("\n")
62
- .trim();
63
- }
64
-
65
- class DiskBackedImageResult extends Container {
66
- readonly artifactPath?: string;
67
- readonly fileSize?: number;
68
- readonly mtimeMs?: number;
69
- readonly showImages: boolean;
70
- readonly textValue: string;
71
-
72
- constructor(args: {
73
- text: string;
74
- artifactPath?: string;
75
- fileSize?: number;
76
- mtimeMs?: number;
77
- showImages: boolean;
78
- base64?: string;
79
- theme: Theme;
80
- previewWarning?: string;
81
- }) {
82
- super();
83
- this.artifactPath = args.artifactPath;
84
- this.fileSize = args.fileSize;
85
- this.mtimeMs = args.mtimeMs;
86
- this.showImages = args.showImages;
87
- this.textValue = `${args.text}\n${args.previewWarning ?? ""}`;
88
- this.addChild(new Text(args.theme.fg("toolOutput", args.text), 0, 0));
89
- if (args.previewWarning) {
90
- this.addChild(new Text(args.theme.fg("muted", args.previewWarning), 0, 0));
91
- }
92
- if (args.base64) {
93
- this.addChild(new Spacer(1));
94
- this.addChild(
95
- new Image(
96
- args.base64,
97
- IMAGE_GENERATION_MIME_TYPE,
98
- { fallbackColor: (text) => args.theme.fg("toolOutput", text) },
99
- { maxWidthCells: 60, filename: args.artifactPath },
100
- ),
101
- );
102
- }
103
- }
104
-
105
- matches(args: {
106
- text: string;
107
- artifactPath?: string;
108
- fileSize?: number;
109
- mtimeMs?: number;
110
- showImages: boolean;
111
- previewWarning?: string;
112
- }): boolean {
113
- return (
114
- this.textValue === `${args.text}\n${args.previewWarning ?? ""}` &&
115
- this.artifactPath === args.artifactPath &&
116
- this.fileSize === args.fileSize &&
117
- this.mtimeMs === args.mtimeMs &&
118
- this.showImages === args.showImages
119
- );
120
- }
121
- }
122
-
123
- function textOnly(args: {
124
- text: string;
125
- theme: Theme;
126
- context: ImageGenerationRenderContext;
127
- previewWarning?: string;
128
- }): Component {
129
- const previous = args.context.lastComponent;
130
- if (
131
- previous instanceof DiskBackedImageResult &&
132
- previous.matches({
133
- text: args.text,
134
- showImages: false,
135
- previewWarning: args.previewWarning,
136
- })
137
- ) {
138
- return previous;
139
- }
140
- return new DiskBackedImageResult({
141
- text: args.text,
142
- showImages: false,
143
- theme: args.theme,
144
- previewWarning: args.previewWarning,
145
- });
146
- }
147
-
148
- export function createImageGenerationResultRenderer(
149
- deps: ImageRendererDependencies = DEFAULT_RENDER_DEPS,
150
- ): (
151
- result: AgentToolResult<ImageGenerationDetails>,
152
- options: ToolRenderResultOptions,
153
- theme: Theme,
154
- context: ImageGenerationRenderContext,
155
- ) => Component {
156
- return (result, _options, theme, context) => {
157
- const text = resultText(result) || "Image generation finished.";
158
- if (
159
- context.isError ||
160
- context.isPartial ||
161
- !context.showImages ||
162
- !isImageGenerationDetails(result.details) ||
163
- !deps.getCapabilities().images
164
- ) {
165
- return textOnly({ text, theme, context });
166
- }
167
-
168
- const artifactPath = resolve(result.details.artifactPath);
169
- let root: string;
170
- let realArtifact: string;
171
- let info: ReturnType<typeof statSync>;
172
- try {
173
- root = deps.realpathSync(getGeneratedImagesRoot(deps.getAgentDir()));
174
- realArtifact = deps.realpathSync(artifactPath);
175
- info = deps.statSync(realArtifact);
176
- } catch {
177
- return textOnly({
178
- text,
179
- theme,
180
- context,
181
- previewWarning: "Image preview unavailable: the saved artifact is missing.",
182
- });
183
- }
184
-
185
- if (
186
- !isPathInsideOrEqual(root, realArtifact) ||
187
- !info.isFile() ||
188
- info.size <= 0 ||
189
- info.size > MAX_GENERATED_IMAGE_BYTES
190
- ) {
191
- return textOnly({
192
- text,
193
- theme,
194
- context,
195
- previewWarning: "Image preview unavailable: the artifact path or file is not safe.",
196
- });
197
- }
198
-
199
- const previous = context.lastComponent;
200
- if (
201
- previous instanceof DiskBackedImageResult &&
202
- previous.matches({
203
- text,
204
- artifactPath: realArtifact,
205
- fileSize: info.size,
206
- mtimeMs: info.mtimeMs,
207
- showImages: true,
208
- })
209
- ) {
210
- return previous;
211
- }
212
-
213
- let bytes: Buffer;
214
- try {
215
- bytes = deps.readFileSync(realArtifact);
216
- } catch {
217
- return textOnly({
218
- text,
219
- theme,
220
- context,
221
- previewWarning: "Image preview unavailable: the artifact could not be read.",
222
- });
223
- }
224
- if (bytes.length !== info.size || !isValidPng(bytes) || !readPngDimensions(bytes)) {
225
- bytes.fill(0);
226
- return textOnly({
227
- text,
228
- theme,
229
- context,
230
- previewWarning: "Image preview unavailable: the artifact is not a valid PNG.",
231
- });
232
- }
233
- const base64 = bytes.toString("base64");
234
- bytes.fill(0);
235
- return new DiskBackedImageResult({
236
- text,
237
- artifactPath: realArtifact,
238
- fileSize: info.size,
239
- mtimeMs: info.mtimeMs,
240
- showImages: true,
241
- base64,
242
- theme,
243
- });
244
- };
245
- }
246
-
247
- export const renderImageGenerationResult = createImageGenerationResultRenderer();
248
-
249
- export const _renderTest = {
250
- DiskBackedImageResult,
251
- isPathInsideOrEqual,
252
- };
1
+ import { readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, relative, resolve, sep } from "node:path";
3
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
+ import {
5
+ getAgentDir,
6
+ type Theme,
7
+ type ToolRenderResultOptions,
8
+ } from "@earendil-works/pi-coding-agent";
9
+ import {
10
+ Container,
11
+ getCapabilities,
12
+ Image,
13
+ Spacer,
14
+ Text,
15
+ type Component,
16
+ } from "@earendil-works/pi-tui";
17
+ import { getGeneratedImagesRoot } from "./artifacts";
18
+ import { isValidPng, readPngDimensions } from "./protocol";
19
+ import {
20
+ IMAGE_GENERATION_MIME_TYPE,
21
+ MAX_GENERATED_IMAGE_BYTES,
22
+ isImageGenerationDetails,
23
+ type GenerateImageParams,
24
+ type ImageGenerationDetails,
25
+ } from "./types";
26
+
27
+ export type ImageGenerationRenderState = Record<string, never>;
28
+
29
+ export type ImageGenerationRenderContext = {
30
+ lastComponent: Component | undefined;
31
+ showImages: boolean;
32
+ isError: boolean;
33
+ isPartial: boolean;
34
+ };
35
+
36
+ export type ImageRendererDependencies = {
37
+ getAgentDir: () => string;
38
+ getCapabilities: typeof getCapabilities;
39
+ readFileSync: typeof readFileSync;
40
+ realpathSync: typeof realpathSync;
41
+ statSync: typeof statSync;
42
+ };
43
+
44
+ const DEFAULT_RENDER_DEPS: ImageRendererDependencies = {
45
+ getAgentDir,
46
+ getCapabilities,
47
+ readFileSync,
48
+ realpathSync,
49
+ statSync,
50
+ };
51
+
52
+ function isPathInsideOrEqual(root: string, candidate: string): boolean {
53
+ const rel = relative(root, candidate);
54
+ return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
55
+ }
56
+
57
+ function resultText(result: AgentToolResult<ImageGenerationDetails>): string {
58
+ return result.content
59
+ .filter((block): block is { type: "text"; text: string } => block.type === "text")
60
+ .map((block) => block.text)
61
+ .join("\n")
62
+ .trim();
63
+ }
64
+
65
+ class DiskBackedImageResult extends Container {
66
+ readonly artifactPath?: string;
67
+ readonly fileSize?: number;
68
+ readonly mtimeMs?: number;
69
+ readonly showImages: boolean;
70
+ readonly textValue: string;
71
+
72
+ constructor(args: {
73
+ text: string;
74
+ artifactPath?: string;
75
+ fileSize?: number;
76
+ mtimeMs?: number;
77
+ showImages: boolean;
78
+ base64?: string;
79
+ theme: Theme;
80
+ previewWarning?: string;
81
+ }) {
82
+ super();
83
+ this.artifactPath = args.artifactPath;
84
+ this.fileSize = args.fileSize;
85
+ this.mtimeMs = args.mtimeMs;
86
+ this.showImages = args.showImages;
87
+ this.textValue = `${args.text}\n${args.previewWarning ?? ""}`;
88
+ this.addChild(new Text(args.theme.fg("toolOutput", args.text), 0, 0));
89
+ if (args.previewWarning) {
90
+ this.addChild(new Text(args.theme.fg("muted", args.previewWarning), 0, 0));
91
+ }
92
+ if (args.base64) {
93
+ this.addChild(new Spacer(1));
94
+ this.addChild(
95
+ new Image(
96
+ args.base64,
97
+ IMAGE_GENERATION_MIME_TYPE,
98
+ { fallbackColor: (text) => args.theme.fg("toolOutput", text) },
99
+ { maxWidthCells: 60, filename: args.artifactPath },
100
+ ),
101
+ );
102
+ }
103
+ }
104
+
105
+ matches(args: {
106
+ text: string;
107
+ artifactPath?: string;
108
+ fileSize?: number;
109
+ mtimeMs?: number;
110
+ showImages: boolean;
111
+ previewWarning?: string;
112
+ }): boolean {
113
+ return (
114
+ this.textValue === `${args.text}\n${args.previewWarning ?? ""}` &&
115
+ this.artifactPath === args.artifactPath &&
116
+ this.fileSize === args.fileSize &&
117
+ this.mtimeMs === args.mtimeMs &&
118
+ this.showImages === args.showImages
119
+ );
120
+ }
121
+ }
122
+
123
+ function textOnly(args: {
124
+ text: string;
125
+ theme: Theme;
126
+ context: ImageGenerationRenderContext;
127
+ previewWarning?: string;
128
+ }): Component {
129
+ const previous = args.context.lastComponent;
130
+ if (
131
+ previous instanceof DiskBackedImageResult &&
132
+ previous.matches({
133
+ text: args.text,
134
+ showImages: false,
135
+ previewWarning: args.previewWarning,
136
+ })
137
+ ) {
138
+ return previous;
139
+ }
140
+ return new DiskBackedImageResult({
141
+ text: args.text,
142
+ showImages: false,
143
+ theme: args.theme,
144
+ previewWarning: args.previewWarning,
145
+ });
146
+ }
147
+
148
+ export function createImageGenerationResultRenderer(
149
+ deps: ImageRendererDependencies = DEFAULT_RENDER_DEPS,
150
+ ): (
151
+ result: AgentToolResult<ImageGenerationDetails>,
152
+ options: ToolRenderResultOptions,
153
+ theme: Theme,
154
+ context: ImageGenerationRenderContext,
155
+ ) => Component {
156
+ return (result, _options, theme, context) => {
157
+ const text = resultText(result) || "Image generation finished.";
158
+ if (
159
+ context.isError ||
160
+ context.isPartial ||
161
+ !context.showImages ||
162
+ !isImageGenerationDetails(result.details) ||
163
+ !deps.getCapabilities().images
164
+ ) {
165
+ return textOnly({ text, theme, context });
166
+ }
167
+
168
+ const artifactPath = resolve(result.details.artifactPath);
169
+ let root: string;
170
+ let realArtifact: string;
171
+ let info: ReturnType<typeof statSync>;
172
+ try {
173
+ root = deps.realpathSync(getGeneratedImagesRoot(deps.getAgentDir()));
174
+ realArtifact = deps.realpathSync(artifactPath);
175
+ info = deps.statSync(realArtifact);
176
+ } catch {
177
+ return textOnly({
178
+ text,
179
+ theme,
180
+ context,
181
+ previewWarning: "Image preview unavailable: the saved artifact is missing.",
182
+ });
183
+ }
184
+
185
+ if (
186
+ !isPathInsideOrEqual(root, realArtifact) ||
187
+ !info.isFile() ||
188
+ info.size <= 0 ||
189
+ info.size > MAX_GENERATED_IMAGE_BYTES
190
+ ) {
191
+ return textOnly({
192
+ text,
193
+ theme,
194
+ context,
195
+ previewWarning: "Image preview unavailable: the artifact path or file is not safe.",
196
+ });
197
+ }
198
+
199
+ const previous = context.lastComponent;
200
+ if (
201
+ previous instanceof DiskBackedImageResult &&
202
+ previous.matches({
203
+ text,
204
+ artifactPath: realArtifact,
205
+ fileSize: info.size,
206
+ mtimeMs: info.mtimeMs,
207
+ showImages: true,
208
+ })
209
+ ) {
210
+ return previous;
211
+ }
212
+
213
+ let bytes: Buffer;
214
+ try {
215
+ bytes = deps.readFileSync(realArtifact);
216
+ } catch {
217
+ return textOnly({
218
+ text,
219
+ theme,
220
+ context,
221
+ previewWarning: "Image preview unavailable: the artifact could not be read.",
222
+ });
223
+ }
224
+ if (bytes.length !== info.size || !isValidPng(bytes) || !readPngDimensions(bytes)) {
225
+ bytes.fill(0);
226
+ return textOnly({
227
+ text,
228
+ theme,
229
+ context,
230
+ previewWarning: "Image preview unavailable: the artifact is not a valid PNG.",
231
+ });
232
+ }
233
+ const base64 = bytes.toString("base64");
234
+ bytes.fill(0);
235
+ return new DiskBackedImageResult({
236
+ text,
237
+ artifactPath: realArtifact,
238
+ fileSize: info.size,
239
+ mtimeMs: info.mtimeMs,
240
+ showImages: true,
241
+ base64,
242
+ theme,
243
+ });
244
+ };
245
+ }
246
+
247
+ export const renderImageGenerationResult = createImageGenerationResultRenderer();
248
+
249
+ export const _renderTest = {
250
+ DiskBackedImageResult,
251
+ isPathInsideOrEqual,
252
+ };