pi-openai-toolkit 0.8.0 → 0.10.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 +84 -13
- package/README_zh.md +84 -13
- package/extensions/auto-mode.ts +1 -0
- package/package.json +13 -3
- package/src/auto-mode/classifier.ts +165 -0
- package/src/auto-mode/extension.ts +530 -0
- package/src/auto-mode/policy.ts +146 -0
- package/src/auto-mode/prompt.ts +279 -0
- package/src/auto-mode/reviewer.ts +325 -0
- package/src/auto-mode/transcript.ts +133 -0
- package/src/auto-mode/types.ts +137 -0
- package/src/config.ts +255 -16
- package/src/extension-runtime.ts +12 -4
- package/src/image-generation/eligibility.ts +7 -7
- package/src/image-generation/extension.ts +160 -160
- package/src/native-fallback.ts +19 -6
- package/src/types.ts +107 -10
- package/tsconfig.check.json +5 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { isExactModelAllowed } from "../model-scope";
|
|
2
1
|
import type { ImageGenerationConfig } from "../types";
|
|
3
|
-
import {
|
|
4
|
-
IMAGE_GENERATION_CAPABLE_APIS,
|
|
5
|
-
type ImageGenerationModel,
|
|
6
|
-
} from "./types";
|
|
2
|
+
import { IMAGE_GENERATION_CAPABLE_APIS, type ImageGenerationModel } from "./types";
|
|
7
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
|
+
*/
|
|
8
9
|
export function isImageGenerationEnabledForModel(
|
|
9
10
|
model: ImageGenerationModel | undefined,
|
|
10
11
|
config: ImageGenerationConfig,
|
|
@@ -12,7 +13,6 @@ export function isImageGenerationEnabledForModel(
|
|
|
12
13
|
return (
|
|
13
14
|
config.enabled &&
|
|
14
15
|
typeof model?.api === "string" &&
|
|
15
|
-
(IMAGE_GENERATION_CAPABLE_APIS as readonly string[]).includes(model.api)
|
|
16
|
-
isExactModelAllowed(model, config.models)
|
|
16
|
+
(IMAGE_GENERATION_CAPABLE_APIS as readonly string[]).includes(model.api)
|
|
17
17
|
);
|
|
18
18
|
}
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import { StringEnum, Type, type Static } from "@earendil-works/pi-ai";
|
|
2
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { loadToolkitConfig } from "../config";
|
|
4
|
-
import { executeImageGeneration } from "./service";
|
|
5
|
-
import { isImageGenerationEnabledForModel } from "./eligibility";
|
|
6
|
-
import { renderImageGenerationResult } from "./render";
|
|
7
|
-
import {
|
|
8
|
-
IMAGE_GENERATION_QUALITIES,
|
|
9
|
-
IMAGE_GENERATION_SIZES,
|
|
10
|
-
IMAGE_GENERATION_TOOL_NAME,
|
|
11
|
-
MAX_IMAGE_PATH_CHARS,
|
|
12
|
-
MAX_IMAGE_PROMPT_CHARS,
|
|
13
|
-
MAX_REFERENCE_IMAGE_COUNT,
|
|
14
|
-
ImageGenerationError,
|
|
15
|
-
sanitizeImageDiagnostic,
|
|
16
|
-
type ImageGenerationDetails,
|
|
17
|
-
} from "./types";
|
|
18
|
-
|
|
19
|
-
const registeredApis = new WeakSet<object>();
|
|
20
|
-
|
|
21
|
-
const GenerateImageParameters = Type.Object(
|
|
22
|
-
{
|
|
23
|
-
prompt: Type.String({
|
|
24
|
-
minLength: 1,
|
|
25
|
-
maxLength: MAX_IMAGE_PROMPT_CHARS,
|
|
26
|
-
description: "Image generation or edit prompt. Preserve the user's requested subject and constraints.",
|
|
27
|
-
}),
|
|
28
|
-
referenceImagePaths: Type.Optional(
|
|
29
|
-
Type.Union(
|
|
30
|
-
[
|
|
31
|
-
Type.Null(),
|
|
32
|
-
Type.Array(Type.String({ minLength: 1, maxLength: MAX_IMAGE_PATH_CHARS }), {
|
|
33
|
-
minItems: 1,
|
|
34
|
-
maxItems: MAX_REFERENCE_IMAGE_COUNT,
|
|
35
|
-
}),
|
|
36
|
-
],
|
|
37
|
-
{
|
|
38
|
-
description:
|
|
39
|
-
"Use null when the user did not explicitly identify reference images; otherwise provide one to five user-identified local image paths.",
|
|
40
|
-
},
|
|
41
|
-
),
|
|
42
|
-
),
|
|
43
|
-
outputPath: Type.Optional(
|
|
44
|
-
Type.Union(
|
|
45
|
-
[
|
|
46
|
-
Type.Null(),
|
|
47
|
-
Type.String({
|
|
48
|
-
minLength: 1,
|
|
49
|
-
maxLength: MAX_IMAGE_PATH_CHARS,
|
|
50
|
-
}),
|
|
51
|
-
],
|
|
52
|
-
{
|
|
53
|
-
description:
|
|
54
|
-
"Use null when the user did not explicitly request a destination; otherwise provide the requested explicit .png file path.",
|
|
55
|
-
},
|
|
56
|
-
),
|
|
57
|
-
),
|
|
58
|
-
size: Type.Optional(
|
|
59
|
-
StringEnum(IMAGE_GENERATION_SIZES, {
|
|
60
|
-
description: "Requested PNG dimensions, or auto.",
|
|
61
|
-
}),
|
|
62
|
-
),
|
|
63
|
-
quality: Type.Optional(
|
|
64
|
-
StringEnum(IMAGE_GENERATION_QUALITIES, {
|
|
65
|
-
description: "Requested image quality, or auto.",
|
|
66
|
-
}),
|
|
67
|
-
),
|
|
68
|
-
},
|
|
69
|
-
{ additionalProperties: false },
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
type GenerateImageToolParams = Static<typeof GenerateImageParameters>;
|
|
73
|
-
|
|
74
|
-
function syncImageGenerationTool(
|
|
75
|
-
pi: ExtensionAPI,
|
|
76
|
-
model: Parameters<typeof isImageGenerationEnabledForModel>[0],
|
|
77
|
-
config: Parameters<typeof isImageGenerationEnabledForModel>[1],
|
|
78
|
-
): void {
|
|
79
|
-
const eligible = isImageGenerationEnabledForModel(model, config);
|
|
80
|
-
const activeTools = pi.getActiveTools();
|
|
81
|
-
const active = activeTools.includes(IMAGE_GENERATION_TOOL_NAME);
|
|
82
|
-
if (eligible && !active) {
|
|
83
|
-
pi.setActiveTools([...activeTools, IMAGE_GENERATION_TOOL_NAME]);
|
|
84
|
-
} else if (!eligible && active) {
|
|
85
|
-
pi.setActiveTools(activeTools.filter((name) => name !== IMAGE_GENERATION_TOOL_NAME));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function registerImageGenerationExtension(
|
|
90
|
-
pi: ExtensionAPI,
|
|
91
|
-
loadConfig: typeof loadToolkitConfig = loadToolkitConfig,
|
|
92
|
-
executeImage: typeof executeImageGeneration = executeImageGeneration,
|
|
93
|
-
): void {
|
|
94
|
-
if (registeredApis.has(pi)) return;
|
|
95
|
-
registeredApis.add(pi);
|
|
96
|
-
|
|
97
|
-
pi.registerTool<typeof GenerateImageParameters, ImageGenerationDetails, Record<string, never>>({
|
|
98
|
-
name: IMAGE_GENERATION_TOOL_NAME,
|
|
99
|
-
label: "OpenAI Generate Image",
|
|
100
|
-
description:
|
|
101
|
-
"Generate a PNG image, or edit from one to five user-approved local reference images, through the current
|
|
102
|
-
promptSnippet: "Generate or edit PNG images through the current
|
|
103
|
-
promptGuidelines: [
|
|
104
|
-
"Use openai_generate_image when the user explicitly asks to create, draw, render, or edit a raster image.",
|
|
105
|
-
"Do not call openai_generate_image speculatively: it consumes the user's provider or gateway image quota.",
|
|
106
|
-
"Keep the image prompt faithful to the user's requested subject and constraints; do not invent unrequested style details.",
|
|
107
|
-
"Set referenceImagePaths to null unless the user explicitly identified local files; never invent paths or placeholder strings, and remember upload requires user approval.",
|
|
108
|
-
"Set outputPath to null unless the user explicitly asks for a destination; never invent a destination or placeholder string, and otherwise use the default Pi agent artifact.",
|
|
109
|
-
"Do not substitute Python, browser automation, shell scripts, or unrelated image tools for an eligible image request.",
|
|
110
|
-
],
|
|
111
|
-
parameters: GenerateImageParameters,
|
|
112
|
-
executionMode: "sequential",
|
|
113
|
-
async execute(toolCallId, params: GenerateImageToolParams, signal, _onUpdate, ctx) {
|
|
114
|
-
try {
|
|
115
|
-
const result = await executeImage({
|
|
116
|
-
params,
|
|
117
|
-
toolCallId,
|
|
118
|
-
signal,
|
|
119
|
-
ctx,
|
|
120
|
-
});
|
|
121
|
-
return {
|
|
122
|
-
content: [{ type: "text", text: result.text }],
|
|
123
|
-
details: result.details,
|
|
124
|
-
};
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (error instanceof ImageGenerationError) {
|
|
127
|
-
throw new Error(error.message);
|
|
128
|
-
}
|
|
129
|
-
throw new Error(
|
|
130
|
-
sanitizeImageDiagnostic(error instanceof Error ? error.message : error, "Image generation failed."),
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
renderResult: renderImageGenerationResult,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
pi.on("session_start", (_event, ctx) => {
|
|
138
|
-
const { config } = loadConfig();
|
|
139
|
-
syncImageGenerationTool(pi, ctx.model, config.imageGeneration);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
pi.on("model_select", (event) => {
|
|
143
|
-
const { config } = loadConfig();
|
|
144
|
-
syncImageGenerationTool(pi, event.model, config.imageGeneration);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
pi.on("before_agent_start", (_event, ctx) => {
|
|
148
|
-
const { config } = loadConfig();
|
|
149
|
-
syncImageGenerationTool(pi, ctx.model, config.imageGeneration);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export default function imageGenerationExtension(pi: ExtensionAPI): void {
|
|
154
|
-
registerImageGenerationExtension(pi);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export const _extensionTest = {
|
|
158
|
-
GenerateImageParameters,
|
|
159
|
-
syncImageGenerationTool,
|
|
160
|
-
};
|
|
1
|
+
import { StringEnum, Type, type Static } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { loadToolkitConfig } from "../config";
|
|
4
|
+
import { executeImageGeneration } from "./service";
|
|
5
|
+
import { isImageGenerationEnabledForModel } from "./eligibility";
|
|
6
|
+
import { renderImageGenerationResult } from "./render";
|
|
7
|
+
import {
|
|
8
|
+
IMAGE_GENERATION_QUALITIES,
|
|
9
|
+
IMAGE_GENERATION_SIZES,
|
|
10
|
+
IMAGE_GENERATION_TOOL_NAME,
|
|
11
|
+
MAX_IMAGE_PATH_CHARS,
|
|
12
|
+
MAX_IMAGE_PROMPT_CHARS,
|
|
13
|
+
MAX_REFERENCE_IMAGE_COUNT,
|
|
14
|
+
ImageGenerationError,
|
|
15
|
+
sanitizeImageDiagnostic,
|
|
16
|
+
type ImageGenerationDetails,
|
|
17
|
+
} from "./types";
|
|
18
|
+
|
|
19
|
+
const registeredApis = new WeakSet<object>();
|
|
20
|
+
|
|
21
|
+
const GenerateImageParameters = Type.Object(
|
|
22
|
+
{
|
|
23
|
+
prompt: Type.String({
|
|
24
|
+
minLength: 1,
|
|
25
|
+
maxLength: MAX_IMAGE_PROMPT_CHARS,
|
|
26
|
+
description: "Image generation or edit prompt. Preserve the user's requested subject and constraints.",
|
|
27
|
+
}),
|
|
28
|
+
referenceImagePaths: Type.Optional(
|
|
29
|
+
Type.Union(
|
|
30
|
+
[
|
|
31
|
+
Type.Null(),
|
|
32
|
+
Type.Array(Type.String({ minLength: 1, maxLength: MAX_IMAGE_PATH_CHARS }), {
|
|
33
|
+
minItems: 1,
|
|
34
|
+
maxItems: MAX_REFERENCE_IMAGE_COUNT,
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
{
|
|
38
|
+
description:
|
|
39
|
+
"Use null when the user did not explicitly identify reference images; otherwise provide one to five user-identified local image paths.",
|
|
40
|
+
},
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
outputPath: Type.Optional(
|
|
44
|
+
Type.Union(
|
|
45
|
+
[
|
|
46
|
+
Type.Null(),
|
|
47
|
+
Type.String({
|
|
48
|
+
minLength: 1,
|
|
49
|
+
maxLength: MAX_IMAGE_PATH_CHARS,
|
|
50
|
+
}),
|
|
51
|
+
],
|
|
52
|
+
{
|
|
53
|
+
description:
|
|
54
|
+
"Use null when the user did not explicitly request a destination; otherwise provide the requested explicit .png file path.",
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
size: Type.Optional(
|
|
59
|
+
StringEnum(IMAGE_GENERATION_SIZES, {
|
|
60
|
+
description: "Requested PNG dimensions, or auto.",
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
quality: Type.Optional(
|
|
64
|
+
StringEnum(IMAGE_GENERATION_QUALITIES, {
|
|
65
|
+
description: "Requested image quality, or auto.",
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
{ additionalProperties: false },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
type GenerateImageToolParams = Static<typeof GenerateImageParameters>;
|
|
73
|
+
|
|
74
|
+
function syncImageGenerationTool(
|
|
75
|
+
pi: ExtensionAPI,
|
|
76
|
+
model: Parameters<typeof isImageGenerationEnabledForModel>[0],
|
|
77
|
+
config: Parameters<typeof isImageGenerationEnabledForModel>[1],
|
|
78
|
+
): void {
|
|
79
|
+
const eligible = isImageGenerationEnabledForModel(model, config);
|
|
80
|
+
const activeTools = pi.getActiveTools();
|
|
81
|
+
const active = activeTools.includes(IMAGE_GENERATION_TOOL_NAME);
|
|
82
|
+
if (eligible && !active) {
|
|
83
|
+
pi.setActiveTools([...activeTools, IMAGE_GENERATION_TOOL_NAME]);
|
|
84
|
+
} else if (!eligible && active) {
|
|
85
|
+
pi.setActiveTools(activeTools.filter((name) => name !== IMAGE_GENERATION_TOOL_NAME));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function registerImageGenerationExtension(
|
|
90
|
+
pi: ExtensionAPI,
|
|
91
|
+
loadConfig: typeof loadToolkitConfig = loadToolkitConfig,
|
|
92
|
+
executeImage: typeof executeImageGeneration = executeImageGeneration,
|
|
93
|
+
): void {
|
|
94
|
+
if (registeredApis.has(pi)) return;
|
|
95
|
+
registeredApis.add(pi);
|
|
96
|
+
|
|
97
|
+
pi.registerTool<typeof GenerateImageParameters, ImageGenerationDetails, Record<string, never>>({
|
|
98
|
+
name: IMAGE_GENERATION_TOOL_NAME,
|
|
99
|
+
label: "OpenAI Generate Image",
|
|
100
|
+
description:
|
|
101
|
+
"Generate a PNG image, or edit from one to five user-approved local reference images, through the current Responses-capable model and the hosted gpt-image-2 image_generation tool. This is a paid provider operation.",
|
|
102
|
+
promptSnippet: "Generate or edit PNG images through the current Responses-capable model and gpt-image-2.",
|
|
103
|
+
promptGuidelines: [
|
|
104
|
+
"Use openai_generate_image when the user explicitly asks to create, draw, render, or edit a raster image and the active model speaks a Responses API.",
|
|
105
|
+
"Do not call openai_generate_image speculatively: it consumes the user's provider or gateway image quota.",
|
|
106
|
+
"Keep the image prompt faithful to the user's requested subject and constraints; do not invent unrequested style details.",
|
|
107
|
+
"Set referenceImagePaths to null unless the user explicitly identified local files; never invent paths or placeholder strings, and remember upload requires user approval.",
|
|
108
|
+
"Set outputPath to null unless the user explicitly asks for a destination; never invent a destination or placeholder string, and otherwise use the default Pi agent artifact.",
|
|
109
|
+
"Do not substitute Python, browser automation, shell scripts, or unrelated image tools for an eligible image request.",
|
|
110
|
+
],
|
|
111
|
+
parameters: GenerateImageParameters,
|
|
112
|
+
executionMode: "sequential",
|
|
113
|
+
async execute(toolCallId, params: GenerateImageToolParams, signal, _onUpdate, ctx) {
|
|
114
|
+
try {
|
|
115
|
+
const result = await executeImage({
|
|
116
|
+
params,
|
|
117
|
+
toolCallId,
|
|
118
|
+
signal,
|
|
119
|
+
ctx,
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: result.text }],
|
|
123
|
+
details: result.details,
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error instanceof ImageGenerationError) {
|
|
127
|
+
throw new Error(error.message);
|
|
128
|
+
}
|
|
129
|
+
throw new Error(
|
|
130
|
+
sanitizeImageDiagnostic(error instanceof Error ? error.message : error, "Image generation failed."),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
renderResult: renderImageGenerationResult,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
pi.on("session_start", (_event, ctx) => {
|
|
138
|
+
const { config } = loadConfig();
|
|
139
|
+
syncImageGenerationTool(pi, ctx.model, config.imageGeneration);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
pi.on("model_select", (event) => {
|
|
143
|
+
const { config } = loadConfig();
|
|
144
|
+
syncImageGenerationTool(pi, event.model, config.imageGeneration);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
pi.on("before_agent_start", (_event, ctx) => {
|
|
148
|
+
const { config } = loadConfig();
|
|
149
|
+
syncImageGenerationTool(pi, ctx.model, config.imageGeneration);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default function imageGenerationExtension(pi: ExtensionAPI): void {
|
|
154
|
+
registerImageGenerationExtension(pi);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const _extensionTest = {
|
|
158
|
+
GenerateImageParameters,
|
|
159
|
+
syncImageGenerationTool,
|
|
160
|
+
};
|
package/src/native-fallback.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { CompactionConfig } from "./types";
|
|
|
12
12
|
export { parseModelSpec } from "./runtime";
|
|
13
13
|
|
|
14
14
|
export type NativeFallbackFailureReason =
|
|
15
|
+
| "disabled"
|
|
15
16
|
| "no-model-configured"
|
|
16
17
|
| "invalid-model-spec"
|
|
17
18
|
| "model-not-found"
|
|
@@ -53,23 +54,35 @@ function toErrorMessage(error: unknown): string {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
|
-
* Run pi's native compaction method with
|
|
57
|
+
* Run pi's native compaction method with a caller-resolved summary model.
|
|
57
58
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
59
|
+
* The two compaction chains stay separate: the caller passes `remoteCompactModel` only when a
|
|
60
|
+
* remote v2 request was actually attempted and failed, and `nativeFallback.model` only when the
|
|
61
|
+
* active model cannot use remote v2 at all. This function never inspects `remoteCompactModel`
|
|
62
|
+
* itself, so a model that cannot compact remotely can never inherit the remote producer.
|
|
63
|
+
*
|
|
64
|
+
* Only the "resolved model differs from the current one" case is handled here: when the feature
|
|
65
|
+
* is disabled, nothing is resolved, or it equals the current model, the caller returns undefined
|
|
66
|
+
* from session_before_compact so pi runs the same native path itself, keeping its internal
|
|
61
67
|
* streamFn/thinkingLevel wiring.
|
|
62
68
|
*/
|
|
63
69
|
export async function runNativeFallbackCompaction(args: {
|
|
64
70
|
ctx: ExtensionContext;
|
|
65
71
|
event: SessionBeforeCompactEvent;
|
|
66
72
|
config: CompactionConfig;
|
|
73
|
+
/** Model spec supplied by the caller for this specific fallback reason. */
|
|
74
|
+
modelSpec?: string;
|
|
67
75
|
compactFn?: NativeCompactFn;
|
|
68
76
|
}): Promise<NativeFallbackResult> {
|
|
69
77
|
const { ctx, event, config } = args;
|
|
70
78
|
const compactFn = args.compactFn ?? compact;
|
|
79
|
+
const nativeFallback = config.nativeFallback;
|
|
80
|
+
|
|
81
|
+
if (!nativeFallback.enabled) {
|
|
82
|
+
return { ok: false, reason: "disabled" };
|
|
83
|
+
}
|
|
71
84
|
|
|
72
|
-
const spec =
|
|
85
|
+
const spec = args.modelSpec?.trim() ?? "";
|
|
73
86
|
if (!spec) {
|
|
74
87
|
return { ok: false, reason: "no-model-configured" };
|
|
75
88
|
}
|
|
@@ -106,7 +119,7 @@ export async function runNativeFallbackCompaction(args: {
|
|
|
106
119
|
mergeProviderHeaders(auth.headers),
|
|
107
120
|
event.customInstructions,
|
|
108
121
|
event.signal,
|
|
109
|
-
|
|
122
|
+
nativeFallback.thinkingLevel,
|
|
110
123
|
undefined,
|
|
111
124
|
auth.env,
|
|
112
125
|
);
|
package/src/types.ts
CHANGED
|
@@ -5,6 +5,7 @@ export const TOOLKIT_ID = "pi-openai-toolkit";
|
|
|
5
5
|
export const COMPACTION_EXTENSION_ID = `${TOOLKIT_ID}:compaction`;
|
|
6
6
|
export const WEB_SEARCH_EXTENSION_ID = `${TOOLKIT_ID}:web-search`;
|
|
7
7
|
export const IMAGE_GENERATION_EXTENSION_ID = `${TOOLKIT_ID}:image-generation`;
|
|
8
|
+
export const AUTO_MODE_EXTENSION_ID = `${TOOLKIT_ID}:auto-mode`;
|
|
8
9
|
export const DEFAULT_ARTIFACT_ROOT = "~/.pi/agent/artifacts/pi-openai-toolkit/compaction";
|
|
9
10
|
export const REDACTED_VALUE = "[REDACTED]";
|
|
10
11
|
/**
|
|
@@ -39,6 +40,20 @@ export type DebugArtifactKind =
|
|
|
39
40
|
|
|
40
41
|
export type CompactionContinuationMode = "inline" | "followUp" | "off";
|
|
41
42
|
|
|
43
|
+
/** Native-method fallback compaction: which model runs pi's compact() and how deeply. */
|
|
44
|
+
export type NativeFallbackConfig = {
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* "provider/model-id" used for native-method fallback compaction when the active model
|
|
48
|
+
* cannot use remote v2 at all. A failed remote request is instead summarized by
|
|
49
|
+
* `remoteCompactModel`; the two sources never substitute for each other. Unset means the
|
|
50
|
+
* active model via pi's default path.
|
|
51
|
+
*/
|
|
52
|
+
model?: string;
|
|
53
|
+
/** Thinking level passed to pi's native compact() when the fallback model runs. */
|
|
54
|
+
thinkingLevel: ThinkingLevel;
|
|
55
|
+
};
|
|
56
|
+
|
|
42
57
|
export type AutoCompactionConfig = {
|
|
43
58
|
enabled: boolean;
|
|
44
59
|
continuation: CompactionContinuationMode;
|
|
@@ -58,13 +73,8 @@ export type CompactionConfig = {
|
|
|
58
73
|
* remains the checkpoint consumer and continues handling normal requests.
|
|
59
74
|
*/
|
|
60
75
|
remoteCompactModel?: string;
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
* or when the compact endpoint fails). Unset = current model via pi's default path.
|
|
64
|
-
*/
|
|
65
|
-
model?: string;
|
|
66
|
-
/** Thinking level passed to pi's native compact() when the fallback model runs. */
|
|
67
|
-
thinkingLevel: ThinkingLevel;
|
|
76
|
+
/** Native-method fallback compaction policy (non-Responses APIs, or when the compact endpoint fails). */
|
|
77
|
+
nativeFallback: NativeFallbackConfig;
|
|
68
78
|
/** Threshold-triggered compaction and same-tool-loop continuation policy. */
|
|
69
79
|
autoCompaction: AutoCompactionConfig;
|
|
70
80
|
/** Subset of RESPONSES_COMPACT_CAPABLE_APIS that should use remote compaction. */
|
|
@@ -85,14 +95,73 @@ export type WebSearchConfig = {
|
|
|
85
95
|
|
|
86
96
|
export type ImageGenerationConfig = {
|
|
87
97
|
enabled: boolean;
|
|
88
|
-
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** `side-effect` reviews bash/write/edit plus extras; `all` reviews every tool call. */
|
|
101
|
+
export type AutoModeGate = "side-effect" | "all";
|
|
102
|
+
|
|
103
|
+
export type AutoModeConfig = {
|
|
104
|
+
enabled: boolean;
|
|
105
|
+
/** Exact provider/model keys whose sessions may engage auto mode. */
|
|
89
106
|
models: string[];
|
|
107
|
+
/** "provider/model-id" that approves gated tool calls while auto mode is engaged. */
|
|
108
|
+
reviewerModel?: string;
|
|
109
|
+
gate: AutoModeGate;
|
|
110
|
+
/** Extra tool names reviewed on top of the side-effect default set. */
|
|
111
|
+
extraTools: string[];
|
|
112
|
+
/** Reviewer deadline in ms; expiry degrades to human confirmation or fail-closed. */
|
|
113
|
+
timeoutMs: number;
|
|
114
|
+
/** Give the reviewer a bounded transcript so it can judge user authorization. */
|
|
115
|
+
transcript: boolean;
|
|
116
|
+
/** Let the reviewer spend read-only tool calls establishing facts before deciding. */
|
|
117
|
+
evidenceTools: boolean;
|
|
118
|
+
maxEvidenceRounds: number;
|
|
119
|
+
/** Non-blocking trajectory pre-scorer that can satisfy ordinary gated calls. */
|
|
120
|
+
classifier: AutoModeClassifierConfig;
|
|
121
|
+
/** End a turn that keeps getting denied instead of negotiating forever. */
|
|
122
|
+
circuitBreaker: AutoModeCircuitBreakerConfig;
|
|
90
123
|
};
|
|
91
124
|
|
|
125
|
+
export type AutoModeClassifierConfig = {
|
|
126
|
+
enabled: boolean;
|
|
127
|
+
/** "provider/model-id"; falls back to `autoMode.reviewerModel` when unset. */
|
|
128
|
+
model?: string;
|
|
129
|
+
timeoutMs: number;
|
|
130
|
+
/** How many tool calls may elapse between scoring and use before a score is stale. */
|
|
131
|
+
maxLag: number;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export type AutoModeCircuitBreakerConfig = {
|
|
135
|
+
/** Consecutive reviewer denials in one turn that end the turn. 0 disables. */
|
|
136
|
+
consecutiveDenials: number;
|
|
137
|
+
/** Denials inside `windowSize` reviewed calls that end the turn. 0 disables. */
|
|
138
|
+
recentDenials: number;
|
|
139
|
+
windowSize: number;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const REVIEWER_TIMEOUT_MIN_MS = 1_000;
|
|
143
|
+
export const REVIEWER_TIMEOUT_MAX_MS = 120_000;
|
|
144
|
+
|
|
145
|
+
/** Read-only investigation rounds the reviewer may spend before it must answer. */
|
|
146
|
+
export const EVIDENCE_ROUNDS_MIN = 0;
|
|
147
|
+
export const EVIDENCE_ROUNDS_MAX = 8;
|
|
148
|
+
|
|
149
|
+
export const CLASSIFIER_MAX_LAG_MIN = 0;
|
|
150
|
+
export const CLASSIFIER_MAX_LAG_MAX = 20;
|
|
151
|
+
|
|
152
|
+
export const DEFAULT_CLASSIFIER_TIMEOUT_MS = 15_000;
|
|
153
|
+
|
|
154
|
+
/** Denial counts are small integers; 0 disables the matching trigger. */
|
|
155
|
+
export const BREAKER_LIMIT_MIN = 0;
|
|
156
|
+
export const BREAKER_LIMIT_MAX = 100;
|
|
157
|
+
export const BREAKER_WINDOW_MIN = 1;
|
|
158
|
+
export const BREAKER_WINDOW_MAX = 200;
|
|
159
|
+
|
|
92
160
|
export type ToolkitConfig = {
|
|
93
161
|
compaction: CompactionConfig;
|
|
94
162
|
webSearch: WebSearchConfig;
|
|
95
163
|
imageGeneration: ImageGenerationConfig;
|
|
164
|
+
autoMode: AutoModeConfig;
|
|
96
165
|
};
|
|
97
166
|
|
|
98
167
|
export type LoadedToolkitConfig = {
|
|
@@ -425,12 +494,17 @@ export const DEFAULT_AUTO_COMPACTION_CONFIG: AutoCompactionConfig = {
|
|
|
425
494
|
reserveTokens: undefined,
|
|
426
495
|
};
|
|
427
496
|
|
|
497
|
+
export const DEFAULT_NATIVE_FALLBACK_CONFIG: NativeFallbackConfig = {
|
|
498
|
+
enabled: true,
|
|
499
|
+
model: undefined,
|
|
500
|
+
thinkingLevel: "off",
|
|
501
|
+
};
|
|
502
|
+
|
|
428
503
|
export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
|
|
429
504
|
enabled: true,
|
|
430
505
|
allowCompactionContinuityBreak: false,
|
|
431
506
|
remoteCompactModel: undefined,
|
|
432
|
-
|
|
433
|
-
thinkingLevel: "off",
|
|
507
|
+
nativeFallback: { ...DEFAULT_NATIVE_FALLBACK_CONFIG },
|
|
434
508
|
autoCompaction: { ...DEFAULT_AUTO_COMPACTION_CONFIG },
|
|
435
509
|
responsesApis: [...RESPONSES_COMPACT_CAPABLE_APIS],
|
|
436
510
|
notifyOnLoad: false,
|
|
@@ -447,12 +521,35 @@ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchConfig = {
|
|
|
447
521
|
};
|
|
448
522
|
|
|
449
523
|
export const DEFAULT_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {
|
|
524
|
+
enabled: false,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
export const DEFAULT_AUTO_MODE_CONFIG: AutoModeConfig = {
|
|
450
528
|
enabled: true,
|
|
451
529
|
models: [],
|
|
530
|
+
reviewerModel: undefined,
|
|
531
|
+
gate: "side-effect",
|
|
532
|
+
extraTools: [],
|
|
533
|
+
timeoutMs: 30_000,
|
|
534
|
+
transcript: true,
|
|
535
|
+
evidenceTools: true,
|
|
536
|
+
maxEvidenceRounds: 3,
|
|
537
|
+
classifier: {
|
|
538
|
+
enabled: false,
|
|
539
|
+
model: undefined,
|
|
540
|
+
timeoutMs: DEFAULT_CLASSIFIER_TIMEOUT_MS,
|
|
541
|
+
maxLag: 2,
|
|
542
|
+
},
|
|
543
|
+
circuitBreaker: {
|
|
544
|
+
consecutiveDenials: 3,
|
|
545
|
+
recentDenials: 10,
|
|
546
|
+
windowSize: 50,
|
|
547
|
+
},
|
|
452
548
|
};
|
|
453
549
|
|
|
454
550
|
export const DEFAULT_TOOLKIT_CONFIG: ToolkitConfig = {
|
|
455
551
|
compaction: DEFAULT_COMPACTION_CONFIG,
|
|
456
552
|
webSearch: DEFAULT_WEB_SEARCH_CONFIG,
|
|
457
553
|
imageGeneration: DEFAULT_IMAGE_GENERATION_CONFIG,
|
|
554
|
+
autoMode: DEFAULT_AUTO_MODE_CONFIG,
|
|
458
555
|
};
|
package/tsconfig.check.json
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"extensions/compaction.ts",
|
|
16
16
|
"extensions/web-search.ts",
|
|
17
17
|
"extensions/image-generation.ts",
|
|
18
|
+
"extensions/auto-mode.ts",
|
|
18
19
|
"src/compact-client.ts",
|
|
19
20
|
"src/config.ts",
|
|
20
21
|
"src/debug.ts",
|
|
@@ -31,6 +32,10 @@
|
|
|
31
32
|
"src/image-generation/render.ts",
|
|
32
33
|
"src/image-generation/service.ts",
|
|
33
34
|
"src/image-generation/types.ts",
|
|
35
|
+
"src/auto-mode/extension.ts",
|
|
36
|
+
"src/auto-mode/policy.ts",
|
|
37
|
+
"src/auto-mode/reviewer.ts",
|
|
38
|
+
"src/auto-mode/types.ts",
|
|
34
39
|
"src/model-scope.ts",
|
|
35
40
|
"src/native-fallback.ts",
|
|
36
41
|
"src/payload-rewrite.ts",
|