pi-openai-toolkit 0.7.1 → 0.8.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.
@@ -9,31 +9,16 @@ import type {
9
9
  import type { ResponsesCompatibleRequestPayload } from "./runtime";
10
10
  import { rewritePayloadWithDeferredToolCarryover } from "./deferred-tool-carryover";
11
11
  import type { NativeCompactionEntry } from "./types";
12
- import {
13
- compareResponsesInputParity,
14
- serializeMessagesToResponsesInput,
15
- type ResponsesInputContentItem,
16
- type ResponsesInputItem,
17
- type ResponsesInputMessageItem,
18
- } from "./serializer";
19
-
20
- export type SerializedReplaySlice = {
21
- entries: SessionEntry[];
22
- messages: AgentMessage[];
23
- input: ResponsesInputItem[];
24
- };
12
+ import { serializeMessagesToResponsesInput, type ResponsesInputContentItem, type ResponsesInputItem, type ResponsesInputMessageItem } from "./serializer";
25
13
 
26
14
  export type NativeReplaySegments = {
27
15
  boundaryIndex: number;
28
16
  firstKeptEntryIndex: number;
29
17
  instructions?: string;
30
- freshPreamble: ResponsesInputMessageItem[];
31
- trailingPreamble: ResponsesInputMessageItem[];
18
+ leading: ResponsesInputItem[];
32
19
  compactionSummary: ResponsesInputItem[];
33
- preCompactionKeptWindow: SerializedReplaySlice;
34
20
  compactedWindow: unknown[];
35
- postCompactionTail: SerializedReplaySlice;
36
- originalPiReplayInput: ResponsesInputItem[];
21
+ post: ResponsesInputItem[];
37
22
  replayInput: unknown[];
38
23
  };
39
24
 
@@ -48,7 +33,6 @@ export type NativeReplayPayloadRewriteFailureReason =
48
33
  | "first-kept-entry-not-found"
49
34
  | "invalid-compacted-window"
50
35
  | "unexpected-compaction-after-boundary"
51
- | "expected-pi-replay-mismatch"
52
36
  | "compaction-summary-not-found";
53
37
 
54
38
  export type NativeReplayPayloadRewriteFailure = {
@@ -89,19 +73,6 @@ function isResponsesInputMessageRole(value: unknown): value is ResponsesInputMes
89
73
  return value === "user" || value === "developer" || value === "system";
90
74
  }
91
75
 
92
- function isPreambleRole(value: ResponsesInputMessageItem["role"]): value is "developer" | "system" {
93
- return value === "developer" || value === "system";
94
- }
95
-
96
- function isResponsesInputMessageItem(value: unknown): value is ResponsesInputMessageItem {
97
- if (!isRecord(value) || !isResponsesInputMessageRole(value.role)) {
98
- return false;
99
- }
100
-
101
- const { content } = value;
102
- return typeof content === "string" || (Array.isArray(content) && content.every(isResponsesInputContentItem));
103
- }
104
-
105
76
  function cloneResponsesInputContentItem(item: ResponsesInputContentItem): ResponsesInputContentItem {
106
77
  return item.type === "input_text"
107
78
  ? {
@@ -294,20 +265,6 @@ function findCompactionSummaryIndex(input: readonly unknown[], summaryMarker: st
294
265
  });
295
266
  }
296
267
 
297
- /** Count consecutive provider-authored developer/system items at the end of the input. */
298
- function countTrailingPreambleItems(items: readonly unknown[]): number {
299
- let count = 0;
300
- for (let index = items.length - 1; index >= 0; index--) {
301
- const item = items[index];
302
- if (isResponsesInputMessageItem(item) && isPreambleRole(item.role)) {
303
- count += 1;
304
- continue;
305
- }
306
- break;
307
- }
308
- return count;
309
- }
310
-
311
268
  function collectReplayMessages(entries: readonly SessionEntry[]): AgentMessage[] {
312
269
  const messages: AgentMessage[] = [];
313
270
 
@@ -321,27 +278,6 @@ function collectReplayMessages(entries: readonly SessionEntry[]): AgentMessage[]
321
278
  return messages;
322
279
  }
323
280
 
324
- function createCompactionSummaryAgentMessage(entry: NativeCompactionEntry): AgentMessage {
325
- return {
326
- role: "compactionSummary",
327
- summary: entry.summary,
328
- tokensBefore: entry.tokensBefore,
329
- timestamp: new Date(entry.timestamp).getTime(),
330
- } as AgentMessage;
331
- }
332
-
333
- function createReplaySlice(
334
- entries: readonly SessionEntry[],
335
- messages: readonly AgentMessage[],
336
- input: readonly ResponsesInputItem[],
337
- ): SerializedReplaySlice {
338
- return {
339
- entries: [...entries],
340
- messages: [...messages],
341
- input: [...input],
342
- };
343
- }
344
-
345
281
  function findEntryIndexByIdBeforeBoundary(
346
282
  entries: readonly SessionEntry[],
347
283
  entryId: string,
@@ -436,14 +372,13 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
436
372
 
437
373
  const preCompactionEntries = args.branchEntries.slice(firstKeptEntryIndex, boundaryIndex);
438
374
  const postCompactionEntries = args.branchEntries.slice(boundaryIndex + 1);
439
- const preCompactionKeptMessages = collectReplayMessages(preCompactionEntries);
440
- const postCompactionTailMessages = collectReplayMessages(postCompactionEntries);
441
- const compactionSummaryMessage = createCompactionSummaryAgentMessage(args.compactionEntry);
442
- const keptWindowSerialized = serializeMessagesToResponsesInput(args.model, preCompactionKeptMessages);
443
-
444
- // Pi-authored compaction summary item. Everything outside the replaced region
445
- // is preserved verbatim, so this content anchor is the only location probe
446
- // needed; the marker text is unique to this compaction entry.
375
+
376
+ // Pi-authored compaction summary item. Pi 0.84.3 renders the context after a
377
+ // compaction as [leading prompts][summary item][all post-compaction messages]:
378
+ // pre-compaction messages never appear in the payload (entry.retainedTail is
379
+ // never persisted by appendCompaction), so the summary item itself is the only
380
+ // anchor and the only thing that gets replaced. Everything else is preserved
381
+ // verbatim, so transient messages injected by other extensions stay intact.
447
382
  const summaryIndex = findCompactionSummaryIndex(args.payload.input, args.compactionEntry.summary);
448
383
  if (summaryIndex < 0) {
449
384
  return {
@@ -452,66 +387,19 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
452
387
  };
453
388
  }
454
389
 
455
- const keptStart = summaryIndex + 1;
456
- const keptEnd = keptStart + keptWindowSerialized.length;
457
- const actualKeptWindow = cloneResponsesInputSlice(args.payload.input.slice(keptStart, keptEnd));
458
- if (!actualKeptWindow) {
459
- return {
460
- ok: false,
461
- reason: "expected-pi-replay-mismatch",
462
- };
463
- }
464
-
465
- // The only hard verification: the region immediately after the sentinel must be
466
- // exactly the kept pre-compaction window derived from session entries. Content
467
- // after this region is preserved as-is, so transient messages injected by other
468
- // extensions (which have no session entries) no longer break replay.
469
- if (!areEquivalentValues(actualKeptWindow, keptWindowSerialized)) {
470
- const parity = compareResponsesInputParity(
471
- args.payload.input.slice(keptStart, keptEnd),
472
- keptWindowSerialized,
473
- );
474
- return {
475
- ok: false,
476
- reason: "expected-pi-replay-mismatch",
477
- parity: {
478
- actual: parity.actual,
479
- expected: parity.expected,
480
- mismatches: parity.mismatches,
481
- },
482
- };
483
- }
484
-
485
- const actualCompactionSummary = cloneResponsesInputSlice([args.payload.input[summaryIndex]]);
486
- const actualPostCompactionTail = cloneResponsesInputSlice(args.payload.input.slice(keptEnd));
487
- if (!actualCompactionSummary || !actualPostCompactionTail) {
488
- return {
489
- ok: false,
490
- reason: "expected-pi-replay-mismatch",
491
- };
492
- }
493
-
494
- // Keep provider-authored trailing developer/system prompts in place.
495
- const trailingCount = countTrailingPreambleItems(actualPostCompactionTail);
496
- const trailingPreamble = actualPostCompactionTail.slice(actualPostCompactionTail.length - trailingCount);
497
- const liveTail = actualPostCompactionTail.slice(0, actualPostCompactionTail.length - trailingCount);
498
-
499
390
  const leadingInput = cloneResponsesInputSlice(args.payload.input.slice(0, summaryIndex));
500
- if (!leadingInput) {
391
+ const actualCompactionSummary = cloneResponsesInputSlice([args.payload.input[summaryIndex]]);
392
+ const actualPostInput = cloneResponsesInputSlice(args.payload.input.slice(summaryIndex + 1));
393
+ if (!leadingInput || !actualCompactionSummary || !actualPostInput) {
501
394
  return {
502
395
  ok: false,
503
- reason: "expected-pi-replay-mismatch",
396
+ reason: "compaction-summary-not-found",
504
397
  };
505
398
  }
506
399
 
507
400
  const baseRewrittenPayload: ResponsesCompatibleRequestPayload = {
508
401
  ...args.payload,
509
- input: [
510
- ...leadingInput,
511
- ...compactedWindow,
512
- ...liveTail,
513
- ...trailingPreamble,
514
- ],
402
+ input: [...leadingInput, ...compactedWindow, ...actualPostInput],
515
403
  };
516
404
  const carryoverRewrite = rewritePayloadWithDeferredToolCarryover({
517
405
  payload: baseRewrittenPayload,
@@ -523,33 +411,16 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
523
411
  | undefined,
524
412
  });
525
413
 
526
- const sentinelSerialized = serializeMessagesToResponsesInput(args.model, [compactionSummaryMessage]);
527
- const serializedTailInput = serializeMessagesToResponsesInput(args.model, postCompactionTailMessages);
528
- const originalPiReplayInput: ResponsesInputItem[] = [
529
- ...leadingInput,
530
- ...sentinelSerialized,
531
- ...keptWindowSerialized,
532
- ...serializedTailInput,
533
- ...trailingPreamble,
534
- ];
535
-
536
414
  return {
537
415
  ok: true,
538
416
  segments: {
539
417
  boundaryIndex,
540
418
  firstKeptEntryIndex,
541
419
  instructions: typeof args.payload.instructions === "string" ? args.payload.instructions : undefined,
542
- freshPreamble: leadingInput as ResponsesInputMessageItem[],
543
- trailingPreamble: trailingPreamble as ResponsesInputMessageItem[],
420
+ leading: leadingInput,
544
421
  compactionSummary: actualCompactionSummary,
545
- preCompactionKeptWindow: createReplaySlice(
546
- preCompactionEntries,
547
- preCompactionKeptMessages,
548
- actualKeptWindow,
549
- ),
550
422
  compactedWindow,
551
- postCompactionTail: createReplaySlice(postCompactionEntries, postCompactionTailMessages, liveTail),
552
- originalPiReplayInput,
423
+ post: actualPostInput,
553
424
  replayInput: carryoverRewrite.payload.input,
554
425
  },
555
426
  rewrittenPayload: carryoverRewrite.payload,
@@ -1,5 +1,5 @@
1
1
  import { writeDebugArtifact } from "./debug";
2
- import { mergeProviderHeaders } from "./provider-headers";
2
+ import { buildResponsesRequestHeaders } from "./responses-headers";
3
3
  import type { NativeCompactionRuntime } from "./runtime";
4
4
  import type { NativeCompactionRequestBody, ResponsesInputItem } from "./serializer";
5
5
  import type { ArtifactContext, CompactionConfig } from "./types";
@@ -114,57 +114,13 @@ function normalizeResponseTimestamp(value: unknown): string | undefined {
114
114
  return Number.isNaN(parsed) ? trimmed : new Date(parsed).toISOString();
115
115
  }
116
116
 
117
- function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
118
- const parts = token.split(".");
119
- if (parts.length !== 3) {
120
- return undefined;
121
- }
122
-
123
- try {
124
- const payloadText = Buffer.from(parts[1]!, "base64url").toString("utf8");
125
- const payload = JSON.parse(payloadText);
126
- return isRecord(payload) ? payload : undefined;
127
- } catch {
128
- return undefined;
129
- }
130
- }
131
-
132
- function extractCodexAccountId(token: string): string | undefined {
133
- const payload = decodeJwtPayload(token);
134
- const authClaims = payload?.["https://api.openai.com/auth"];
135
- if (!isRecord(authClaims)) {
136
- return undefined;
137
- }
138
-
139
- const accountId = authClaims.chatgpt_account_id;
140
- return isNonEmptyString(accountId) ? accountId.trim() : undefined;
141
- }
142
-
143
- function buildCodexUserAgent(): string {
144
- const platform = typeof process !== "undefined" ? process.platform : "browser";
145
- const arch = typeof process !== "undefined" ? process.arch : "unknown";
146
- return `pi (${platform}; ${arch})`;
147
- }
148
-
149
117
  function toHeaders(runtime: NativeCompactionRuntime): Record<string, string> {
150
- const headers = new Headers(mergeProviderHeaders(runtime.currentModel.headers, runtime.headers));
151
- headers.set("accept", SSE_CONTENT_TYPE);
152
- headers.set("content-type", JSON_CONTENT_TYPE);
153
- if (!headers.has("authorization")) {
154
- headers.set("authorization", `Bearer ${runtime.apiKey}`);
155
- }
156
-
157
- if (runtime.api === "openai-codex-responses") {
158
- const accountId = extractCodexAccountId(runtime.apiKey);
159
- if (accountId) {
160
- headers.set("chatgpt-account-id", accountId);
161
- }
162
- headers.set("originator", "pi");
163
- headers.set("user-agent", buildCodexUserAgent());
164
- headers.set("openai-beta", "responses=experimental");
165
- }
166
-
167
- return Object.fromEntries(headers.entries());
118
+ return Object.fromEntries(
119
+ buildResponsesRequestHeaders(runtime, {
120
+ accept: SSE_CONTENT_TYPE,
121
+ contentType: JSON_CONTENT_TYPE,
122
+ }).entries(),
123
+ );
168
124
  }
169
125
 
170
126
  export function buildRemoteV2CompactionRequest(
@@ -0,0 +1,67 @@
1
+ import type { ProviderHeaders } from "@earendil-works/pi-ai";
2
+ import { mergeProviderHeaders } from "./provider-headers";
3
+
4
+ export type ResponsesHeaderRuntime = {
5
+ api: string;
6
+ apiKey: string;
7
+ headers?: ProviderHeaders;
8
+ currentModel: {
9
+ headers?: ProviderHeaders;
10
+ };
11
+ };
12
+
13
+ function isRecord(value: unknown): value is Record<string, unknown> {
14
+ return !!value && typeof value === "object" && !Array.isArray(value);
15
+ }
16
+
17
+ function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
18
+ const parts = token.split(".");
19
+ if (parts.length !== 3) return undefined;
20
+ try {
21
+ const payload = JSON.parse(Buffer.from(parts[1]!, "base64url").toString("utf8")) as unknown;
22
+ return isRecord(payload) ? payload : undefined;
23
+ } catch {
24
+ return undefined;
25
+ }
26
+ }
27
+
28
+ function extractCodexAccountId(token: string): string | undefined {
29
+ const authClaims = decodeJwtPayload(token)?.["https://api.openai.com/auth"];
30
+ if (!isRecord(authClaims)) return undefined;
31
+ const accountId = authClaims.chatgpt_account_id;
32
+ return typeof accountId === "string" && accountId.trim() ? accountId.trim() : undefined;
33
+ }
34
+
35
+ function buildCodexUserAgent(): string {
36
+ const platform = typeof process !== "undefined" ? process.platform : "browser";
37
+ const arch = typeof process !== "undefined" ? process.arch : "unknown";
38
+ return `pi (${platform}; ${arch})`;
39
+ }
40
+
41
+ export function buildResponsesRequestHeaders(
42
+ runtime: ResponsesHeaderRuntime,
43
+ options: { accept: string; contentType?: string },
44
+ ): Headers {
45
+ const headers = new Headers(
46
+ mergeProviderHeaders(runtime.currentModel.headers, runtime.headers),
47
+ );
48
+ headers.set("accept", options.accept);
49
+ headers.set("content-type", options.contentType ?? "application/json");
50
+ if (!headers.has("authorization")) {
51
+ headers.set("authorization", `Bearer ${runtime.apiKey}`);
52
+ }
53
+
54
+ if (runtime.api === "openai-codex-responses") {
55
+ const accountId = extractCodexAccountId(runtime.apiKey);
56
+ if (accountId) headers.set("chatgpt-account-id", accountId);
57
+ headers.set("originator", "pi");
58
+ headers.set("user-agent", buildCodexUserAgent());
59
+ headers.set("openai-beta", "responses=experimental");
60
+ }
61
+
62
+ return headers;
63
+ }
64
+
65
+ export const _responsesHeadersTest = {
66
+ extractCodexAccountId,
67
+ };
package/src/runtime.ts CHANGED
@@ -7,8 +7,9 @@ const CODEX_RESPONSES_PATH = "codex/responses";
7
7
  const OPENAI_COMPACT_PATH = "responses/compact";
8
8
  const CODEX_COMPACT_PATH = "codex/responses/compact";
9
9
 
10
- type ResponsesCompactApi = (typeof RESPONSES_COMPACT_CAPABLE_APIS)[number];
10
+ export type ResponsesApi = (typeof RESPONSES_COMPACT_CAPABLE_APIS)[number];
11
11
 
12
+ type ResponsesCompactApi = ResponsesApi;
12
13
  type RuntimeModel = Model<Api>;
13
14
 
14
15
  export type ParsedModelSpec = {
@@ -35,6 +36,8 @@ export type NativeCompactionSupportOptions = {
35
36
  responsesApis?: readonly string[];
36
37
  };
37
38
 
39
+ export type ResponsesSupportOptions = NativeCompactionSupportOptions;
40
+
38
41
  export type ResponsesCompatibleRequestPayload = {
39
42
  model: string;
40
43
  input: unknown[];
@@ -42,19 +45,22 @@ export type ResponsesCompatibleRequestPayload = {
42
45
  [key: string]: unknown;
43
46
  };
44
47
 
45
- export type NativeCompactionRuntime = {
48
+ export type ResponsesRuntime = {
46
49
  provider: string;
47
- api: ResponsesCompactApi;
50
+ api: ResponsesApi;
48
51
  model: string;
49
52
  baseUrl: string;
50
53
  apiKey: string;
51
54
  headers?: ProviderHeaders;
52
55
  responsesPath: string;
53
56
  responsesUrl: string;
57
+ currentModel: RuntimeModel;
58
+ };
59
+
60
+ export type NativeCompactionRuntime = ResponsesRuntime & {
54
61
  compactPath: string;
55
62
  compactUrl: string;
56
63
  payload?: ResponsesCompatibleRequestPayload;
57
- currentModel: RuntimeModel;
58
64
  };
59
65
 
60
66
  export type NativeCompactionEnvironmentFailure = {
@@ -77,6 +83,15 @@ export type NativeCompactionEnvironmentResolution =
77
83
  | NativeCompactionEnvironmentFailure
78
84
  | NativeCompactionEnvironmentSuccess;
79
85
 
86
+ export type ResponsesEnvironmentSuccess = {
87
+ ok: true;
88
+ runtime: ResponsesRuntime;
89
+ };
90
+
91
+ export type ResponsesEnvironmentResolution =
92
+ | NativeCompactionEnvironmentFailure
93
+ | ResponsesEnvironmentSuccess;
94
+
80
95
  export type RemoteCompactionExecution = {
81
96
  /** Active session model. Its identity owns replay matching and is never mutated. */
82
97
  consumer: NativeCompactionRuntime;
@@ -327,6 +342,30 @@ async function resolveNativeCompactionEnvironmentForModel(
327
342
  };
328
343
  }
329
344
 
345
+ export async function resolveResponsesEnvironment(
346
+ ctx: ExtensionContext,
347
+ options: ResponsesSupportOptions = {},
348
+ ): Promise<ResponsesEnvironmentResolution> {
349
+ const resolution = await resolveNativeCompactionEnvironmentForModel(ctx, ctx.model, options);
350
+ if (!resolution.ok) return resolution;
351
+
352
+ const runtime = resolution.runtime;
353
+ return {
354
+ ok: true,
355
+ runtime: {
356
+ provider: runtime.provider,
357
+ api: runtime.api,
358
+ model: runtime.model,
359
+ baseUrl: runtime.baseUrl,
360
+ apiKey: runtime.apiKey,
361
+ headers: runtime.headers,
362
+ responsesPath: runtime.responsesPath,
363
+ responsesUrl: runtime.responsesUrl,
364
+ currentModel: runtime.currentModel,
365
+ },
366
+ };
367
+ }
368
+
330
369
  export async function resolveNativeCompactionEnvironment(
331
370
  ctx: ExtensionContext,
332
371
  options: NativeCompactionSupportOptions = {},
package/src/types.ts CHANGED
@@ -4,6 +4,7 @@ import type { CompactionEntry, CompactionResult, ExtensionContext, SessionEntry
4
4
  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
+ export const IMAGE_GENERATION_EXTENSION_ID = `${TOOLKIT_ID}:image-generation`;
7
8
  export const DEFAULT_ARTIFACT_ROOT = "~/.pi/agent/artifacts/pi-openai-toolkit/compaction";
8
9
  export const REDACTED_VALUE = "[REDACTED]";
9
10
  /**
@@ -82,9 +83,16 @@ export type WebSearchConfig = {
82
83
  models: string[];
83
84
  };
84
85
 
86
+ export type ImageGenerationConfig = {
87
+ enabled: boolean;
88
+ /** Exact provider/model keys allowed to use toolkit-native image generation. */
89
+ models: string[];
90
+ };
91
+
85
92
  export type ToolkitConfig = {
86
93
  compaction: CompactionConfig;
87
94
  webSearch: WebSearchConfig;
95
+ imageGeneration: ImageGenerationConfig;
88
96
  };
89
97
 
90
98
  export type LoadedToolkitConfig = {
@@ -438,7 +446,13 @@ export const DEFAULT_WEB_SEARCH_CONFIG: WebSearchConfig = {
438
446
  models: [],
439
447
  };
440
448
 
449
+ export const DEFAULT_IMAGE_GENERATION_CONFIG: ImageGenerationConfig = {
450
+ enabled: true,
451
+ models: [],
452
+ };
453
+
441
454
  export const DEFAULT_TOOLKIT_CONFIG: ToolkitConfig = {
442
455
  compaction: DEFAULT_COMPACTION_CONFIG,
443
456
  webSearch: DEFAULT_WEB_SEARCH_CONFIG,
457
+ imageGeneration: DEFAULT_IMAGE_GENERATION_CONFIG,
444
458
  };
@@ -1,3 +1,4 @@
1
+ import { getExactModelKey, isExactModelAllowed } from "../model-scope";
1
2
  import type { WebSearchConfig } from "../types";
2
3
 
3
4
  export const WEB_SEARCH_CAPABLE_APIS = ["openai-responses", "openai-codex-responses"] as const;
@@ -28,20 +29,17 @@ export type WebSearchPayloadTransform = {
28
29
  };
29
30
 
30
31
  export function getWebSearchModelKey(model: WebSearchModel | undefined): string | undefined {
31
- if (!model?.provider || !model.id) return undefined;
32
- return `${model.provider}/${model.id}`;
32
+ return getExactModelKey(model);
33
33
  }
34
34
 
35
35
  export function isWebSearchEnabledForModel(
36
36
  model: WebSearchModel | undefined,
37
37
  config: WebSearchConfig,
38
38
  ): boolean {
39
- const modelKey = getWebSearchModelKey(model);
40
39
  return (
41
40
  config.enabled &&
42
41
  typeof model?.api === "string" &&
43
42
  (WEB_SEARCH_CAPABLE_APIS as readonly string[]).includes(model.api) &&
44
- modelKey !== undefined &&
45
- config.models.includes(modelKey)
43
+ isExactModelAllowed(model, config.models)
46
44
  );
47
45
  }
@@ -14,6 +14,7 @@
14
14
  "include": [
15
15
  "extensions/compaction.ts",
16
16
  "extensions/web-search.ts",
17
+ "extensions/image-generation.ts",
17
18
  "src/compact-client.ts",
18
19
  "src/config.ts",
19
20
  "src/debug.ts",
@@ -21,9 +22,20 @@
21
22
  "src/deferred-tool-carryover.ts",
22
23
  "src/inline-compaction.ts",
23
24
  "src/extension-runtime.ts",
25
+ "src/image-generation/artifacts.ts",
26
+ "src/image-generation/client.ts",
27
+ "src/image-generation/eligibility.ts",
28
+ "src/image-generation/extension.ts",
29
+ "src/image-generation/protocol.ts",
30
+ "src/image-generation/references.ts",
31
+ "src/image-generation/render.ts",
32
+ "src/image-generation/service.ts",
33
+ "src/image-generation/types.ts",
34
+ "src/model-scope.ts",
24
35
  "src/native-fallback.ts",
25
36
  "src/payload-rewrite.ts",
26
37
  "src/provider-headers.ts",
38
+ "src/responses-headers.ts",
27
39
  "src/remote-v2-client.ts",
28
40
  "src/request-context-cache.ts",
29
41
  "src/runtime.ts",