pi-openai-toolkit 0.24.1 → 0.24.2

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 (41) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +83 -83
  3. package/README.md +1 -1
  4. package/README.zh.md +1 -1
  5. package/config.schema.json +264 -264
  6. package/docs/configuration.md +304 -304
  7. package/docs/internals.md +3 -1
  8. package/package.json +123 -123
  9. package/src/codex-fast/extension.ts +1 -1
  10. package/src/compact-client.ts +412 -412
  11. package/src/compaction-projection.ts +35 -35
  12. package/src/config/legacy.ts +469 -469
  13. package/src/config/migration.ts +115 -115
  14. package/src/config/notifications.ts +46 -46
  15. package/src/config/policy.ts +115 -115
  16. package/src/config/v2.ts +1128 -1128
  17. package/src/config.ts +320 -320
  18. package/src/debug.ts +238 -238
  19. package/src/deferred-tool-carryover.ts +282 -282
  20. package/src/details-store.ts +151 -151
  21. package/src/extension-runtime.ts +1095 -1095
  22. package/src/image-generation/extension.ts +189 -189
  23. package/src/image-generation/protocol.ts +579 -579
  24. package/src/image-generation/types.ts +300 -300
  25. package/src/native-fallback.ts +140 -140
  26. package/src/payload-rewrite.ts +515 -515
  27. package/src/pi-context-hook.ts +117 -117
  28. package/src/remote-v2-client.ts +811 -811
  29. package/src/request-context-cache.ts +155 -155
  30. package/src/responses-headers.ts +180 -180
  31. package/src/retired-remote-windows.ts +35 -35
  32. package/src/runtime.ts +875 -875
  33. package/src/serializer.ts +883 -882
  34. package/src/supported-environment.ts +27 -27
  35. package/src/types.ts +541 -541
  36. package/src/web-search/alpha-client.ts +876 -876
  37. package/src/web-search/extension.ts +530 -530
  38. package/src/web-search/payload.ts +299 -299
  39. package/src/web-search/prompt.ts +60 -60
  40. package/src/web-search/types.ts +166 -166
  41. package/tsconfig.check.json +50 -50
package/src/types.ts CHANGED
@@ -1,541 +1,541 @@
1
- import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
- import type { ConfigDocumentSnapshot } from "./config/policy";
3
- import type { CompactionEntry, CompactionResult, ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
4
-
5
- export const TOOLKIT_ID = "pi-openai-toolkit";
6
- export const COMPACTION_EXTENSION_ID = `${TOOLKIT_ID}:compaction`;
7
- export const WEB_SEARCH_EXTENSION_ID = `${TOOLKIT_ID}:web-search`;
8
- export const IMAGE_GENERATION_EXTENSION_ID = `${TOOLKIT_ID}:image-generation`;
9
- export const DEFAULT_ARTIFACT_ROOT = "~/.pi/agent/artifacts/pi-openai-toolkit/compaction";
10
- export const REDACTED_VALUE = "[REDACTED]";
11
- /**
12
- * APIs the extension knows how to build a `/responses/compact` URL for.
13
- * `compaction.responsesApis` in config.json may only narrow this set.
14
- */
15
- export const RESPONSES_COMPACT_CAPABLE_APIS = [
16
- "openai-responses",
17
- "openai-codex-responses",
18
- "azure-openai-responses",
19
- ] as const;
20
- /**
21
- * Bare model ID used by the independent Astra effort compatibility layer.
22
- * Gateway transport is an independent exact-model opt-in; this constant
23
- * does not verify backend support or grant that opt-in.
24
- */
25
- export const ASTRA_MODEL_ID = "gpt-6-astra";
26
- export const LEGACY_NATIVE_COMPACTION_STRATEGY = "openai-native-compact-v1";
27
- export const REMOTE_V2_COMPACTION_STRATEGY = "openai-remote-compaction-v2";
28
- export const NATIVE_COMPACTION_STRATEGY = REMOTE_V2_COMPACTION_STRATEGY;
29
- /**
30
- * Provenance recorded when the checkpoint input came from Pi's ordered context
31
- * hook projection. Older persisted entries may omit it and must not be
32
- * replayed by Remote V2.
33
- */
34
- export const NATIVE_COMPACTION_INPUT_PROVENANCE = "pi-context-hook-v1" as const;
35
- /** Explicit opt-in marker for the pre-projection raw session/branch chain. */
36
- export const LEGACY_REMOTE_V2_INPUT_PROVENANCE = "legacy-raw-context-v1" as const;
37
- export type NativeCompactionInputProvenance =
38
- | typeof NATIVE_COMPACTION_INPUT_PROVENANCE
39
- | typeof LEGACY_REMOTE_V2_INPUT_PROVENANCE;
40
- /**
41
- * Pi currently requires CompactionResult.summary to be text. This marker is only a
42
- * replay shim; the provider receives the opaque item from details.compactedWindow.
43
- */
44
- export const NATIVE_COMPACTION_FALLBACK_SUMMARY = "[OpenAI remote v2 opaque compaction checkpoint]";
45
- export const STANDALONE_COMPACTION_FALLBACK_SUMMARY = "[OpenAI standalone opaque compaction window]";
46
-
47
- export const THINKING_LEVELS: readonly ThinkingLevel[] = [
48
- "off",
49
- "minimal",
50
- "low",
51
- "medium",
52
- "high",
53
- "xhigh",
54
- "max",
55
- ];
56
-
57
- export type DebugArtifactKind =
58
- | "provider-request"
59
- | "compact-response"
60
- | "compaction-event"
61
- | "lifecycle";
62
-
63
- /** How Remote V2 obtains the history it sends to the compaction endpoint. */
64
- export type RemoteV2ContextSource = "pi-context-hook" | "legacy";
65
-
66
- /** Native-method fallback compaction: which model runs pi's compact() and how deeply. */
67
- export type NativeFallbackConfig = {
68
- enabled: boolean;
69
- /**
70
- * "provider/model-id" used for native-method fallback compaction when the active model
71
- * cannot use remote v2 at all. A failed remote request is instead summarized by
72
- * `remoteCompactModel`; the two sources never substitute for each other. Unset means the
73
- * active model via pi's default path.
74
- */
75
- model?: string;
76
- /** Thinking level passed to pi's native compact() when the fallback model runs. */
77
- thinkingLevel: ThinkingLevel;
78
- };
79
-
80
- export type DebugConfig = {
81
- debug: boolean;
82
- notifyOnLoad: boolean;
83
- logProviderPayloads: boolean;
84
- logCompactResponses: boolean;
85
- redactSensitiveData: boolean;
86
- artifactRoot: string;
87
- };
88
-
89
- export type CompactionConfig = {
90
- enabled: boolean;
91
- /**
92
- * Allow a Responses session whose latest compaction was not created by this extension
93
- * to restart native compaction from Pi's current serialized session context.
94
- */
95
- allowCompactionContinuityBreak: boolean;
96
- /**
97
- * "provider/model-id" used only for remote_compaction_v2. The active session model
98
- * remains the checkpoint consumer and continues handling normal requests.
99
- */
100
- remoteCompactModel?: string;
101
- /**
102
- * Remote V2 input source. Pi's patched context hook preserves the normal
103
- * provider-visible projection; legacy keeps the pre-patch raw session/branch chain.
104
- */
105
- remoteV2ContextSource: RemoteV2ContextSource;
106
- /** Native-method fallback compaction policy (non-Responses APIs, or when the compact endpoint fails). */
107
- nativeFallback: NativeFallbackConfig;
108
- /**
109
- * Subset of RESPONSES_COMPACT_CAPABLE_APIS that should use remote compaction.
110
- */
111
- responsesApis: string[];
112
- /**
113
- * Legacy alias for exact "provider/model" Codex gateway transport opt-ins.
114
- * Shared by compaction and image generation; not a window capability.
115
- */
116
- gatewayContextModels: string[];
117
- notifyOnLoad: boolean;
118
- debug: boolean;
119
- logProviderPayloads: boolean;
120
- logCompactResponses: boolean;
121
- redactSensitiveData: boolean;
122
- artifactRoot: string;
123
- };
124
-
125
- export type WebSearchRoute = "local" | "hosted" | "standalone-alpha";
126
-
127
- export type WebSearchConfig = {
128
- enabled: boolean;
129
- /** Exact provider/model keys allowed to use toolkit-native Web Search. */
130
- models: string[];
131
- /** Explicit route for every model not matched by `routes`. */
132
- defaultRoute?: WebSearchRoute;
133
- /** Exact `provider/model-id` overrides; no globs or fuzzy matching. */
134
- routes?: Record<string, WebSearchRoute>;
135
- };
136
-
137
- export type ImageGenerationConfig = {
138
- enabled: boolean;
139
- /**
140
- * Bare Images API model ids this toolkit may send, without a `provider/` prefix.
141
- * The first legacy entry is the default; `openai_generate_image`'s optional `model` argument
142
- * may select any other entry in this order-preserving list.
143
- */
144
- models: string[];
145
- };
146
-
147
- export type ToolkitConfig = {
148
- compaction: CompactionConfig;
149
- webSearch: WebSearchConfig;
150
- imageGeneration: ImageGenerationConfig;
151
- };
152
-
153
- export type LoadedToolkitConfig = {
154
- /** Legacy normalization seam. Feature entrypoints must resolve this snapshot for their model. */
155
- config: ToolkitConfig;
156
- /** Optional only for existing injected legacy readers; real loads always include it. */
157
- document?: ConfigDocumentSnapshot;
158
- /** Path of the config file that was applied, if it existed and parsed. */
159
- source?: string;
160
- warnings: string[];
161
- };
162
-
163
- export type ArtifactPaths = {
164
- rootDir: string;
165
- sessionDir: string;
166
- providerRequestsDir: string;
167
- compactResponsesDir: string;
168
- compactionDir: string;
169
- lifecycleDir: string;
170
- };
171
-
172
- export type ArtifactSessionInfo = {
173
- cwd: string;
174
- sessionId?: string;
175
- sessionFile?: string;
176
- sessionDir?: string;
177
- };
178
-
179
- export type ArtifactContext = ArtifactSessionInfo | Pick<ExtensionContext, "cwd" | "sessionManager">;
180
-
181
- export type DebugArtifactEnvelope = {
182
- extension: string;
183
- kind: DebugArtifactKind;
184
- timestamp: string;
185
- cwd: string;
186
- sessionId?: string;
187
- sessionFile?: string;
188
- sessionDir?: string;
189
- redaction: {
190
- enabled: boolean;
191
- };
192
- data: unknown;
193
- };
194
-
195
- export type RedactOptions = {
196
- placeholder?: string;
197
- };
198
-
199
- export type NativeCompactionStrategy =
200
- | typeof LEGACY_NATIVE_COMPACTION_STRATEGY
201
- | typeof REMOTE_V2_COMPACTION_STRATEGY;
202
-
203
- export type NativeCompactionRequestMeta = {
204
- tokensBefore?: number;
205
- previousSummaryPresent?: boolean;
206
- };
207
-
208
- export type NativeCompactionIdentity = {
209
- provider: string;
210
- api: string;
211
- model: string;
212
- baseUrl: string;
213
- };
214
-
215
- export const CACHE_STACK_ACTIVATION_ENTRY_TYPE = "pi-cache-stack.activation-state.v1" as const;
216
-
217
- export type DeferredToolCarryoverV1 = {
218
- version: 1;
219
- source: typeof CACHE_STACK_ACTIVATION_ENTRY_TYPE;
220
- toolNames: string[];
221
- catalogHash?: string;
222
- };
223
-
224
- export type NativeCompactionDetails = NativeCompactionIdentity & {
225
- strategy: NativeCompactionStrategy;
226
- /**
227
- * Input source recorded for checkpoints created by this extension. Older
228
- * persisted entries may omit it and must not be treated as verified for
229
- * Remote V2 replay.
230
- */
231
- inputProvenance?: NativeCompactionInputProvenance;
232
- /** Actual producer of the opaque checkpoint; absent on legacy same-model entries. */
233
- compactionModel?: NativeCompactionIdentity;
234
- /** Cache-stack activation state captured at this opaque checkpoint. */
235
- deferredToolCarryover?: DeferredToolCarryoverV1;
236
- compactedWindow: unknown[];
237
- compactResponseId?: string;
238
- createdAt: string;
239
- requestMeta?: NativeCompactionRequestMeta;
240
- };
241
-
242
- export type NativeCompactionEntry = CompactionEntry<NativeCompactionDetails>;
243
-
244
- export type CreateNativeCompactionDetailsInput = NativeCompactionIdentity & {
245
- strategy?: NativeCompactionStrategy;
246
- inputProvenance: NativeCompactionInputProvenance;
247
- compactionModel?: NativeCompactionIdentity;
248
- deferredToolCarryover?: DeferredToolCarryoverV1;
249
- compactedWindow: unknown[];
250
- compactResponseId?: string;
251
- createdAt?: string;
252
- requestMeta?: NativeCompactionRequestMeta;
253
- };
254
-
255
- export type CreateNativeCompactionResultInput = {
256
- firstKeptEntryId: string;
257
- tokensBefore: number;
258
- details: NativeCompactionDetails;
259
- /**
260
- * An explicitly supplied genuine summary. Retained standalone assistant text is
261
- * not a summary of the sealed history; ordinary remote success uses an opaque marker.
262
- */
263
- summary?: string;
264
- };
265
-
266
- function isRecord(value: unknown): value is Record<string, unknown> {
267
- return !!value && typeof value === "object" && !Array.isArray(value);
268
- }
269
-
270
- function isNonEmptyString(value: unknown): value is string {
271
- return typeof value === "string" && value.trim().length > 0;
272
- }
273
-
274
- function isFiniteNonNegativeNumber(value: unknown): value is number {
275
- return typeof value === "number" && Number.isFinite(value) && value >= 0;
276
- }
277
-
278
- function normalizeString(value: string): string {
279
- return value.trim();
280
- }
281
-
282
- function isStructuredValue(value: unknown): boolean {
283
- if (
284
- value === null ||
285
- typeof value === "string" ||
286
- (typeof value === "number" && Number.isFinite(value)) ||
287
- typeof value === "boolean"
288
- ) {
289
- return true;
290
- }
291
-
292
- if (Array.isArray(value)) {
293
- return value.every(isStructuredValue);
294
- }
295
-
296
- if (isRecord(value)) {
297
- return Object.values(value).every(isStructuredValue);
298
- }
299
-
300
- return false;
301
- }
302
-
303
- function cloneStructuredValue(value: unknown): unknown {
304
- if (
305
- value === null ||
306
- typeof value === "string" ||
307
- typeof value === "number" ||
308
- typeof value === "boolean"
309
- ) {
310
- return value;
311
- }
312
-
313
- if (Array.isArray(value)) {
314
- return value.map(cloneStructuredValue);
315
- }
316
-
317
- if (isRecord(value)) {
318
- return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, cloneStructuredValue(nested)]));
319
- }
320
-
321
- throw new Error(`Unsupported structured value: ${typeof value}`);
322
- }
323
-
324
- export function isCompactedWindowItem(value: unknown): value is Record<string, unknown> {
325
- return isRecord(value) && Object.values(value).every(isStructuredValue);
326
- }
327
-
328
- export function isNativeCompactionRequestMeta(value: unknown): value is NativeCompactionRequestMeta {
329
- if (!isRecord(value)) {
330
- return false;
331
- }
332
-
333
- const { tokensBefore, previousSummaryPresent } = value;
334
- if (tokensBefore !== undefined && !isFiniteNonNegativeNumber(tokensBefore)) {
335
- return false;
336
- }
337
-
338
- if (previousSummaryPresent !== undefined && typeof previousSummaryPresent !== "boolean") {
339
- return false;
340
- }
341
-
342
- return true;
343
- }
344
-
345
- export function isNativeCompactionIdentity(value: unknown): value is NativeCompactionIdentity {
346
- if (!isRecord(value)) {
347
- return false;
348
- }
349
-
350
- return (
351
- isNonEmptyString(value.provider) &&
352
- isNonEmptyString(value.api) &&
353
- isNonEmptyString(value.model) &&
354
- isNonEmptyString(value.baseUrl)
355
- );
356
- }
357
-
358
- export function isNativeCompactionInputProvenance(value: unknown): value is NativeCompactionInputProvenance {
359
- return value === NATIVE_COMPACTION_INPUT_PROVENANCE || value === LEGACY_REMOTE_V2_INPUT_PROVENANCE;
360
- }
361
-
362
- export function getRemoteV2InputProvenance(source: RemoteV2ContextSource): NativeCompactionInputProvenance {
363
- return source === "pi-context-hook"
364
- ? NATIVE_COMPACTION_INPUT_PROVENANCE
365
- : LEGACY_REMOTE_V2_INPUT_PROVENANCE;
366
- }
367
-
368
- export function isDeferredToolCarryover(value: unknown): value is DeferredToolCarryoverV1 {
369
- if (!isRecord(value) || value.version !== 1 || value.source !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
370
- return false;
371
- }
372
-
373
- return (
374
- Array.isArray(value.toolNames) &&
375
- value.toolNames.every((name) => isNonEmptyString(name)) &&
376
- (value.catalogHash === undefined || typeof value.catalogHash === "string")
377
- );
378
- }
379
-
380
- function normalizeDeferredToolCarryover(value: DeferredToolCarryoverV1): DeferredToolCarryoverV1 {
381
- if (!isDeferredToolCarryover(value)) {
382
- throw new Error("Invalid deferred tool carryover");
383
- }
384
-
385
- return {
386
- version: 1,
387
- source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
388
- toolNames: [...new Set(value.toolNames.map((name) => name.trim()).filter(Boolean))].sort(),
389
- ...(typeof value.catalogHash === "string" ? { catalogHash: value.catalogHash } : {}),
390
- };
391
- }
392
-
393
- function decodeCacheStackActivationState(value: unknown): DeferredToolCarryoverV1 | undefined {
394
- if (!isRecord(value) || value.version !== 1 || typeof value.catalogHash !== "string") {
395
- return undefined;
396
- }
397
- if (!Array.isArray(value.activatedTools) || !value.activatedTools.every((name) => typeof name === "string")) {
398
- return undefined;
399
- }
400
-
401
- return normalizeDeferredToolCarryover({
402
- version: 1,
403
- source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
404
- toolNames: value.activatedTools,
405
- catalogHash: value.catalogHash,
406
- });
407
- }
408
-
409
- /** Read only the latest valid cache-stack activation snapshot on this branch. */
410
- export function getLatestDeferredToolCarryover(
411
- entries: readonly SessionEntry[],
412
- ): DeferredToolCarryoverV1 | undefined {
413
- for (let index = entries.length - 1; index >= 0; index -= 1) {
414
- const entry = entries[index];
415
- if (entry?.type !== "custom" || entry.customType !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
416
- continue;
417
- }
418
-
419
- const decoded = decodeCacheStackActivationState(entry.data);
420
- if (decoded) return decoded;
421
- }
422
-
423
- return undefined;
424
- }
425
-
426
- export function isNativeCompactionDetails(value: unknown): value is NativeCompactionDetails {
427
- if (!isRecord(value) || !isNativeCompactionIdentity(value)) {
428
- return false;
429
- }
430
-
431
- const candidate = value as Record<string, unknown>;
432
- return (
433
- (candidate.strategy === LEGACY_NATIVE_COMPACTION_STRATEGY ||
434
- candidate.strategy === REMOTE_V2_COMPACTION_STRATEGY) &&
435
- (candidate.inputProvenance === undefined || isNativeCompactionInputProvenance(candidate.inputProvenance)) &&
436
- (candidate.compactionModel === undefined || isNativeCompactionIdentity(candidate.compactionModel)) &&
437
- (candidate.deferredToolCarryover === undefined || isDeferredToolCarryover(candidate.deferredToolCarryover)) &&
438
- Array.isArray(candidate.compactedWindow) &&
439
- candidate.compactedWindow.every(isCompactedWindowItem) &&
440
- isNonEmptyString(candidate.createdAt) &&
441
- (candidate.compactResponseId === undefined || isNonEmptyString(candidate.compactResponseId)) &&
442
- (candidate.requestMeta === undefined || isNativeCompactionRequestMeta(candidate.requestMeta))
443
- );
444
- }
445
-
446
- export function isNativeCompactionEntry(value: unknown): value is NativeCompactionEntry {
447
- return isRecord(value) && value.type === "compaction" && isNativeCompactionDetails(value.details);
448
- }
449
-
450
- export function createNativeCompactionDetails(input: CreateNativeCompactionDetailsInput): NativeCompactionDetails {
451
- if (input.strategy !== undefined && input.strategy !== LEGACY_NATIVE_COMPACTION_STRATEGY && input.strategy !== REMOTE_V2_COMPACTION_STRATEGY) {
452
- throw new Error("Invalid native compaction strategy");
453
- }
454
- return {
455
- strategy: input.strategy ?? NATIVE_COMPACTION_STRATEGY,
456
- inputProvenance: input.inputProvenance,
457
- provider: normalizeString(input.provider),
458
- api: normalizeString(input.api),
459
- model: normalizeString(input.model),
460
- baseUrl: normalizeString(input.baseUrl),
461
- compactionModel: input.compactionModel
462
- ? {
463
- provider: normalizeString(input.compactionModel.provider),
464
- api: normalizeString(input.compactionModel.api),
465
- model: normalizeString(input.compactionModel.model),
466
- baseUrl: normalizeString(input.compactionModel.baseUrl),
467
- }
468
- : undefined,
469
- deferredToolCarryover: input.deferredToolCarryover
470
- ? normalizeDeferredToolCarryover(input.deferredToolCarryover)
471
- : undefined,
472
- compactedWindow: input.compactedWindow.map((item) => cloneStructuredValue(item)),
473
- compactResponseId: isNonEmptyString(input.compactResponseId) ? normalizeString(input.compactResponseId) : undefined,
474
- createdAt: isNonEmptyString(input.createdAt) ? normalizeString(input.createdAt) : new Date().toISOString(),
475
- requestMeta: input.requestMeta
476
- ? {
477
- ...(input.requestMeta.tokensBefore !== undefined ? { tokensBefore: input.requestMeta.tokensBefore } : {}),
478
- ...(input.requestMeta.previousSummaryPresent !== undefined
479
- ? { previousSummaryPresent: input.requestMeta.previousSummaryPresent }
480
- : {}),
481
- }
482
- : undefined,
483
- };
484
- }
485
-
486
- export function createNativeCompactionResult(
487
- input: CreateNativeCompactionResultInput,
488
- ): CompactionResult<NativeCompactionDetails> {
489
- const summary = input.summary?.trim();
490
- return {
491
- summary: summary && summary.length > 0 ? summary
492
- : input.details.strategy === LEGACY_NATIVE_COMPACTION_STRATEGY ? STANDALONE_COMPACTION_FALLBACK_SUMMARY
493
- : NATIVE_COMPACTION_FALLBACK_SUMMARY,
494
- firstKeptEntryId: input.firstKeptEntryId,
495
- tokensBefore: input.tokensBefore,
496
- details: input.details,
497
- };
498
- }
499
-
500
- export const DEFAULT_NATIVE_FALLBACK_CONFIG: NativeFallbackConfig = {
501
- enabled: true,
502
- model: undefined,
503
- thinkingLevel: "off",
504
- };
505
-
506
- export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
507
- enabled: true,
508
- allowCompactionContinuityBreak: false,
509
- remoteCompactModel: undefined,
510
- remoteV2ContextSource: "legacy",
511
-
512
- nativeFallback: { ...DEFAULT_NATIVE_FALLBACK_CONFIG },
513
- responsesApis: [...RESPONSES_COMPACT_CAPABLE_APIS],
514
- gatewayContextModels: [],
515
- notifyOnLoad: false,
516
- debug: false,
517
- logProviderPayloads: false,
518
- logCompactResponses: false,
519
- redactSensitiveData: true,
520
- artifactRoot: DEFAULT_ARTIFACT_ROOT,
521
- };
522
-
523
- export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchConfig = {
524
- enabled: true,
525
- models: [],
526
- };
527
-
528
- /** Single source of truth for the hosted image model used when nothing is configured. */
529
- export const DEFAULT_IMAGE_GENERATION_MODEL = "grok-imagine-image-2.0";
530
- export const DEFAULT_IMAGE_GENERATION_MODELS: readonly string[] = [DEFAULT_IMAGE_GENERATION_MODEL];
531
-
532
- export const DEFAULT_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {
533
- enabled: false,
534
- models: [...DEFAULT_IMAGE_GENERATION_MODELS],
535
- };
536
-
537
- export const DEFAULT_TOOLKIT_CONFIG: ToolkitConfig = {
538
- compaction: DEFAULT_COMPACTION_CONFIG,
539
- webSearch: DEFAULT_WEB_SEARCH_CONFIG,
540
- imageGeneration: DEFAULT_IMAGE_GENERATION_CONFIG,
541
- };
1
+ import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
+ import type { ConfigDocumentSnapshot } from "./config/policy";
3
+ import type { CompactionEntry, CompactionResult, ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
4
+
5
+ export const TOOLKIT_ID = "pi-openai-toolkit";
6
+ export const COMPACTION_EXTENSION_ID = `${TOOLKIT_ID}:compaction`;
7
+ export const WEB_SEARCH_EXTENSION_ID = `${TOOLKIT_ID}:web-search`;
8
+ export const IMAGE_GENERATION_EXTENSION_ID = `${TOOLKIT_ID}:image-generation`;
9
+ export const DEFAULT_ARTIFACT_ROOT = "~/.pi/agent/artifacts/pi-openai-toolkit/compaction";
10
+ export const REDACTED_VALUE = "[REDACTED]";
11
+ /**
12
+ * APIs the extension knows how to build a `/responses/compact` URL for.
13
+ * `compaction.responsesApis` in config.json may only narrow this set.
14
+ */
15
+ export const RESPONSES_COMPACT_CAPABLE_APIS = [
16
+ "openai-responses",
17
+ "openai-codex-responses",
18
+ "azure-openai-responses",
19
+ ] as const;
20
+ /**
21
+ * Bare model ID used by the independent Astra effort compatibility layer.
22
+ * Gateway transport is an independent exact-model opt-in; this constant
23
+ * does not verify backend support or grant that opt-in.
24
+ */
25
+ export const ASTRA_MODEL_ID = "gpt-6-astra";
26
+ export const LEGACY_NATIVE_COMPACTION_STRATEGY = "openai-native-compact-v1";
27
+ export const REMOTE_V2_COMPACTION_STRATEGY = "openai-remote-compaction-v2";
28
+ export const NATIVE_COMPACTION_STRATEGY = REMOTE_V2_COMPACTION_STRATEGY;
29
+ /**
30
+ * Provenance recorded when the checkpoint input came from Pi's ordered context
31
+ * hook projection. Older persisted entries may omit it and must not be
32
+ * replayed by Remote V2.
33
+ */
34
+ export const NATIVE_COMPACTION_INPUT_PROVENANCE = "pi-context-hook-v1" as const;
35
+ /** Explicit opt-in marker for the pre-projection raw session/branch chain. */
36
+ export const LEGACY_REMOTE_V2_INPUT_PROVENANCE = "legacy-raw-context-v1" as const;
37
+ export type NativeCompactionInputProvenance =
38
+ | typeof NATIVE_COMPACTION_INPUT_PROVENANCE
39
+ | typeof LEGACY_REMOTE_V2_INPUT_PROVENANCE;
40
+ /**
41
+ * Pi currently requires CompactionResult.summary to be text. This marker is only a
42
+ * replay shim; the provider receives the opaque item from details.compactedWindow.
43
+ */
44
+ export const NATIVE_COMPACTION_FALLBACK_SUMMARY = "[OpenAI remote v2 opaque compaction checkpoint]";
45
+ export const STANDALONE_COMPACTION_FALLBACK_SUMMARY = "[OpenAI standalone opaque compaction window]";
46
+
47
+ export const THINKING_LEVELS: readonly ThinkingLevel[] = [
48
+ "off",
49
+ "minimal",
50
+ "low",
51
+ "medium",
52
+ "high",
53
+ "xhigh",
54
+ "max",
55
+ ];
56
+
57
+ export type DebugArtifactKind =
58
+ | "provider-request"
59
+ | "compact-response"
60
+ | "compaction-event"
61
+ | "lifecycle";
62
+
63
+ /** How Remote V2 obtains the history it sends to the compaction endpoint. */
64
+ export type RemoteV2ContextSource = "pi-context-hook" | "legacy";
65
+
66
+ /** Native-method fallback compaction: which model runs pi's compact() and how deeply. */
67
+ export type NativeFallbackConfig = {
68
+ enabled: boolean;
69
+ /**
70
+ * "provider/model-id" used for native-method fallback compaction when the active model
71
+ * cannot use remote v2 at all. A failed remote request is instead summarized by
72
+ * `remoteCompactModel`; the two sources never substitute for each other. Unset means the
73
+ * active model via pi's default path.
74
+ */
75
+ model?: string;
76
+ /** Thinking level passed to pi's native compact() when the fallback model runs. */
77
+ thinkingLevel: ThinkingLevel;
78
+ };
79
+
80
+ export type DebugConfig = {
81
+ debug: boolean;
82
+ notifyOnLoad: boolean;
83
+ logProviderPayloads: boolean;
84
+ logCompactResponses: boolean;
85
+ redactSensitiveData: boolean;
86
+ artifactRoot: string;
87
+ };
88
+
89
+ export type CompactionConfig = {
90
+ enabled: boolean;
91
+ /**
92
+ * Allow a Responses session whose latest compaction was not created by this extension
93
+ * to restart native compaction from Pi's current serialized session context.
94
+ */
95
+ allowCompactionContinuityBreak: boolean;
96
+ /**
97
+ * "provider/model-id" used only for remote_compaction_v2. The active session model
98
+ * remains the checkpoint consumer and continues handling normal requests.
99
+ */
100
+ remoteCompactModel?: string;
101
+ /**
102
+ * Remote V2 input source. Pi's patched context hook preserves the normal
103
+ * provider-visible projection; legacy keeps the pre-patch raw session/branch chain.
104
+ */
105
+ remoteV2ContextSource: RemoteV2ContextSource;
106
+ /** Native-method fallback compaction policy (non-Responses APIs, or when the compact endpoint fails). */
107
+ nativeFallback: NativeFallbackConfig;
108
+ /**
109
+ * Subset of RESPONSES_COMPACT_CAPABLE_APIS that should use remote compaction.
110
+ */
111
+ responsesApis: string[];
112
+ /**
113
+ * Legacy alias for exact "provider/model" Codex gateway transport opt-ins.
114
+ * Shared by compaction and image generation; not a window capability.
115
+ */
116
+ gatewayContextModels: string[];
117
+ notifyOnLoad: boolean;
118
+ debug: boolean;
119
+ logProviderPayloads: boolean;
120
+ logCompactResponses: boolean;
121
+ redactSensitiveData: boolean;
122
+ artifactRoot: string;
123
+ };
124
+
125
+ export type WebSearchRoute = "local" | "hosted" | "standalone-alpha";
126
+
127
+ export type WebSearchConfig = {
128
+ enabled: boolean;
129
+ /** Exact provider/model keys allowed to use toolkit-native Web Search. */
130
+ models: string[];
131
+ /** Explicit route for every model not matched by `routes`. */
132
+ defaultRoute?: WebSearchRoute;
133
+ /** Exact `provider/model-id` overrides; no globs or fuzzy matching. */
134
+ routes?: Record<string, WebSearchRoute>;
135
+ };
136
+
137
+ export type ImageGenerationConfig = {
138
+ enabled: boolean;
139
+ /**
140
+ * Bare Images API model ids this toolkit may send, without a `provider/` prefix.
141
+ * The first legacy entry is the default; `openai_generate_image`'s optional `model` argument
142
+ * may select any other entry in this order-preserving list.
143
+ */
144
+ models: string[];
145
+ };
146
+
147
+ export type ToolkitConfig = {
148
+ compaction: CompactionConfig;
149
+ webSearch: WebSearchConfig;
150
+ imageGeneration: ImageGenerationConfig;
151
+ };
152
+
153
+ export type LoadedToolkitConfig = {
154
+ /** Legacy normalization seam. Feature entrypoints must resolve this snapshot for their model. */
155
+ config: ToolkitConfig;
156
+ /** Optional only for existing injected legacy readers; real loads always include it. */
157
+ document?: ConfigDocumentSnapshot;
158
+ /** Path of the config file that was applied, if it existed and parsed. */
159
+ source?: string;
160
+ warnings: string[];
161
+ };
162
+
163
+ export type ArtifactPaths = {
164
+ rootDir: string;
165
+ sessionDir: string;
166
+ providerRequestsDir: string;
167
+ compactResponsesDir: string;
168
+ compactionDir: string;
169
+ lifecycleDir: string;
170
+ };
171
+
172
+ export type ArtifactSessionInfo = {
173
+ cwd: string;
174
+ sessionId?: string;
175
+ sessionFile?: string;
176
+ sessionDir?: string;
177
+ };
178
+
179
+ export type ArtifactContext = ArtifactSessionInfo | Pick<ExtensionContext, "cwd" | "sessionManager">;
180
+
181
+ export type DebugArtifactEnvelope = {
182
+ extension: string;
183
+ kind: DebugArtifactKind;
184
+ timestamp: string;
185
+ cwd: string;
186
+ sessionId?: string;
187
+ sessionFile?: string;
188
+ sessionDir?: string;
189
+ redaction: {
190
+ enabled: boolean;
191
+ };
192
+ data: unknown;
193
+ };
194
+
195
+ export type RedactOptions = {
196
+ placeholder?: string;
197
+ };
198
+
199
+ export type NativeCompactionStrategy =
200
+ | typeof LEGACY_NATIVE_COMPACTION_STRATEGY
201
+ | typeof REMOTE_V2_COMPACTION_STRATEGY;
202
+
203
+ export type NativeCompactionRequestMeta = {
204
+ tokensBefore?: number;
205
+ previousSummaryPresent?: boolean;
206
+ };
207
+
208
+ export type NativeCompactionIdentity = {
209
+ provider: string;
210
+ api: string;
211
+ model: string;
212
+ baseUrl: string;
213
+ };
214
+
215
+ export const CACHE_STACK_ACTIVATION_ENTRY_TYPE = "pi-cache-stack.activation-state.v1" as const;
216
+
217
+ export type DeferredToolCarryoverV1 = {
218
+ version: 1;
219
+ source: typeof CACHE_STACK_ACTIVATION_ENTRY_TYPE;
220
+ toolNames: string[];
221
+ catalogHash?: string;
222
+ };
223
+
224
+ export type NativeCompactionDetails = NativeCompactionIdentity & {
225
+ strategy: NativeCompactionStrategy;
226
+ /**
227
+ * Input source recorded for checkpoints created by this extension. Older
228
+ * persisted entries may omit it and must not be treated as verified for
229
+ * Remote V2 replay.
230
+ */
231
+ inputProvenance?: NativeCompactionInputProvenance;
232
+ /** Actual producer of the opaque checkpoint; absent on legacy same-model entries. */
233
+ compactionModel?: NativeCompactionIdentity;
234
+ /** Cache-stack activation state captured at this opaque checkpoint. */
235
+ deferredToolCarryover?: DeferredToolCarryoverV1;
236
+ compactedWindow: unknown[];
237
+ compactResponseId?: string;
238
+ createdAt: string;
239
+ requestMeta?: NativeCompactionRequestMeta;
240
+ };
241
+
242
+ export type NativeCompactionEntry = CompactionEntry<NativeCompactionDetails>;
243
+
244
+ export type CreateNativeCompactionDetailsInput = NativeCompactionIdentity & {
245
+ strategy?: NativeCompactionStrategy;
246
+ inputProvenance: NativeCompactionInputProvenance;
247
+ compactionModel?: NativeCompactionIdentity;
248
+ deferredToolCarryover?: DeferredToolCarryoverV1;
249
+ compactedWindow: unknown[];
250
+ compactResponseId?: string;
251
+ createdAt?: string;
252
+ requestMeta?: NativeCompactionRequestMeta;
253
+ };
254
+
255
+ export type CreateNativeCompactionResultInput = {
256
+ firstKeptEntryId: string;
257
+ tokensBefore: number;
258
+ details: NativeCompactionDetails;
259
+ /**
260
+ * An explicitly supplied genuine summary. Retained standalone assistant text is
261
+ * not a summary of the sealed history; ordinary remote success uses an opaque marker.
262
+ */
263
+ summary?: string;
264
+ };
265
+
266
+ function isRecord(value: unknown): value is Record<string, unknown> {
267
+ return !!value && typeof value === "object" && !Array.isArray(value);
268
+ }
269
+
270
+ function isNonEmptyString(value: unknown): value is string {
271
+ return typeof value === "string" && value.trim().length > 0;
272
+ }
273
+
274
+ function isFiniteNonNegativeNumber(value: unknown): value is number {
275
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
276
+ }
277
+
278
+ function normalizeString(value: string): string {
279
+ return value.trim();
280
+ }
281
+
282
+ function isStructuredValue(value: unknown): boolean {
283
+ if (
284
+ value === null ||
285
+ typeof value === "string" ||
286
+ (typeof value === "number" && Number.isFinite(value)) ||
287
+ typeof value === "boolean"
288
+ ) {
289
+ return true;
290
+ }
291
+
292
+ if (Array.isArray(value)) {
293
+ return value.every(isStructuredValue);
294
+ }
295
+
296
+ if (isRecord(value)) {
297
+ return Object.values(value).every(isStructuredValue);
298
+ }
299
+
300
+ return false;
301
+ }
302
+
303
+ function cloneStructuredValue(value: unknown): unknown {
304
+ if (
305
+ value === null ||
306
+ typeof value === "string" ||
307
+ typeof value === "number" ||
308
+ typeof value === "boolean"
309
+ ) {
310
+ return value;
311
+ }
312
+
313
+ if (Array.isArray(value)) {
314
+ return value.map(cloneStructuredValue);
315
+ }
316
+
317
+ if (isRecord(value)) {
318
+ return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, cloneStructuredValue(nested)]));
319
+ }
320
+
321
+ throw new Error(`Unsupported structured value: ${typeof value}`);
322
+ }
323
+
324
+ export function isCompactedWindowItem(value: unknown): value is Record<string, unknown> {
325
+ return isRecord(value) && Object.values(value).every(isStructuredValue);
326
+ }
327
+
328
+ export function isNativeCompactionRequestMeta(value: unknown): value is NativeCompactionRequestMeta {
329
+ if (!isRecord(value)) {
330
+ return false;
331
+ }
332
+
333
+ const { tokensBefore, previousSummaryPresent } = value;
334
+ if (tokensBefore !== undefined && !isFiniteNonNegativeNumber(tokensBefore)) {
335
+ return false;
336
+ }
337
+
338
+ if (previousSummaryPresent !== undefined && typeof previousSummaryPresent !== "boolean") {
339
+ return false;
340
+ }
341
+
342
+ return true;
343
+ }
344
+
345
+ export function isNativeCompactionIdentity(value: unknown): value is NativeCompactionIdentity {
346
+ if (!isRecord(value)) {
347
+ return false;
348
+ }
349
+
350
+ return (
351
+ isNonEmptyString(value.provider) &&
352
+ isNonEmptyString(value.api) &&
353
+ isNonEmptyString(value.model) &&
354
+ isNonEmptyString(value.baseUrl)
355
+ );
356
+ }
357
+
358
+ export function isNativeCompactionInputProvenance(value: unknown): value is NativeCompactionInputProvenance {
359
+ return value === NATIVE_COMPACTION_INPUT_PROVENANCE || value === LEGACY_REMOTE_V2_INPUT_PROVENANCE;
360
+ }
361
+
362
+ export function getRemoteV2InputProvenance(source: RemoteV2ContextSource): NativeCompactionInputProvenance {
363
+ return source === "pi-context-hook"
364
+ ? NATIVE_COMPACTION_INPUT_PROVENANCE
365
+ : LEGACY_REMOTE_V2_INPUT_PROVENANCE;
366
+ }
367
+
368
+ export function isDeferredToolCarryover(value: unknown): value is DeferredToolCarryoverV1 {
369
+ if (!isRecord(value) || value.version !== 1 || value.source !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
370
+ return false;
371
+ }
372
+
373
+ return (
374
+ Array.isArray(value.toolNames) &&
375
+ value.toolNames.every((name) => isNonEmptyString(name)) &&
376
+ (value.catalogHash === undefined || typeof value.catalogHash === "string")
377
+ );
378
+ }
379
+
380
+ function normalizeDeferredToolCarryover(value: DeferredToolCarryoverV1): DeferredToolCarryoverV1 {
381
+ if (!isDeferredToolCarryover(value)) {
382
+ throw new Error("Invalid deferred tool carryover");
383
+ }
384
+
385
+ return {
386
+ version: 1,
387
+ source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
388
+ toolNames: [...new Set(value.toolNames.map((name) => name.trim()).filter(Boolean))].sort(),
389
+ ...(typeof value.catalogHash === "string" ? { catalogHash: value.catalogHash } : {}),
390
+ };
391
+ }
392
+
393
+ function decodeCacheStackActivationState(value: unknown): DeferredToolCarryoverV1 | undefined {
394
+ if (!isRecord(value) || value.version !== 1 || typeof value.catalogHash !== "string") {
395
+ return undefined;
396
+ }
397
+ if (!Array.isArray(value.activatedTools) || !value.activatedTools.every((name) => typeof name === "string")) {
398
+ return undefined;
399
+ }
400
+
401
+ return normalizeDeferredToolCarryover({
402
+ version: 1,
403
+ source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
404
+ toolNames: value.activatedTools,
405
+ catalogHash: value.catalogHash,
406
+ });
407
+ }
408
+
409
+ /** Read only the latest valid cache-stack activation snapshot on this branch. */
410
+ export function getLatestDeferredToolCarryover(
411
+ entries: readonly SessionEntry[],
412
+ ): DeferredToolCarryoverV1 | undefined {
413
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
414
+ const entry = entries[index];
415
+ if (entry?.type !== "custom" || entry.customType !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
416
+ continue;
417
+ }
418
+
419
+ const decoded = decodeCacheStackActivationState(entry.data);
420
+ if (decoded) return decoded;
421
+ }
422
+
423
+ return undefined;
424
+ }
425
+
426
+ export function isNativeCompactionDetails(value: unknown): value is NativeCompactionDetails {
427
+ if (!isRecord(value) || !isNativeCompactionIdentity(value)) {
428
+ return false;
429
+ }
430
+
431
+ const candidate = value as Record<string, unknown>;
432
+ return (
433
+ (candidate.strategy === LEGACY_NATIVE_COMPACTION_STRATEGY ||
434
+ candidate.strategy === REMOTE_V2_COMPACTION_STRATEGY) &&
435
+ (candidate.inputProvenance === undefined || isNativeCompactionInputProvenance(candidate.inputProvenance)) &&
436
+ (candidate.compactionModel === undefined || isNativeCompactionIdentity(candidate.compactionModel)) &&
437
+ (candidate.deferredToolCarryover === undefined || isDeferredToolCarryover(candidate.deferredToolCarryover)) &&
438
+ Array.isArray(candidate.compactedWindow) &&
439
+ candidate.compactedWindow.every(isCompactedWindowItem) &&
440
+ isNonEmptyString(candidate.createdAt) &&
441
+ (candidate.compactResponseId === undefined || isNonEmptyString(candidate.compactResponseId)) &&
442
+ (candidate.requestMeta === undefined || isNativeCompactionRequestMeta(candidate.requestMeta))
443
+ );
444
+ }
445
+
446
+ export function isNativeCompactionEntry(value: unknown): value is NativeCompactionEntry {
447
+ return isRecord(value) && value.type === "compaction" && isNativeCompactionDetails(value.details);
448
+ }
449
+
450
+ export function createNativeCompactionDetails(input: CreateNativeCompactionDetailsInput): NativeCompactionDetails {
451
+ if (input.strategy !== undefined && input.strategy !== LEGACY_NATIVE_COMPACTION_STRATEGY && input.strategy !== REMOTE_V2_COMPACTION_STRATEGY) {
452
+ throw new Error("Invalid native compaction strategy");
453
+ }
454
+ return {
455
+ strategy: input.strategy ?? NATIVE_COMPACTION_STRATEGY,
456
+ inputProvenance: input.inputProvenance,
457
+ provider: normalizeString(input.provider),
458
+ api: normalizeString(input.api),
459
+ model: normalizeString(input.model),
460
+ baseUrl: normalizeString(input.baseUrl),
461
+ compactionModel: input.compactionModel
462
+ ? {
463
+ provider: normalizeString(input.compactionModel.provider),
464
+ api: normalizeString(input.compactionModel.api),
465
+ model: normalizeString(input.compactionModel.model),
466
+ baseUrl: normalizeString(input.compactionModel.baseUrl),
467
+ }
468
+ : undefined,
469
+ deferredToolCarryover: input.deferredToolCarryover
470
+ ? normalizeDeferredToolCarryover(input.deferredToolCarryover)
471
+ : undefined,
472
+ compactedWindow: input.compactedWindow.map((item) => cloneStructuredValue(item)),
473
+ compactResponseId: isNonEmptyString(input.compactResponseId) ? normalizeString(input.compactResponseId) : undefined,
474
+ createdAt: isNonEmptyString(input.createdAt) ? normalizeString(input.createdAt) : new Date().toISOString(),
475
+ requestMeta: input.requestMeta
476
+ ? {
477
+ ...(input.requestMeta.tokensBefore !== undefined ? { tokensBefore: input.requestMeta.tokensBefore } : {}),
478
+ ...(input.requestMeta.previousSummaryPresent !== undefined
479
+ ? { previousSummaryPresent: input.requestMeta.previousSummaryPresent }
480
+ : {}),
481
+ }
482
+ : undefined,
483
+ };
484
+ }
485
+
486
+ export function createNativeCompactionResult(
487
+ input: CreateNativeCompactionResultInput,
488
+ ): CompactionResult<NativeCompactionDetails> {
489
+ const summary = input.summary?.trim();
490
+ return {
491
+ summary: summary && summary.length > 0 ? summary
492
+ : input.details.strategy === LEGACY_NATIVE_COMPACTION_STRATEGY ? STANDALONE_COMPACTION_FALLBACK_SUMMARY
493
+ : NATIVE_COMPACTION_FALLBACK_SUMMARY,
494
+ firstKeptEntryId: input.firstKeptEntryId,
495
+ tokensBefore: input.tokensBefore,
496
+ details: input.details,
497
+ };
498
+ }
499
+
500
+ export const DEFAULT_NATIVE_FALLBACK_CONFIG: NativeFallbackConfig = {
501
+ enabled: true,
502
+ model: undefined,
503
+ thinkingLevel: "off",
504
+ };
505
+
506
+ export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
507
+ enabled: true,
508
+ allowCompactionContinuityBreak: false,
509
+ remoteCompactModel: undefined,
510
+ remoteV2ContextSource: "legacy",
511
+
512
+ nativeFallback: { ...DEFAULT_NATIVE_FALLBACK_CONFIG },
513
+ responsesApis: [...RESPONSES_COMPACT_CAPABLE_APIS],
514
+ gatewayContextModels: [],
515
+ notifyOnLoad: false,
516
+ debug: false,
517
+ logProviderPayloads: false,
518
+ logCompactResponses: false,
519
+ redactSensitiveData: true,
520
+ artifactRoot: DEFAULT_ARTIFACT_ROOT,
521
+ };
522
+
523
+ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchConfig = {
524
+ enabled: true,
525
+ models: [],
526
+ };
527
+
528
+ /** Single source of truth for the hosted image model used when nothing is configured. */
529
+ export const DEFAULT_IMAGE_GENERATION_MODEL = "grok-imagine-image-2.0";
530
+ export const DEFAULT_IMAGE_GENERATION_MODELS: readonly string[] = [DEFAULT_IMAGE_GENERATION_MODEL];
531
+
532
+ export const DEFAULT_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {
533
+ enabled: false,
534
+ models: [...DEFAULT_IMAGE_GENERATION_MODELS],
535
+ };
536
+
537
+ export const DEFAULT_TOOLKIT_CONFIG: ToolkitConfig = {
538
+ compaction: DEFAULT_COMPACTION_CONFIG,
539
+ webSearch: DEFAULT_WEB_SEARCH_CONFIG,
540
+ imageGeneration: DEFAULT_IMAGE_GENERATION_CONFIG,
541
+ };