@code-yeongyu/senpi 2026.9.8 → 2026.9.9-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 (185) hide show
  1. package/CHANGELOG.md +43 -3
  2. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  3. package/dist/core/agent-session-runtime.js +2 -0
  4. package/dist/core/agent-session-runtime.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +8 -1
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +33 -0
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/compaction/compaction-execution.d.ts.map +1 -1
  10. package/dist/core/compaction/compaction-execution.js +5 -5
  11. package/dist/core/compaction/compaction-execution.js.map +1 -1
  12. package/dist/core/compaction/compaction-settings.d.ts +2 -0
  13. package/dist/core/compaction/compaction-settings.d.ts.map +1 -1
  14. package/dist/core/compaction/compaction-settings.js.map +1 -1
  15. package/dist/core/compaction/compaction.d.ts +3 -3
  16. package/dist/core/compaction/compaction.d.ts.map +1 -1
  17. package/dist/core/compaction/compaction.js +12 -7
  18. package/dist/core/compaction/compaction.js.map +1 -1
  19. package/dist/core/compaction/stream-watchdog.d.ts +29 -0
  20. package/dist/core/compaction/stream-watchdog.d.ts.map +1 -1
  21. package/dist/core/compaction/stream-watchdog.js +35 -0
  22. package/dist/core/compaction/stream-watchdog.js.map +1 -1
  23. package/dist/core/compaction-settings-access.d.ts +6 -0
  24. package/dist/core/compaction-settings-access.d.ts.map +1 -1
  25. package/dist/core/compaction-settings-access.js.map +1 -1
  26. package/dist/core/compaction-settings-resolver.d.ts +2 -0
  27. package/dist/core/compaction-settings-resolver.d.ts.map +1 -1
  28. package/dist/core/compaction-settings-resolver.js +5 -0
  29. package/dist/core/compaction-settings-resolver.js.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +4 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  33. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts +1 -1
  34. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/compaction/model-usability-budget.js +23 -3
  36. package/dist/core/extensions/builtin/compaction/model-usability-budget.js.map +1 -1
  37. package/dist/core/extensions/builtin/compaction/resume-admission.d.ts +7 -0
  38. package/dist/core/extensions/builtin/compaction/resume-admission.d.ts.map +1 -0
  39. package/dist/core/extensions/builtin/compaction/resume-admission.js +7 -0
  40. package/dist/core/extensions/builtin/compaction/resume-admission.js.map +1 -0
  41. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +2 -0
  42. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/compaction/speculative-summary.js +5 -2
  44. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -1
  45. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/compaction/speculative.js +9 -3
  47. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  48. package/dist/core/extensions/builtin/compaction/summarization-retry.d.ts +10 -4
  49. package/dist/core/extensions/builtin/compaction/summarization-retry.d.ts.map +1 -1
  50. package/dist/core/extensions/builtin/compaction/summarization-retry.js +14 -5
  51. package/dist/core/extensions/builtin/compaction/summarization-retry.js.map +1 -1
  52. package/dist/core/extensions/builtin/imagegen/params.d.ts +36 -0
  53. package/dist/core/extensions/builtin/imagegen/params.d.ts.map +1 -0
  54. package/dist/core/extensions/builtin/imagegen/params.js +55 -0
  55. package/dist/core/extensions/builtin/imagegen/params.js.map +1 -0
  56. package/dist/core/extensions/builtin/imagegen/reference-images.d.ts +11 -0
  57. package/dist/core/extensions/builtin/imagegen/reference-images.d.ts.map +1 -0
  58. package/dist/core/extensions/builtin/imagegen/reference-images.js +55 -0
  59. package/dist/core/extensions/builtin/imagegen/reference-images.js.map +1 -0
  60. package/dist/core/extensions/builtin/imagegen/skill/SKILL.md +47 -4
  61. package/dist/core/extensions/builtin/imagegen/tool.d.ts +10 -19
  62. package/dist/core/extensions/builtin/imagegen/tool.d.ts.map +1 -1
  63. package/dist/core/extensions/builtin/imagegen/tool.js +21 -45
  64. package/dist/core/extensions/builtin/imagegen/tool.js.map +1 -1
  65. package/dist/core/extensions/loader.d.ts +2 -1
  66. package/dist/core/extensions/loader.d.ts.map +1 -1
  67. package/dist/core/extensions/loader.js +17 -8
  68. package/dist/core/extensions/loader.js.map +1 -1
  69. package/dist/core/extensions/types.d.ts +5 -0
  70. package/dist/core/extensions/types.d.ts.map +1 -1
  71. package/dist/core/extensions/types.js.map +1 -1
  72. package/dist/core/resource-loader.d.ts +2 -0
  73. package/dist/core/resource-loader.d.ts.map +1 -1
  74. package/dist/core/resource-loader.js +11 -7
  75. package/dist/core/resource-loader.js.map +1 -1
  76. package/dist/core/sdk.d.ts.map +1 -1
  77. package/dist/core/sdk.js +13 -1
  78. package/dist/core/sdk.js.map +1 -1
  79. package/dist/core/session-manager.d.ts.map +1 -1
  80. package/dist/core/session-manager.js +11 -1
  81. package/dist/core/session-manager.js.map +1 -1
  82. package/dist/core/session-write-reservation.d.ts +4 -0
  83. package/dist/core/session-write-reservation.d.ts.map +1 -0
  84. package/dist/core/session-write-reservation.js +12 -0
  85. package/dist/core/session-write-reservation.js.map +1 -0
  86. package/dist/core/tools/read-classifiers.d.ts +17 -0
  87. package/dist/core/tools/read-classifiers.d.ts.map +1 -0
  88. package/dist/core/tools/read-classifiers.js +26 -0
  89. package/dist/core/tools/read-classifiers.js.map +1 -0
  90. package/dist/core/tools/read.d.ts +5 -1
  91. package/dist/core/tools/read.d.ts.map +1 -1
  92. package/dist/core/tools/read.js +21 -1
  93. package/dist/core/tools/read.js.map +1 -1
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +1 -0
  97. package/dist/index.js.map +1 -1
  98. package/dist/main.d.ts +14 -0
  99. package/dist/main.d.ts.map +1 -1
  100. package/dist/main.js +166 -147
  101. package/dist/main.js.map +1 -1
  102. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  103. package/dist/modes/interactive/interactive-mode.js +3 -0
  104. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  105. package/dist/modes/rpc/connection-handler.d.ts +2 -0
  106. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  107. package/dist/modes/rpc/connection-handler.js +1 -1
  108. package/dist/modes/rpc/connection-handler.js.map +1 -1
  109. package/dist/modes/rpc/multi-session-host.d.ts +2 -0
  110. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  111. package/dist/modes/rpc/multi-session-host.js +18 -8
  112. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  113. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  114. package/dist/modes/rpc/session-binding.js +3 -0
  115. package/dist/modes/rpc/session-binding.js.map +1 -1
  116. package/dist/modes/rpc/session-command-router.d.ts +1 -1
  117. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  118. package/dist/modes/rpc/session-command-router.js +57 -25
  119. package/dist/modes/rpc/session-command-router.js.map +1 -1
  120. package/dist/modes/rpc/session-event-writer.d.ts +23 -1
  121. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  122. package/dist/modes/rpc/session-event-writer.js +108 -1
  123. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  124. package/dist/modes/rpc/session-registry.d.ts +4 -2
  125. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  126. package/dist/modes/rpc/session-registry.js +1 -1
  127. package/dist/modes/rpc/session-registry.js.map +1 -1
  128. package/dist/modes/rpc/session-worker-client.d.ts +47 -0
  129. package/dist/modes/rpc/session-worker-client.d.ts.map +1 -0
  130. package/dist/modes/rpc/session-worker-client.js +226 -0
  131. package/dist/modes/rpc/session-worker-client.js.map +1 -0
  132. package/dist/modes/rpc/session-worker-protocol.d.ts +103 -0
  133. package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -0
  134. package/dist/modes/rpc/session-worker-protocol.js +12 -0
  135. package/dist/modes/rpc/session-worker-protocol.js.map +1 -0
  136. package/dist/modes/rpc/session-worker-requests.d.ts +24 -0
  137. package/dist/modes/rpc/session-worker-requests.d.ts.map +1 -0
  138. package/dist/modes/rpc/session-worker-requests.js +71 -0
  139. package/dist/modes/rpc/session-worker-requests.js.map +1 -0
  140. package/dist/modes/rpc/session-worker.d.ts +2 -0
  141. package/dist/modes/rpc/session-worker.d.ts.map +1 -0
  142. package/dist/modes/rpc/session-worker.js +229 -0
  143. package/dist/modes/rpc/session-worker.js.map +1 -0
  144. package/dist/modes/rpc/worker-session-registry.d.ts +37 -0
  145. package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -0
  146. package/dist/modes/rpc/worker-session-registry.js +207 -0
  147. package/dist/modes/rpc/worker-session-registry.js.map +1 -0
  148. package/docs/rpc.md +70 -9
  149. package/docs/settings.md +1 -0
  150. package/docs/skills.md +1 -1
  151. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  152. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  153. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  154. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +23 -5
  155. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  156. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-edit.d.ts +4 -0
  157. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-edit.d.ts.map +1 -0
  158. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-edit.js +11 -0
  159. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-edit.js.map +1 -0
  160. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-params.d.ts +25 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-params.d.ts.map +1 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-params.js +58 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images-params.js.map +1 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images.d.ts +3 -6
  165. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images.js +15 -33
  167. package/node_modules/@earendil-works/pi-ai/dist/api/openai-images.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +61 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +61 -1
  171. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/images.d.ts +1 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/images.d.ts.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/images.js +1 -0
  175. package/node_modules/@earendil-works/pi-ai/dist/images.js.map +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  182. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  183. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  184. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  185. package/package.json +7 -7
@@ -12,7 +12,7 @@ How to write image prompts that come back right the first time, and how to fix t
12
12
  When image generation tooling is present in your tool set, pick the surface that actually exists right now:
13
13
 
14
14
  - If a native `image_generation` server tool is available, use it. The provider runs generation server-side and returns the image in the response stream.
15
- - Otherwise, call the `generate_image` tool. It sends your prompt to the configured OpenAI-compatible endpoint and saves the result to a file.
15
+ - Otherwise, call the `generate_image` tool. It sends your prompt and optional reference images to the configured OpenAI-compatible endpoint and saves the result as PNG files. Its default model is `gpt-image-2.5-sunburst`; choose `gpt-image-2.5-flare` for speed or `gpt-image-2` for the previous generation. These model and parameter controls apply to `generate_image`, not the native server tool.
16
16
 
17
17
  Check your current tool set before choosing. Skill visibility refreshes on reload, but tool state can change mid-session (model switch, credential change), so trust the tools you can see over what this page said at startup. If both surfaces ever appear, prefer the native server tool.
18
18
 
@@ -53,11 +53,54 @@ Both surfaces can return a `revised_prompt`: the prompt the model actually used
53
53
  2. Fold the delta into your next prompt explicitly. If the rewrite dropped "overcast sky", put "overcast sky, no direct sunlight" back with more weight. If it added something you dislike, name the exclusion ("no lens flare").
54
54
  3. Regenerate. Treat each round as a conversation with the rewriter, not a fresh roll of the dice.
55
55
 
56
- ## Editing and masks
56
+ ## Model selection
57
57
 
58
- v1 has no editing surface. The `generate_image` tool is text-only: it accepts a prompt and returns new images. There is no image input, no mask, no inpainting, no variation mode. The native `image_generation` server tool may perform provider-side edits on its own (action=auto), but no edit controls are exposed here.
58
+ - `gpt-image-2.5-sunburst` (default): most capable; choose it for precise edits, difficult compositions, and fidelity to references.
59
+ - `gpt-image-2.5-flare`: fastest; choose it for everyday generation and quick iterations.
60
+ - `gpt-image-2`: legacy, previous-generation option.
59
61
 
60
- Don't build workflows around editing. If the user asks to change an existing image, say that v1 generates from text only, then offer the closest text-only path: describe the desired end state as a full new prompt. Editing, masks, and image input are planned for a future version.
62
+ ## Quality tiers
63
+
64
+ `quality` accepts `low`, `medium`, `high`, `xhigh`, `max`, or `auto` (default). Start with `auto` or a lower tier while exploring, then increase quality for the final image. `xhigh` and `max` are available only on GPT Image 2.5 and incur higher latency and cost. Do not request them with `gpt-image-2`; the API rejects unsupported model/tier combinations.
65
+
66
+ ## Image sizes
67
+
68
+ `size` defaults to `auto`. Choose a preset or an arbitrary `WIDTHxHEIGHT` for GPT Image 2 / 2.5. Custom dimensions must satisfy all of these constraints:
69
+
70
+ - Both width and height are divisible by 16.
71
+ - Aspect ratio is between 1:3 and 3:1, inclusive.
72
+ - Neither edge exceeds 3840 pixels.
73
+ - Total pixels are between 655,360 and 8,294,400, inclusive (the upper limit is 3840x2160 or 2160x3840, not 3840x3840).
74
+
75
+ | Size | Typical use |
76
+ | --- | --- |
77
+ | `auto` | Let the model choose |
78
+ | `1024x1024` | Square preset |
79
+ | `1536x1024` | Landscape preset |
80
+ | `1024x1536` | Portrait preset |
81
+ | `2048x2048` | Larger square |
82
+ | `2048x1152` | 16:9 landscape |
83
+ | `3840x2160` | Maximum-pixel 16:9 landscape |
84
+ | `2160x3840` | Maximum-pixel 9:16 portrait |
85
+
86
+ ## Editing with reference images
87
+
88
+ Pass `reference_image_paths` to edit an existing image or use images as visual references. Supply 1-5 local PNG, JPEG, or WEBP files, each at most 50 MB. Paths may be absolute or relative to the current working directory. With references the tool sends an image-edit request; without them it generates from text.
89
+
90
+ Describe the desired END STATE in the prompt, not just a terse change instruction. State what must remain unchanged and what the finished image should contain. When passing multiple references, identify their roles in the same order as the paths.
91
+
92
+ ```json
93
+ {
94
+ "prompt": "The same red fox from the reference, now wearing a knitted blue scarf, in an eye-level wildlife photograph. Preserve its face, fur markings, pose, and forest background. Soft overcast light, muted woodland colors, shallow depth of field; only the scarf is new.",
95
+ "model": "gpt-image-2.5-sunburst",
96
+ "reference_image_paths": ["art/fox.png"],
97
+ "quality": "high",
98
+ "size": "2048x1152",
99
+ "output_path": "art/fox-with-scarf.png"
100
+ }
101
+ ```
102
+
103
+ Read any returned `revised_prompt` after an edit, just as after generation. The client tool includes these in its result text and `details.revisedPrompts`. Compare the rewrite and saved image against the desired end state before another edit, and use a new `output_path` to preserve the source image.
61
104
 
62
105
  ## Iteration workflow
63
106
 
@@ -1,22 +1,13 @@
1
- import { Type } from "typebox";
2
- export interface GenerateImageDetails {
3
- paths: string[];
4
- model: string;
5
- source: string;
6
- size: string;
7
- quality: string;
8
- requested: number;
9
- generated: number;
10
- revisedPrompts: string[];
11
- error?: string;
12
- reason?: "missing_config" | "provider_native_bypass" | "invalid_params" | "write_failed" | "provider_error";
13
- }
1
+ import { type GenerateImageDetails } from "./params.ts";
2
+ export type { GenerateImageDetails } from "./params.ts";
14
3
  export declare const GENERATE_IMAGE_TOOL_NAME = "generate_image";
15
- export declare const generateImageTool: import("../../types.ts").ToolDefinition<Type.TObject<{
16
- prompt: Type.TString;
17
- size: Type.TOptional<Type.TUnion<[Type.TLiteral<"auto">, Type.TLiteral<"1024x1024">, Type.TLiteral<"1024x1536">, Type.TLiteral<"1536x1024">]>>;
18
- quality: Type.TOptional<Type.TUnion<[Type.TLiteral<"auto">, Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">]>>;
19
- n: Type.TOptional<Type.TInteger>;
20
- output_path: Type.TOptional<Type.TString>;
4
+ export declare const generateImageTool: import("../../types.ts").ToolDefinition<import("typebox").TObject<{
5
+ prompt: import("typebox").TString;
6
+ model: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"gpt-image-2.5-sunburst">, import("typebox").TLiteral<"gpt-image-2.5-flare">, import("typebox").TLiteral<"gpt-image-2">]>>;
7
+ size: import("typebox").TOptional<import("typebox").TString>;
8
+ quality: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<"auto">, import("typebox").TLiteral<"low">, import("typebox").TLiteral<"medium">, import("typebox").TLiteral<"high">, import("typebox").TLiteral<"xhigh">, import("typebox").TLiteral<"max">]>>;
9
+ reference_image_paths: import("typebox").TOptional<import("typebox").TArray<import("typebox").TString>>;
10
+ n: import("typebox").TOptional<import("typebox").TInteger>;
11
+ output_path: import("typebox").TOptional<import("typebox").TString>;
21
12
  }>, GenerateImageDetails, any> & import("../../types.ts").ToolDefinition<any, any, any>;
22
13
  //# sourceMappingURL=tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/tool.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAyC/B,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,gBAAgB,GAAG,wBAAwB,GAAG,gBAAgB,GAAG,cAAc,GAAG,gBAAgB,CAAC;CAC5G;AAiFD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,eAAO,MAAM,iBAAiB;;;;;;uFA4F5B,CAAC"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/tool.ts"],"names":[],"mappings":"AAOA,OAAO,EAAgC,KAAK,oBAAoB,EAA6B,MAAM,aAAa,CAAC;AAKjH,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AA+DxD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,eAAO,MAAM,iBAAiB;;;;;;;;uFAqG5B,CAAC"}
@@ -1,46 +1,13 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { mkdir, rm, writeFile } from "node:fs/promises";
3
3
  import { dirname } from "node:path";
4
- import { generateImages } from "@earendil-works/pi-ai/compat";
5
- import { Type } from "typebox";
4
+ import { generateImages, parseOpenAIImageSize } from "@earendil-works/pi-ai/compat";
6
5
  import { defineTool } from "../../types.js";
7
6
  import { resolveImageGenAuth } from "./auth.js";
7
+ import { DEFAULT_IMAGE_MODEL, failure, IMAGE_MODEL_NAMES, Params } from "./params.js";
8
8
  import { displayPath, resolveTargets } from "./paths.js";
9
+ import { loadReferenceImages } from "./reference-images.js";
9
10
  import { imageGenRegistryOverride, isNativeBypass, NATIVE_BYPASS_MESSAGE } from "./state.js";
10
- const MODEL_ID = "gpt-image-2";
11
- const Params = Type.Object({
12
- prompt: Type.String({
13
- minLength: 1,
14
- maxLength: 32_000,
15
- description: "Detailed description of the image to generate.",
16
- }),
17
- size: Type.Optional(Type.Union([Type.Literal("auto"), Type.Literal("1024x1024"), Type.Literal("1024x1536"), Type.Literal("1536x1024")], { default: "auto", description: "Output resolution. Defaults to auto." })),
18
- quality: Type.Optional(Type.Union([Type.Literal("auto"), Type.Literal("low"), Type.Literal("medium"), Type.Literal("high")], {
19
- default: "auto",
20
- description: "Rendering quality. Defaults to auto.",
21
- })),
22
- n: Type.Optional(Type.Integer({ minimum: 1, maximum: 10, default: 1, description: "How many images to generate." })),
23
- output_path: Type.Optional(Type.String({
24
- minLength: 1,
25
- description: "Where to write the image, relative to the working directory. Must end in .png. Defaults to generated-images/.",
26
- })),
27
- }, { additionalProperties: false });
28
- /**
29
- * Only the registry surface the credential resolver needs. The session passes its
30
- * full ModelRegistry, which satisfies this structurally.
31
- */
32
- function failure(message, reason, base) {
33
- const details = {
34
- ...base,
35
- paths: [],
36
- model: MODEL_ID,
37
- generated: 0,
38
- revisedPrompts: [],
39
- error: message,
40
- reason,
41
- };
42
- return { content: [{ type: "text", text: message }], details };
43
- }
44
11
  function sourceLabel(auth) {
45
12
  if (auth.kind === "none")
46
13
  return "none";
@@ -48,14 +15,14 @@ function sourceLabel(auth) {
48
15
  return "env:OPENAI_API_KEY";
49
16
  return `${auth.provenance}:${auth.providerId ?? auth.kind}`;
50
17
  }
51
- function synthesizeModel(auth) {
18
+ function synthesizeModel(auth, id) {
52
19
  const model = {
53
- id: MODEL_ID,
54
- name: "GPT Image 2",
20
+ id,
21
+ name: IMAGE_MODEL_NAMES[id],
55
22
  api: "openai-images",
56
23
  provider: auth.providerId ?? "openai",
57
24
  baseUrl: auth.baseUrl,
58
- input: ["text"],
25
+ input: ["text", "image"],
59
26
  output: ["image"],
60
27
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
61
28
  };
@@ -99,14 +66,15 @@ export const GENERATE_IMAGE_TOOL_NAME = "generate_image";
99
66
  export const generateImageTool = defineTool({
100
67
  name: GENERATE_IMAGE_TOOL_NAME,
101
68
  label: "Generate Image",
102
- description: "Generate an image from a text prompt with gpt-image-2 and save it as a PNG file. Returns the saved file paths.",
103
- promptSnippet: "Generate images from text prompts and save them as PNG files.",
69
+ description: "Generate or edit an image with OpenAI gpt-image-2.5 (Sunburst by default; Flare for speed) and save it as a PNG file. Pass reference_image_paths to edit or reference existing images. Returns the saved file paths.",
70
+ promptSnippet: "Generate or edit images from prompts and optional reference images, saving them as PNG files.",
104
71
  parameters: Params,
105
72
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
106
73
  const size = params.size ?? "auto";
107
74
  const quality = params.quality ?? "auto";
108
75
  const requested = params.n ?? 1;
109
- const context = { size, quality, requested, source: "none" };
76
+ const modelId = params.model ?? DEFAULT_IMAGE_MODEL;
77
+ const context = { model: modelId, size, quality, requested, source: "none" };
110
78
  const prompt = params.prompt.trim();
111
79
  if (prompt.length === 0) {
112
80
  return failure("Error: prompt must contain non-whitespace text.", "invalid_params", context);
@@ -119,6 +87,14 @@ export const generateImageTool = defineTool({
119
87
  return failure(auth.reason, "missing_config", context);
120
88
  }
121
89
  const source = sourceLabel(auth);
90
+ const parsedSize = parseOpenAIImageSize(size);
91
+ if (!parsedSize.ok) {
92
+ return failure(`Error: ${parsedSize.error}`, "invalid_params", { ...context, source });
93
+ }
94
+ const references = await loadReferenceImages(ctx.cwd, params.reference_image_paths);
95
+ if (!references.ok) {
96
+ return failure(references.error, "invalid_params", { ...context, source });
97
+ }
122
98
  const targets = resolveTargets(ctx.cwd, toolCallId, requested, params.output_path);
123
99
  if (!targets.ok) {
124
100
  return failure(targets.error, "invalid_params", { ...context, source });
@@ -128,7 +104,7 @@ export const generateImageTool = defineTool({
128
104
  return failure(`Error: ${displayPath(ctx.cwd, target)} already exists. Choose another output_path.`, "invalid_params", { ...context, source });
129
105
  }
130
106
  }
131
- const images = await generateImages(synthesizeModel(auth), { input: [{ type: "text", text: prompt }] }, {
107
+ const images = await generateImages(synthesizeModel(auth, modelId), { input: [{ type: "text", text: prompt }, ...references.images] }, {
132
108
  ...(auth.apiKey === undefined ? {} : { apiKey: auth.apiKey }),
133
109
  ...(auth.headers === undefined ? {} : { headers: auth.headers }),
134
110
  ...(signal === undefined ? {} : { signal }),
@@ -152,7 +128,7 @@ export const generateImageTool = defineTool({
152
128
  const revisedPrompts = generated.flatMap((image) => (image.revisedPrompt ? [image.revisedPrompt] : []));
153
129
  const details = {
154
130
  paths: savedPaths,
155
- model: MODEL_ID,
131
+ model: modelId,
156
132
  source,
157
133
  size,
158
134
  quality,
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, rm, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { AssistantImages, ImagesModel } from \"@earendil-works/pi-ai/compat\";\nimport { generateImages } from \"@earendil-works/pi-ai/compat\";\nimport { Type } from \"typebox\";\nimport { defineTool, type ExtensionContext } from \"../../types.ts\";\nimport { type ImageGenAuthResolution, resolveImageGenAuth } from \"./auth.ts\";\nimport { displayPath, resolveTargets } from \"./paths.ts\";\nimport { imageGenRegistryOverride, isNativeBypass, NATIVE_BYPASS_MESSAGE } from \"./state.ts\";\n\nconst MODEL_ID = \"gpt-image-2\";\n\nconst Params = Type.Object(\n\t{\n\t\tprompt: Type.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: 32_000,\n\t\t\tdescription: \"Detailed description of the image to generate.\",\n\t\t}),\n\t\tsize: Type.Optional(\n\t\t\tType.Union(\n\t\t\t\t[Type.Literal(\"auto\"), Type.Literal(\"1024x1024\"), Type.Literal(\"1024x1536\"), Type.Literal(\"1536x1024\")],\n\t\t\t\t{ default: \"auto\", description: \"Output resolution. Defaults to auto.\" },\n\t\t\t),\n\t\t),\n\t\tquality: Type.Optional(\n\t\t\tType.Union([Type.Literal(\"auto\"), Type.Literal(\"low\"), Type.Literal(\"medium\"), Type.Literal(\"high\")], {\n\t\t\t\tdefault: \"auto\",\n\t\t\t\tdescription: \"Rendering quality. Defaults to auto.\",\n\t\t\t}),\n\t\t),\n\t\tn: Type.Optional(\n\t\t\tType.Integer({ minimum: 1, maximum: 10, default: 1, description: \"How many images to generate.\" }),\n\t\t),\n\t\toutput_path: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tdescription:\n\t\t\t\t\t\"Where to write the image, relative to the working directory. Must end in .png. Defaults to generated-images/.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface GenerateImageDetails {\n\tpaths: string[];\n\tmodel: string;\n\tsource: string;\n\tsize: string;\n\tquality: string;\n\trequested: number;\n\tgenerated: number;\n\trevisedPrompts: string[];\n\terror?: string;\n\treason?: \"missing_config\" | \"provider_native_bypass\" | \"invalid_params\" | \"write_failed\" | \"provider_error\";\n}\n\n/**\n * Only the registry surface the credential resolver needs. The session passes its\n * full ModelRegistry, which satisfies this structurally.\n */\nfunction failure(\n\tmessage: string,\n\treason: NonNullable<GenerateImageDetails[\"reason\"]>,\n\tbase: Pick<GenerateImageDetails, \"size\" | \"quality\" | \"requested\" | \"source\">,\n) {\n\tconst details: GenerateImageDetails = {\n\t\t...base,\n\t\tpaths: [],\n\t\tmodel: MODEL_ID,\n\t\tgenerated: 0,\n\t\trevisedPrompts: [],\n\t\terror: message,\n\t\treason,\n\t};\n\treturn { content: [{ type: \"text\" as const, text: message }], details };\n}\n\nfunction sourceLabel(auth: ImageGenAuthResolution): string {\n\tif (auth.kind === \"none\") return \"none\";\n\tif (auth.provenance === \"env\") return \"env:OPENAI_API_KEY\";\n\treturn `${auth.provenance}:${auth.providerId ?? auth.kind}`;\n}\n\nfunction synthesizeModel(auth: Extract<ImageGenAuthResolution, { kind: \"native-openai\" | \"gateway\" }>) {\n\tconst model: ImagesModel<\"openai-images\"> = {\n\t\tid: MODEL_ID,\n\t\tname: \"GPT Image 2\",\n\t\tapi: \"openai-images\",\n\t\tprovider: auth.providerId ?? \"openai\",\n\t\tbaseUrl: auth.baseUrl,\n\t\tinput: [\"text\"],\n\t\toutput: [\"image\"],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t};\n\treturn model;\n}\n\ninterface GeneratedImage {\n\tdata: string;\n\trevisedPrompt?: string;\n}\n\nfunction collectImages(images: AssistantImages): GeneratedImage[] {\n\tconst collected: GeneratedImage[] = [];\n\tlet pendingText: string | undefined;\n\tfor (const block of images.output) {\n\t\tif (block.type === \"text\") {\n\t\t\tconst text = block.text.trim();\n\t\t\tpendingText = text.length > 0 ? text : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tcollected.push({ data: block.data, ...(pendingText === undefined ? {} : { revisedPrompt: pendingText }) });\n\t\tpendingText = undefined;\n\t}\n\treturn collected;\n}\n\nasync function writeImages(paths: string[], images: GeneratedImage[]): Promise<string | undefined> {\n\tconst written: string[] = [];\n\tfor (const [index, image] of images.entries()) {\n\t\tconst target = paths[index];\n\t\tif (target === undefined) break;\n\t\ttry {\n\t\t\tawait mkdir(dirname(target), { recursive: true });\n\t\t\tawait writeFile(target, Buffer.from(image.data, \"base64\"), { flag: \"wx\" });\n\t\t\twritten.push(target);\n\t\t} catch (error) {\n\t\t\tfor (const path of written) await rm(path, { force: true }).catch(() => undefined);\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\treturn `Error: failed to write generated image to ${target}: ${reason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport const GENERATE_IMAGE_TOOL_NAME = \"generate_image\";\n\nexport const generateImageTool = defineTool<typeof Params, GenerateImageDetails>({\n\tname: GENERATE_IMAGE_TOOL_NAME,\n\tlabel: \"Generate Image\",\n\tdescription:\n\t\t\"Generate an image from a text prompt with gpt-image-2 and save it as a PNG file. Returns the saved file paths.\",\n\tpromptSnippet: \"Generate images from text prompts and save them as PNG files.\",\n\tparameters: Params,\n\tasync execute(toolCallId, params, signal, _onUpdate, ctx: ExtensionContext) {\n\t\tconst size = params.size ?? \"auto\";\n\t\tconst quality = params.quality ?? \"auto\";\n\t\tconst requested = params.n ?? 1;\n\t\tconst context = { size, quality, requested, source: \"none\" };\n\n\t\tconst prompt = params.prompt.trim();\n\t\tif (prompt.length === 0) {\n\t\t\treturn failure(\"Error: prompt must contain non-whitespace text.\", \"invalid_params\", context);\n\t\t}\n\t\tif (isNativeBypass()) {\n\t\t\treturn failure(NATIVE_BYPASS_MESSAGE, \"provider_native_bypass\", context);\n\t\t}\n\n\t\tconst auth = await resolveImageGenAuth({ modelRegistry: imageGenRegistryOverride() ?? ctx.modelRegistry });\n\t\tif (auth.kind === \"none\") {\n\t\t\treturn failure(auth.reason, \"missing_config\", context);\n\t\t}\n\t\tconst source = sourceLabel(auth);\n\t\tconst targets = resolveTargets(ctx.cwd, toolCallId, requested, params.output_path);\n\t\tif (!targets.ok) {\n\t\t\treturn failure(targets.error, \"invalid_params\", { ...context, source });\n\t\t}\n\t\tfor (const target of targets.paths) {\n\t\t\tif (existsSync(target)) {\n\t\t\t\treturn failure(\n\t\t\t\t\t`Error: ${displayPath(ctx.cwd, target)} already exists. Choose another output_path.`,\n\t\t\t\t\t\"invalid_params\",\n\t\t\t\t\t{ ...context, source },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst images = await generateImages(\n\t\t\tsynthesizeModel(auth),\n\t\t\t{ input: [{ type: \"text\", text: prompt }] },\n\t\t\t{\n\t\t\t\t...(auth.apiKey === undefined ? {} : { apiKey: auth.apiKey }),\n\t\t\t\t...(auth.headers === undefined ? {} : { headers: auth.headers }),\n\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\tsize,\n\t\t\t\tquality,\n\t\t\t\tn: requested,\n\t\t\t},\n\t\t);\n\t\tif (images.stopReason !== \"stop\") {\n\t\t\tconst message = images.errorMessage ?? `Image generation ${images.stopReason}.`;\n\t\t\treturn failure(`Error: ${message}`, \"provider_error\", { ...context, source });\n\t\t}\n\n\t\tconst generated = collectImages(images);\n\t\tif (generated.length === 0) {\n\t\t\treturn failure(\"Error: the provider returned no images.\", \"provider_error\", { ...context, source });\n\t\t}\n\t\tconst writeError = await writeImages(targets.paths, generated);\n\t\tif (writeError !== undefined) {\n\t\t\treturn failure(writeError, \"write_failed\", { ...context, source });\n\t\t}\n\n\t\tconst savedPaths = targets.paths.slice(0, generated.length).map((target) => displayPath(ctx.cwd, target));\n\t\tconst revisedPrompts = generated.flatMap((image) => (image.revisedPrompt ? [image.revisedPrompt] : []));\n\t\tconst details: GenerateImageDetails = {\n\t\t\tpaths: savedPaths,\n\t\t\tmodel: MODEL_ID,\n\t\t\tsource,\n\t\t\tsize,\n\t\t\tquality,\n\t\t\trequested,\n\t\t\tgenerated: generated.length,\n\t\t\trevisedPrompts,\n\t\t};\n\t\tconst summary = [\n\t\t\t`Generated ${generated.length} image${generated.length === 1 ? \"\" : \"s\"}:`,\n\t\t\t...savedPaths.map((path) => `- ${path}`),\n\t\t\t...revisedPrompts.map((revised) => `Revised prompt: ${revised}`),\n\t\t].join(\"\\n\");\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{ type: \"text\" as const, text: summary },\n\t\t\t\t...generated.map((image) => ({ type: \"image\" as const, data: image.data, mimeType: \"image/png\" })),\n\t\t\t],\n\t\t\tdetails,\n\t\t\t...(images.usage === undefined ? {} : { usage: images.usage }),\n\t\t};\n\t},\n});\n"]}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/imagegen/tool.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, rm, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { AssistantImages, ImagesModel } from \"@earendil-works/pi-ai/compat\";\nimport { generateImages, parseOpenAIImageSize } from \"@earendil-works/pi-ai/compat\";\nimport { defineTool, type ExtensionContext } from \"../../types.ts\";\nimport { type ImageGenAuthResolution, resolveImageGenAuth } from \"./auth.ts\";\nimport { DEFAULT_IMAGE_MODEL, failure, type GenerateImageDetails, IMAGE_MODEL_NAMES, Params } from \"./params.ts\";\nimport { displayPath, resolveTargets } from \"./paths.ts\";\nimport { loadReferenceImages } from \"./reference-images.ts\";\nimport { imageGenRegistryOverride, isNativeBypass, NATIVE_BYPASS_MESSAGE } from \"./state.ts\";\n\nexport type { GenerateImageDetails } from \"./params.ts\";\n\nfunction sourceLabel(auth: ImageGenAuthResolution): string {\n\tif (auth.kind === \"none\") return \"none\";\n\tif (auth.provenance === \"env\") return \"env:OPENAI_API_KEY\";\n\treturn `${auth.provenance}:${auth.providerId ?? auth.kind}`;\n}\n\nfunction synthesizeModel(\n\tauth: Extract<ImageGenAuthResolution, { kind: \"native-openai\" | \"gateway\" }>,\n\tid: keyof typeof IMAGE_MODEL_NAMES,\n) {\n\tconst model: ImagesModel<\"openai-images\"> = {\n\t\tid,\n\t\tname: IMAGE_MODEL_NAMES[id],\n\t\tapi: \"openai-images\",\n\t\tprovider: auth.providerId ?? \"openai\",\n\t\tbaseUrl: auth.baseUrl,\n\t\tinput: [\"text\", \"image\"],\n\t\toutput: [\"image\"],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t};\n\treturn model;\n}\n\ninterface GeneratedImage {\n\tdata: string;\n\trevisedPrompt?: string;\n}\n\nfunction collectImages(images: AssistantImages): GeneratedImage[] {\n\tconst collected: GeneratedImage[] = [];\n\tlet pendingText: string | undefined;\n\tfor (const block of images.output) {\n\t\tif (block.type === \"text\") {\n\t\t\tconst text = block.text.trim();\n\t\t\tpendingText = text.length > 0 ? text : undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tcollected.push({ data: block.data, ...(pendingText === undefined ? {} : { revisedPrompt: pendingText }) });\n\t\tpendingText = undefined;\n\t}\n\treturn collected;\n}\n\nasync function writeImages(paths: string[], images: GeneratedImage[]): Promise<string | undefined> {\n\tconst written: string[] = [];\n\tfor (const [index, image] of images.entries()) {\n\t\tconst target = paths[index];\n\t\tif (target === undefined) break;\n\t\ttry {\n\t\t\tawait mkdir(dirname(target), { recursive: true });\n\t\t\tawait writeFile(target, Buffer.from(image.data, \"base64\"), { flag: \"wx\" });\n\t\t\twritten.push(target);\n\t\t} catch (error) {\n\t\t\tfor (const path of written) await rm(path, { force: true }).catch(() => undefined);\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\treturn `Error: failed to write generated image to ${target}: ${reason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport const GENERATE_IMAGE_TOOL_NAME = \"generate_image\";\n\nexport const generateImageTool = defineTool<typeof Params, GenerateImageDetails>({\n\tname: GENERATE_IMAGE_TOOL_NAME,\n\tlabel: \"Generate Image\",\n\tdescription:\n\t\t\"Generate or edit an image with OpenAI gpt-image-2.5 (Sunburst by default; Flare for speed) and save it as a PNG file. Pass reference_image_paths to edit or reference existing images. Returns the saved file paths.\",\n\tpromptSnippet: \"Generate or edit images from prompts and optional reference images, saving them as PNG files.\",\n\tparameters: Params,\n\tasync execute(toolCallId, params, signal, _onUpdate, ctx: ExtensionContext) {\n\t\tconst size = params.size ?? \"auto\";\n\t\tconst quality = params.quality ?? \"auto\";\n\t\tconst requested = params.n ?? 1;\n\t\tconst modelId = params.model ?? DEFAULT_IMAGE_MODEL;\n\t\tconst context = { model: modelId, size, quality, requested, source: \"none\" };\n\n\t\tconst prompt = params.prompt.trim();\n\t\tif (prompt.length === 0) {\n\t\t\treturn failure(\"Error: prompt must contain non-whitespace text.\", \"invalid_params\", context);\n\t\t}\n\t\tif (isNativeBypass()) {\n\t\t\treturn failure(NATIVE_BYPASS_MESSAGE, \"provider_native_bypass\", context);\n\t\t}\n\n\t\tconst auth = await resolveImageGenAuth({ modelRegistry: imageGenRegistryOverride() ?? ctx.modelRegistry });\n\t\tif (auth.kind === \"none\") {\n\t\t\treturn failure(auth.reason, \"missing_config\", context);\n\t\t}\n\t\tconst source = sourceLabel(auth);\n\t\tconst parsedSize = parseOpenAIImageSize(size);\n\t\tif (!parsedSize.ok) {\n\t\t\treturn failure(`Error: ${parsedSize.error}`, \"invalid_params\", { ...context, source });\n\t\t}\n\t\tconst references = await loadReferenceImages(ctx.cwd, params.reference_image_paths);\n\t\tif (!references.ok) {\n\t\t\treturn failure(references.error, \"invalid_params\", { ...context, source });\n\t\t}\n\t\tconst targets = resolveTargets(ctx.cwd, toolCallId, requested, params.output_path);\n\t\tif (!targets.ok) {\n\t\t\treturn failure(targets.error, \"invalid_params\", { ...context, source });\n\t\t}\n\t\tfor (const target of targets.paths) {\n\t\t\tif (existsSync(target)) {\n\t\t\t\treturn failure(\n\t\t\t\t\t`Error: ${displayPath(ctx.cwd, target)} already exists. Choose another output_path.`,\n\t\t\t\t\t\"invalid_params\",\n\t\t\t\t\t{ ...context, source },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst images = await generateImages(\n\t\t\tsynthesizeModel(auth, modelId),\n\t\t\t{ input: [{ type: \"text\", text: prompt }, ...references.images] },\n\t\t\t{\n\t\t\t\t...(auth.apiKey === undefined ? {} : { apiKey: auth.apiKey }),\n\t\t\t\t...(auth.headers === undefined ? {} : { headers: auth.headers }),\n\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\tsize,\n\t\t\t\tquality,\n\t\t\t\tn: requested,\n\t\t\t},\n\t\t);\n\t\tif (images.stopReason !== \"stop\") {\n\t\t\tconst message = images.errorMessage ?? `Image generation ${images.stopReason}.`;\n\t\t\treturn failure(`Error: ${message}`, \"provider_error\", { ...context, source });\n\t\t}\n\n\t\tconst generated = collectImages(images);\n\t\tif (generated.length === 0) {\n\t\t\treturn failure(\"Error: the provider returned no images.\", \"provider_error\", { ...context, source });\n\t\t}\n\t\tconst writeError = await writeImages(targets.paths, generated);\n\t\tif (writeError !== undefined) {\n\t\t\treturn failure(writeError, \"write_failed\", { ...context, source });\n\t\t}\n\n\t\tconst savedPaths = targets.paths.slice(0, generated.length).map((target) => displayPath(ctx.cwd, target));\n\t\tconst revisedPrompts = generated.flatMap((image) => (image.revisedPrompt ? [image.revisedPrompt] : []));\n\t\tconst details: GenerateImageDetails = {\n\t\t\tpaths: savedPaths,\n\t\t\tmodel: modelId,\n\t\t\tsource,\n\t\t\tsize,\n\t\t\tquality,\n\t\t\trequested,\n\t\t\tgenerated: generated.length,\n\t\t\trevisedPrompts,\n\t\t};\n\t\tconst summary = [\n\t\t\t`Generated ${generated.length} image${generated.length === 1 ? \"\" : \"s\"}:`,\n\t\t\t...savedPaths.map((path) => `- ${path}`),\n\t\t\t...revisedPrompts.map((revised) => `Revised prompt: ${revised}`),\n\t\t].join(\"\\n\");\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{ type: \"text\" as const, text: summary },\n\t\t\t\t...generated.map((image) => ({ type: \"image\" as const, data: image.data, mimeType: \"image/png\" })),\n\t\t\t],\n\t\t\tdetails,\n\t\t\t...(images.usage === undefined ? {} : { usage: images.usage }),\n\t\t};\n\t},\n});\n"]}
@@ -23,9 +23,10 @@ export declare function drainPendingProviderRegistrations(runtime: ExtensionRunt
23
23
  /**
24
24
  * Create an Extension from an inline factory function.
25
25
  */
26
- export declare function loadExtensionFromFactory(factory: ExtensionFactory, cwd: string, eventBus: EventBus, runtime: ExtensionRuntime, extensionPath?: string): Promise<Extension>;
26
+ export declare function loadExtensionFromFactory(factory: ExtensionFactory, cwd: string, eventBus: EventBus, runtime: ExtensionRuntime, extensionPath?: string, sharedHostEnabled?: boolean): Promise<Extension>;
27
27
  export declare function loadExtensions(paths: string[], cwd: string, eventBus?: EventBus, runtime?: ExtensionRuntime, options?: {
28
28
  factoryResolver?: ExtensionFactoryResolver;
29
+ sharedHostEnabled?: boolean;
29
30
  }): Promise<LoadExtensionsResult>;
30
31
  export declare function loadExtensionsCached(paths: string[], cwd: string, eventBus?: EventBus, runtime?: ExtensionRuntime): Promise<LoadExtensionsResult>;
31
32
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AACA;;;GAGG;AAyBH,OAAO,EAAkB,KAAK,QAAQ,EAAuD,MAAM,iBAAiB,CAAC;AAOrH,OAAO,KAAK,EAEX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAGhB,oBAAoB,EAGpB,2BAA2B,EAK3B,MAAM,YAAY,CAAC;AAmJpB,MAAM,MAAM,wBAAwB,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;AAiBrH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAyBD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAsFzD;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,gBAAgB,GAAG,2BAA2B,EAAE,CAS1G;AA4bD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,GACxB,OAAO,CAAC,SAAS,CAAC,CAGpB;AAsDD,wBAAsB,cAAc,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,EAC1B,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,wBAAwB,CAAA;CAAE,GACtD,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA2FD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA2C/B"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AACA;;;GAGG;AAyBH,OAAO,EAAkB,KAAK,QAAQ,EAAuD,MAAM,iBAAiB,CAAC;AAQrH,OAAO,KAAK,EAEX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAGhB,oBAAoB,EAGpB,2BAA2B,EAK3B,MAAM,YAAY,CAAC;AAmJpB,MAAM,MAAM,wBAAwB,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;AAiBrH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAyBD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAsFzD;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,gBAAgB,GAAG,2BAA2B,EAAE,CAS1G;AA+cD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,EAC1B,iBAAiB,UAAQ,GACvB,OAAO,CAAC,SAAS,CAAC,CAGpB;AAuDD,wBAAsB,cAAc,CACnC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,EAC1B,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GACnF,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,MAAM,EAAE,EACf,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AA2FD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CA2C/B"}
@@ -29,6 +29,7 @@ import { execCommand } from "../exec.js";
29
29
  import { readPiManifest } from "../pi-manifest.js";
30
30
  import { createSyntheticSourceInfo } from "../source-info.js";
31
31
  import { time } from "../timings.js";
32
+ import { registerReadClassifier } from "../tools/read-classifiers.js";
32
33
  import { validateMcpServerDeclaration } from "./builtin/mcp/config-schema.js";
33
34
  /** Modules available to extensions via virtualModules (for compiled binaries) */
34
35
  const VIRTUAL_MODULES = {
@@ -267,7 +268,7 @@ export function drainPendingProviderRegistrations(runtime) {
267
268
  * Registration methods write to the extension object.
268
269
  * Action methods delegate to the shared runtime.
269
270
  */
270
- function createExtensionAPI(extension, runtime, cwd, eventBus) {
271
+ function createExtensionAPI(extension, runtime, cwd, eventBus, sharedHostEnabled) {
271
272
  const pendingFlagValues = new Map();
272
273
  const pendingRuntimeChanges = [];
273
274
  const loadingUnsubscribers = [];
@@ -291,6 +292,7 @@ function createExtensionAPI(extension, runtime, cwd, eventBus) {
291
292
  };
292
293
  const api = {
293
294
  cwd,
295
+ sharedHostEnabled,
294
296
  // Registration methods - write to extension
295
297
  on(event, handler) {
296
298
  assertActive();
@@ -373,6 +375,13 @@ function createExtensionAPI(extension, runtime, cwd, eventBus) {
373
375
  extension.entryRenderers ??= new Map();
374
376
  extension.entryRenderers.set(customType, renderer);
375
377
  },
378
+ registerReadClassifier(classifier) {
379
+ assertActive();
380
+ const unregister = runtime.trackEventBusSubscription(registerReadClassifier(classifier));
381
+ if (state === "loading")
382
+ loadingUnsubscribers.push(unregister);
383
+ return unregister;
384
+ },
376
385
  registerMcpServer(name, config) {
377
386
  runtime.assertActive();
378
387
  const error = validateMcpServerDeclaration(name, config);
@@ -606,9 +615,9 @@ function createExtension(extensionPath, resolvedPath, registrationCwd) {
606
615
  registrationCwd,
607
616
  };
608
617
  }
609
- async function initializeExtension(factory, extensionPath, resolvedPath, cwd, eventBus, runtime) {
618
+ async function initializeExtension(factory, extensionPath, resolvedPath, cwd, eventBus, runtime, sharedHostEnabled) {
610
619
  const extension = createExtension(extensionPath, resolvedPath, cwd);
611
- const load = createExtensionAPI(extension, runtime, cwd, eventBus);
620
+ const load = createExtensionAPI(extension, runtime, cwd, eventBus, sharedHostEnabled);
612
621
  try {
613
622
  await factory(load.api);
614
623
  load.commit();
@@ -620,7 +629,7 @@ async function initializeExtension(factory, extensionPath, resolvedPath, cwd, ev
620
629
  time(`${extensionPath} factory`, "extensions");
621
630
  return extension;
622
631
  }
623
- async function loadExtension(extensionPath, cwd, eventBus, runtime, getImporter, factoryResolver, cacheToken) {
632
+ async function loadExtension(extensionPath, cwd, eventBus, runtime, getImporter, factoryResolver, cacheToken, sharedHostEnabled = false) {
624
633
  const resolvedPath = resolvePath(extensionPath, cwd, { normalizeUnicodeSpaces: true });
625
634
  try {
626
635
  const factory = factoryResolver?.(extensionPath, resolvedPath) ??
@@ -629,7 +638,7 @@ async function loadExtension(extensionPath, cwd, eventBus, runtime, getImporter,
629
638
  if (!factory) {
630
639
  return { extension: null, error: `Extension does not export a valid factory function: ${extensionPath}` };
631
640
  }
632
- const extension = await initializeExtension(factory, extensionPath, resolvedPath, cwd, eventBus, runtime);
641
+ const extension = await initializeExtension(factory, extensionPath, resolvedPath, cwd, eventBus, runtime, sharedHostEnabled);
633
642
  return { extension, error: null };
634
643
  }
635
644
  catch (err) {
@@ -640,9 +649,9 @@ async function loadExtension(extensionPath, cwd, eventBus, runtime, getImporter,
640
649
  /**
641
650
  * Create an Extension from an inline factory function.
642
651
  */
643
- export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath = "<inline>") {
652
+ export async function loadExtensionFromFactory(factory, cwd, eventBus, runtime, extensionPath = "<inline>", sharedHostEnabled = false) {
644
653
  const resolvedCwd = resolvePath(cwd);
645
- return initializeExtension(factory, extensionPath, extensionPath, resolvedCwd, eventBus, runtime);
654
+ return initializeExtension(factory, extensionPath, extensionPath, resolvedCwd, eventBus, runtime, sharedHostEnabled);
646
655
  }
647
656
  /**
648
657
  * Load extensions from paths.
@@ -660,7 +669,7 @@ async function loadExtensionsInternal(paths, cwd, eventBus, runtime, options, us
660
669
  return importer;
661
670
  };
662
671
  for (const extPath of paths) {
663
- const { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, resolvedRuntime, getImporter, options?.factoryResolver, cacheToken);
672
+ const { extension, error } = await loadExtension(extPath, resolvedCwd, resolvedEventBus, resolvedRuntime, getImporter, options?.factoryResolver, cacheToken, options?.sharedHostEnabled ?? false);
664
673
  if (error) {
665
674
  errors.push({ path: extPath, error });
666
675
  continue;