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,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
+ };
@@ -1,17 +1,17 @@
1
- export type ExactModelScopeModel = {
2
- provider?: string;
3
- id?: string;
4
- };
5
-
6
- export function getExactModelKey(model: ExactModelScopeModel | undefined): string | undefined {
7
- if (!model?.provider || !model.id) return undefined;
8
- return `${model.provider}/${model.id}`;
9
- }
10
-
11
- export function isExactModelAllowed(
12
- model: ExactModelScopeModel | undefined,
13
- allowlist: readonly string[],
14
- ): boolean {
15
- const modelKey = getExactModelKey(model);
16
- return modelKey !== undefined && allowlist.includes(modelKey);
17
- }
1
+ export type ExactModelScopeModel = {
2
+ provider?: string;
3
+ id?: string;
4
+ };
5
+
6
+ export function getExactModelKey(model: ExactModelScopeModel | undefined): string | undefined {
7
+ if (!model?.provider || !model.id) return undefined;
8
+ return `${model.provider}/${model.id}`;
9
+ }
10
+
11
+ export function isExactModelAllowed(
12
+ model: ExactModelScopeModel | undefined,
13
+ allowlist: readonly string[],
14
+ ): boolean {
15
+ const modelKey = getExactModelKey(model);
16
+ return modelKey !== undefined && allowlist.includes(modelKey);
17
+ }