@avocadostudio-ai/orchestrator-core 0.1.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.
Files changed (196) hide show
  1. package/LICENSE +201 -0
  2. package/dist/agent/agent-context.d.ts +19 -0
  3. package/dist/agent/agent-context.js +67 -0
  4. package/dist/agent/agent-logger.d.ts +5 -0
  5. package/dist/agent/agent-logger.js +22 -0
  6. package/dist/agent/agent-loop-openai.d.ts +8 -0
  7. package/dist/agent/agent-loop-openai.js +172 -0
  8. package/dist/agent/agent-loop.d.ts +56 -0
  9. package/dist/agent/agent-loop.js +167 -0
  10. package/dist/agent/agent-provider.d.ts +28 -0
  11. package/dist/agent/agent-provider.js +63 -0
  12. package/dist/agent/agent-tools.d.ts +28 -0
  13. package/dist/agent/agent-tools.js +899 -0
  14. package/dist/agent/context/editing-guidelines.md +46 -0
  15. package/dist/agent/context/role.md +39 -0
  16. package/dist/agent/integration-prompt.d.ts +9 -0
  17. package/dist/agent/integration-prompt.js +154 -0
  18. package/dist/agent/sites-agent-context.d.ts +12 -0
  19. package/dist/agent/sites-agent-context.js +316 -0
  20. package/dist/agent/sites-agent-shared.d.ts +161 -0
  21. package/dist/agent/sites-agent-shared.js +1101 -0
  22. package/dist/agent/sites-agent-tools.d.ts +18 -0
  23. package/dist/agent/sites-agent-tools.js +1227 -0
  24. package/dist/chat/anthropic-cache.d.ts +20 -0
  25. package/dist/chat/anthropic-cache.js +54 -0
  26. package/dist/chat/anthropic-planner.d.ts +98 -0
  27. package/dist/chat/anthropic-planner.js +1012 -0
  28. package/dist/chat/changelog-coverage-validator.d.ts +37 -0
  29. package/dist/chat/changelog-coverage-validator.js +215 -0
  30. package/dist/chat/chat-pipeline-context.d.ts +211 -0
  31. package/dist/chat/chat-pipeline-context.js +249 -0
  32. package/dist/chat/chat-pipeline-deterministic.d.ts +61 -0
  33. package/dist/chat/chat-pipeline-deterministic.js +407 -0
  34. package/dist/chat/chat-pipeline-image.d.ts +86 -0
  35. package/dist/chat/chat-pipeline-image.js +897 -0
  36. package/dist/chat/chat-pipeline-shared.d.ts +69 -0
  37. package/dist/chat/chat-pipeline-shared.js +212 -0
  38. package/dist/chat/chat-pipeline-translation.d.ts +27 -0
  39. package/dist/chat/chat-pipeline-translation.js +417 -0
  40. package/dist/chat/chat-pipeline-ui.d.ts +14 -0
  41. package/dist/chat/chat-pipeline-ui.js +244 -0
  42. package/dist/chat/chat-pipeline.d.ts +99 -0
  43. package/dist/chat/chat-pipeline.js +3999 -0
  44. package/dist/chat/decomposer.d.ts +21 -0
  45. package/dist/chat/decomposer.js +65 -0
  46. package/dist/chat/gemini-planner.d.ts +70 -0
  47. package/dist/chat/gemini-planner.js +541 -0
  48. package/dist/chat/hallucination-validator.d.ts +36 -0
  49. package/dist/chat/hallucination-validator.js +110 -0
  50. package/dist/chat/locale-strings.d.ts +47 -0
  51. package/dist/chat/locale-strings.js +100 -0
  52. package/dist/chat/plan-json-schema.d.ts +133 -0
  53. package/dist/chat/plan-json-schema.js +112 -0
  54. package/dist/chat/planner-types.d.ts +120 -0
  55. package/dist/chat/planner-types.js +66 -0
  56. package/dist/chat/planner.d.ts +148 -0
  57. package/dist/chat/planner.js +1361 -0
  58. package/dist/chat/prompts.d.ts +67 -0
  59. package/dist/chat/prompts.js +356 -0
  60. package/dist/chat/provider-routing.d.ts +14 -0
  61. package/dist/chat/provider-routing.js +27 -0
  62. package/dist/chat/variation-pipeline.d.ts +135 -0
  63. package/dist/chat/variation-pipeline.js +837 -0
  64. package/dist/chat/vision-alt-generator.d.ts +35 -0
  65. package/dist/chat/vision-alt-generator.js +152 -0
  66. package/dist/cms/adapter.d.ts +62 -0
  67. package/dist/cms/adapter.js +1 -0
  68. package/dist/cms/bootstrap.d.ts +17 -0
  69. package/dist/cms/bootstrap.js +85 -0
  70. package/dist/cms/editor-api-adapter.d.ts +23 -0
  71. package/dist/cms/editor-api-adapter.js +71 -0
  72. package/dist/cms/index.d.ts +4 -0
  73. package/dist/cms/index.js +3 -0
  74. package/dist/cms/json-file-adapter.d.ts +11 -0
  75. package/dist/cms/json-file-adapter.js +62 -0
  76. package/dist/demo-mode.d.ts +59 -0
  77. package/dist/demo-mode.js +201 -0
  78. package/dist/errors.d.ts +67 -0
  79. package/dist/errors.js +129 -0
  80. package/dist/http/chat-stream-resumable.d.ts +108 -0
  81. package/dist/http/chat-stream-resumable.js +290 -0
  82. package/dist/http/chat-stream.d.ts +99 -0
  83. package/dist/http/chat-stream.js +92 -0
  84. package/dist/image/gdrive-client.d.ts +22 -0
  85. package/dist/image/gdrive-client.js +215 -0
  86. package/dist/image/image-helpers.d.ts +95 -0
  87. package/dist/image/image-helpers.js +488 -0
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.js +1 -0
  90. package/dist/jira/jira-approval.d.ts +22 -0
  91. package/dist/jira/jira-approval.js +51 -0
  92. package/dist/jira/jira-client.d.ts +44 -0
  93. package/dist/jira/jira-client.js +313 -0
  94. package/dist/jira/jira-poller.d.ts +46 -0
  95. package/dist/jira/jira-poller.js +184 -0
  96. package/dist/jira/jira-processor.d.ts +103 -0
  97. package/dist/jira/jira-processor.js +1085 -0
  98. package/dist/jira/jira-types.d.ts +117 -0
  99. package/dist/jira/jira-types.js +38 -0
  100. package/dist/logger.d.ts +12 -0
  101. package/dist/logger.js +28 -0
  102. package/dist/migration/mcp-server-stdio.d.ts +8 -0
  103. package/dist/migration/mcp-server-stdio.js +672 -0
  104. package/dist/migration/migration-prompt.d.ts +7 -0
  105. package/dist/migration/migration-prompt.js +197 -0
  106. package/dist/migration/migration-tools.d.ts +17 -0
  107. package/dist/migration/migration-tools.js +159 -0
  108. package/dist/migration/scrape-cache.d.ts +9 -0
  109. package/dist/migration/scrape-cache.js +19 -0
  110. package/dist/nlp/deterministic-planner-context.d.ts +141 -0
  111. package/dist/nlp/deterministic-planner-context.js +362 -0
  112. package/dist/nlp/deterministic-planner-pages.d.ts +26 -0
  113. package/dist/nlp/deterministic-planner-pages.js +170 -0
  114. package/dist/nlp/deterministic-planner-patches.d.ts +80 -0
  115. package/dist/nlp/deterministic-planner-patches.js +508 -0
  116. package/dist/nlp/deterministic-planner-refs.d.ts +33 -0
  117. package/dist/nlp/deterministic-planner-refs.js +164 -0
  118. package/dist/nlp/deterministic-planner-suggestions.d.ts +49 -0
  119. package/dist/nlp/deterministic-planner-suggestions.js +579 -0
  120. package/dist/nlp/deterministic-planner.d.ts +85 -0
  121. package/dist/nlp/deterministic-planner.js +1631 -0
  122. package/dist/nlp/intent-detection.d.ts +309 -0
  123. package/dist/nlp/intent-detection.js +730 -0
  124. package/dist/nlp/intent-helpers.d.ts +15 -0
  125. package/dist/nlp/intent-helpers.js +243 -0
  126. package/dist/nlp/intent-patterns.d.ts +40 -0
  127. package/dist/nlp/intent-patterns.js +223 -0
  128. package/dist/nlp/plan-normalizer.d.ts +41 -0
  129. package/dist/nlp/plan-normalizer.js +1537 -0
  130. package/dist/ops/destructive-action-gate.d.ts +44 -0
  131. package/dist/ops/destructive-action-gate.js +90 -0
  132. package/dist/ops/ops-engine.d.ts +151 -0
  133. package/dist/ops/ops-engine.js +1394 -0
  134. package/dist/publish/diff-engine.d.ts +18 -0
  135. package/dist/publish/diff-engine.js +305 -0
  136. package/dist/publish/publish-helpers.d.ts +87 -0
  137. package/dist/publish/publish-helpers.js +521 -0
  138. package/dist/publish/publish-target-registry.d.ts +7 -0
  139. package/dist/publish/publish-target-registry.js +61 -0
  140. package/dist/publish/publish-target.d.ts +81 -0
  141. package/dist/publish/publish-target.js +1 -0
  142. package/dist/publish/targets/deploy-hook.d.ts +13 -0
  143. package/dist/publish/targets/deploy-hook.js +123 -0
  144. package/dist/publish/targets/git.d.ts +13 -0
  145. package/dist/publish/targets/git.js +55 -0
  146. package/dist/publish/targets/site-contract.d.ts +19 -0
  147. package/dist/publish/targets/site-contract.js +124 -0
  148. package/dist/state/content-source.d.ts +17 -0
  149. package/dist/state/content-source.js +1 -0
  150. package/dist/state/in-memory-content-source.d.ts +27 -0
  151. package/dist/state/in-memory-content-source.js +51 -0
  152. package/dist/state/session-lock.d.ts +13 -0
  153. package/dist/state/session-lock.js +29 -0
  154. package/dist/state/session-state.d.ts +310 -0
  155. package/dist/state/session-state.js +1083 -0
  156. package/dist/state/sqlite-store-singleton.d.ts +31 -0
  157. package/dist/state/sqlite-store-singleton.js +170 -0
  158. package/dist/state/sqlite-store.d.ts +135 -0
  159. package/dist/state/sqlite-store.js +421 -0
  160. package/dist/telemetry/chat-telemetry.d.ts +105 -0
  161. package/dist/telemetry/chat-telemetry.js +247 -0
  162. package/dist/telemetry/eval-candidate-store.d.ts +50 -0
  163. package/dist/telemetry/eval-candidate-store.js +120 -0
  164. package/dist/telemetry/feedback-store.d.ts +34 -0
  165. package/dist/telemetry/feedback-store.js +76 -0
  166. package/dist/telemetry/jira-telemetry.d.ts +57 -0
  167. package/dist/telemetry/jira-telemetry.js +68 -0
  168. package/dist/telemetry/migration-telemetry.d.ts +35 -0
  169. package/dist/telemetry/migration-telemetry.js +40 -0
  170. package/dist/telemetry/usage.d.ts +24 -0
  171. package/dist/telemetry/usage.js +80 -0
  172. package/dist/tools/builtin-registrations.d.ts +12 -0
  173. package/dist/tools/builtin-registrations.js +33 -0
  174. package/dist/tools/builtins/gdrive-browse.d.ts +3 -0
  175. package/dist/tools/builtins/gdrive-browse.js +68 -0
  176. package/dist/tools/builtins/image-generate.d.ts +3 -0
  177. package/dist/tools/builtins/image-generate.js +211 -0
  178. package/dist/tools/builtins/unsplash-get-by-id.d.ts +23 -0
  179. package/dist/tools/builtins/unsplash-get-by-id.js +119 -0
  180. package/dist/tools/builtins/unsplash-search.d.ts +3 -0
  181. package/dist/tools/builtins/unsplash-search.js +74 -0
  182. package/dist/tools/executor.d.ts +23 -0
  183. package/dist/tools/executor.js +169 -0
  184. package/dist/tools/index.d.ts +5 -0
  185. package/dist/tools/index.js +5 -0
  186. package/dist/tools/registry.d.ts +21 -0
  187. package/dist/tools/registry.js +75 -0
  188. package/dist/tools/runtime.d.ts +27 -0
  189. package/dist/tools/runtime.js +48 -0
  190. package/dist/tools/schema-validator.d.ts +24 -0
  191. package/dist/tools/schema-validator.js +88 -0
  192. package/dist/tools/types.d.ts +86 -0
  193. package/dist/tools/types.js +1 -0
  194. package/dist/variation-images.d.ts +19 -0
  195. package/dist/variation-images.js +12 -0
  196. package/package.json +78 -0
@@ -0,0 +1,215 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ // ---------------------------------------------------------------------------
4
+ // Auth
5
+ // ---------------------------------------------------------------------------
6
+ async function createDriveClient() {
7
+ const serviceAccountJson = process.env.GOOGLE_SERVICE_ACCOUNT_KEY_JSON?.trim();
8
+ const apiKey = process.env.GOOGLE_API_KEY?.trim();
9
+ if (!serviceAccountJson && !apiKey)
10
+ return null;
11
+ const { google } = await import("googleapis");
12
+ if (serviceAccountJson) {
13
+ try {
14
+ const key = JSON.parse(serviceAccountJson);
15
+ const auth = new google.auth.JWT({
16
+ email: key.client_email,
17
+ key: key.private_key,
18
+ scopes: ["https://www.googleapis.com/auth/drive.readonly"]
19
+ });
20
+ return google.drive({ version: "v3", auth });
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ return google.drive({ version: "v3", auth: apiKey });
27
+ }
28
+ let cachedClient;
29
+ async function getDriveClient() {
30
+ if (cachedClient !== undefined)
31
+ return cachedClient;
32
+ cachedClient = await createDriveClient();
33
+ return cachedClient;
34
+ }
35
+ /** Reset cached client (useful after env changes or in tests). */
36
+ export function resetDriveClient() {
37
+ cachedClient = undefined;
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // In-memory listing cache (5-min TTL, max 100 entries)
41
+ // ---------------------------------------------------------------------------
42
+ const CACHE_TTL_MS = 5 * 60 * 1000;
43
+ const CACHE_MAX_ENTRIES = 100;
44
+ const listCache = new Map();
45
+ function evictStaleCache() {
46
+ if (listCache.size <= CACHE_MAX_ENTRIES)
47
+ return;
48
+ const now = Date.now();
49
+ for (const [key, entry] of listCache) {
50
+ if (now - entry.ts > CACHE_TTL_MS)
51
+ listCache.delete(key);
52
+ }
53
+ // If still over limit after TTL eviction, drop oldest entries
54
+ if (listCache.size > CACHE_MAX_ENTRIES) {
55
+ const entries = Array.from(listCache.entries()).sort((a, b) => a[1].ts - b[1].ts);
56
+ const toRemove = entries.slice(0, entries.length - CACHE_MAX_ENTRIES);
57
+ for (const [key] of toRemove)
58
+ listCache.delete(key);
59
+ }
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Helpers
63
+ // ---------------------------------------------------------------------------
64
+ /** Convert a file name like "hero_image-v2.jpg" to alt text "hero image v2". */
65
+ export function fileNameToAlt(name) {
66
+ return name.replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ");
67
+ }
68
+ export function isGdriveConfigured() {
69
+ return Boolean(process.env.GOOGLE_DRIVE_FOLDER_ID?.trim() &&
70
+ (process.env.GOOGLE_SERVICE_ACCOUNT_KEY_JSON?.trim() || process.env.GOOGLE_API_KEY?.trim()));
71
+ }
72
+ /** Extract a bare folder ID from a value that may be a full Google Drive URL or a raw ID. */
73
+ function extractFolderId(value) {
74
+ // https://drive.google.com/drive/u/0/folders/FOLDER_ID or /drive/folders/FOLDER_ID
75
+ const urlMatch = value.match(/\/folders\/([a-zA-Z0-9_-]+)/);
76
+ if (urlMatch?.[1])
77
+ return urlMatch[1];
78
+ return value;
79
+ }
80
+ /** Resolve folder ID from an override or fall back to env var. Returns undefined if neither is set. */
81
+ export function resolveGdriveFolderId(override) {
82
+ const trimmed = override?.trim();
83
+ if (trimmed)
84
+ return extractFolderId(trimmed);
85
+ const envId = process.env.GOOGLE_DRIVE_FOLDER_ID?.trim();
86
+ if (envId)
87
+ return extractFolderId(envId);
88
+ return undefined;
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // List images
92
+ // ---------------------------------------------------------------------------
93
+ const MAX_SUBFOLDER_DEPTH = 3;
94
+ async function listImagesInFolder(drive, folderId, query, log, depth) {
95
+ const items = [];
96
+ const subfolderIds = [];
97
+ // Query both images and subfolders in this folder
98
+ const qParts = [
99
+ `'${folderId}' in parents`,
100
+ `(mimeType contains 'image/' or mimeType = 'application/vnd.google-apps.folder')`,
101
+ "trashed = false"
102
+ ];
103
+ if (query) {
104
+ // Name filter only applies to images; subfolders are always traversed
105
+ // We'll filter by name client-side for images while still discovering folders
106
+ }
107
+ const pageSize = 100;
108
+ let pageToken;
109
+ do {
110
+ const res = await drive.files.list({
111
+ q: qParts.join(" and "),
112
+ fields: "nextPageToken, files(id, name, mimeType, thumbnailLink, size, imageMediaMetadata)",
113
+ pageSize,
114
+ pageToken,
115
+ orderBy: "name"
116
+ });
117
+ for (const file of res.data.files ?? []) {
118
+ if (!file.id || !file.name)
119
+ continue;
120
+ if (file.mimeType === "application/vnd.google-apps.folder") {
121
+ if (depth < MAX_SUBFOLDER_DEPTH)
122
+ subfolderIds.push(file.id);
123
+ continue;
124
+ }
125
+ // Apply name filter client-side (so subfolders are still traversed)
126
+ if (query && !file.name.toLowerCase().includes(query.toLowerCase()))
127
+ continue;
128
+ items.push({
129
+ id: file.id,
130
+ name: file.name,
131
+ mimeType: file.mimeType ?? "image/jpeg",
132
+ thumbnailLink: file.thumbnailLink ?? "",
133
+ size: Number(file.size ?? 0),
134
+ width: file.imageMediaMetadata?.width ?? undefined,
135
+ height: file.imageMediaMetadata?.height ?? undefined
136
+ });
137
+ }
138
+ pageToken = res.data.nextPageToken ?? undefined;
139
+ } while (pageToken);
140
+ // Recursively list images in subfolders (in parallel)
141
+ if (subfolderIds.length > 0) {
142
+ const subResults = await Promise.all(subfolderIds.map((subId) => listImagesInFolder(drive, subId, query, log, depth + 1)));
143
+ for (const subItems of subResults)
144
+ items.push(...subItems);
145
+ }
146
+ return items;
147
+ }
148
+ export async function listImages(folderId, query, log, maxResults, skipCache) {
149
+ const drive = await getDriveClient();
150
+ if (!drive) {
151
+ log?.warn({ event: "gdrive_no_client" }, "Google Drive client not configured");
152
+ return [];
153
+ }
154
+ const cacheKey = `${folderId}::${query ?? ""}`;
155
+ if (!skipCache) {
156
+ const cached = listCache.get(cacheKey);
157
+ if (cached && Date.now() - cached.ts < CACHE_TTL_MS) {
158
+ return maxResults ? cached.items.slice(0, maxResults) : cached.items;
159
+ }
160
+ }
161
+ try {
162
+ const items = await listImagesInFolder(drive, folderId, query, log, 0);
163
+ listCache.set(cacheKey, { items, ts: Date.now() });
164
+ evictStaleCache();
165
+ log?.info({ event: "gdrive_list", folderId, query, count: items.length }, "Listed Google Drive images");
166
+ return maxResults ? items.slice(0, maxResults) : items;
167
+ }
168
+ catch (err) {
169
+ log?.warn({ event: "gdrive_list_error", folderId, error: err instanceof Error ? err.message : String(err) }, "Failed to list Google Drive images");
170
+ return [];
171
+ }
172
+ }
173
+ // ---------------------------------------------------------------------------
174
+ // Download + optimize
175
+ // ---------------------------------------------------------------------------
176
+ const MAX_WIDTH = 1536;
177
+ const MAX_HEIGHT = 1024;
178
+ const WEBP_QUALITY = 80;
179
+ export async function downloadImage(fileId, log) {
180
+ const generatedImageDir = process.env.ORCHESTRATOR_GENERATED_IMAGE_DIR ?? resolve(process.cwd(), "../../.data/generated-images");
181
+ const fileName = `gdrive_${fileId}.webp`;
182
+ const filePath = resolve(generatedImageDir, fileName);
183
+ // Try disk cache first — no need for existsSync, just attempt the read
184
+ try {
185
+ await readFile(filePath);
186
+ return { filePath, fileName };
187
+ }
188
+ catch {
189
+ // Not cached, proceed to download
190
+ }
191
+ const drive = await getDriveClient();
192
+ if (!drive)
193
+ return null;
194
+ try {
195
+ const res = await drive.files.get({ fileId, alt: "media" }, { responseType: "arraybuffer" });
196
+ const rawBytes = Buffer.from(res.data);
197
+ if (rawBytes.byteLength === 0)
198
+ return null;
199
+ const { default: sharp } = await import("sharp");
200
+ // Optimize: auto-orient first (before resize for correct dimensions), then resize + WebP
201
+ const optimized = await sharp(rawBytes)
202
+ .rotate() // auto-orient based on EXIF before resize
203
+ .resize({ width: MAX_WIDTH, height: MAX_HEIGHT, fit: "inside", withoutEnlargement: true })
204
+ .webp({ quality: WEBP_QUALITY })
205
+ .toBuffer();
206
+ await mkdir(generatedImageDir, { recursive: true });
207
+ await writeFile(filePath, optimized);
208
+ log?.info({ event: "gdrive_download", fileId, originalSize: rawBytes.byteLength, optimizedSize: optimized.byteLength }, `Downloaded and optimized Drive image ${fileId}`);
209
+ return { filePath, fileName };
210
+ }
211
+ catch (err) {
212
+ log?.warn({ event: "gdrive_download_error", fileId, error: err instanceof Error ? err.message : String(err) }, "Failed to download Google Drive image");
213
+ return null;
214
+ }
215
+ }
@@ -0,0 +1,95 @@
1
+ import type { PageDoc } from "@avocadostudio-ai/shared";
2
+ import { type UnsplashImage, type UnsplashResolveOptions } from "../variation-images.ts";
3
+ export type ImageLogger = {
4
+ info: (obj: Record<string, unknown>, msg: string) => void;
5
+ warn: (obj: Record<string, unknown>, msg: string) => void;
6
+ };
7
+ export declare function recordImageGenDuration(ms: number): void;
8
+ export declare function estimatedImageGenMs(): number;
9
+ export declare function normalizeUnsplashQuery(raw: string): string;
10
+ export declare function extractUnsplashQuery(message: string): string | undefined;
11
+ /**
12
+ * Detect explicit image generation intent in a message (e.g. "generate a new image", "new photo").
13
+ * Returns true for phrases that clearly request creating/replacing an image, but NOT for
14
+ * property edits like "change the image alt text".
15
+ */
16
+ export declare function isExplicitImageGenRequest(message: string): boolean;
17
+ /**
18
+ * Extract a detailed image description/prompt from a user message.
19
+ * Matches patterns like "generate a new image: [full description]".
20
+ * Returns the description (typically a rich prompt for AI image generation) or undefined.
21
+ */
22
+ export declare function extractImagePromptFromMessage(message: string): string | undefined;
23
+ export declare function imageKeywordsFromQuery(raw: string, max?: number): string[];
24
+ export declare function heroImageQueryFromContext(args: {
25
+ message: string;
26
+ currentPage: PageDoc;
27
+ targetBlock: PageDoc["blocks"][number];
28
+ patchCandidate?: Record<string, unknown>;
29
+ }): string;
30
+ export type VariationImageIntent = {
31
+ baseQuery: string;
32
+ subjectKeywords: string[];
33
+ varyBackgrounds: boolean;
34
+ styleTerms: string[];
35
+ provider: "unsplash" | "llm";
36
+ backgroundTerms: string[];
37
+ };
38
+ export declare function deriveVariationImageIntent(args: {
39
+ message: string;
40
+ block: PageDoc["blocks"][number];
41
+ }): VariationImageIntent;
42
+ export declare function buildVariationImageQuery(intent: VariationImageIntent, variationIndex: number): string;
43
+ export declare function buildVariationImagePrompt(args: {
44
+ intent: VariationImageIntent;
45
+ blockType: string;
46
+ variationIndex: number;
47
+ sectionContext?: string;
48
+ pageContext?: string;
49
+ }): string;
50
+ export declare function generateVariationImageWithOpenAI(args: {
51
+ prompt: string;
52
+ altText: string;
53
+ size?: string;
54
+ model?: string;
55
+ background?: "transparent" | "opaque" | "auto";
56
+ outputFormat?: "png" | "webp" | "jpeg";
57
+ log?: ImageLogger;
58
+ signal?: AbortSignal;
59
+ }): Promise<UnsplashImage | null>;
60
+ export declare function saveGeneratedImage(bytes: Buffer, prefix?: string, ext?: string): Promise<{
61
+ fileName: string;
62
+ url: string;
63
+ }>;
64
+ /** Extract the first inline image from a Gemini response, save to disk, return URL. */
65
+ export declare function saveGeminiInlineImage(parts: Array<{
66
+ inlineData?: {
67
+ data?: string;
68
+ mimeType?: string;
69
+ };
70
+ }>, prefix?: string): Promise<{
71
+ url: string;
72
+ mimeType: string;
73
+ } | null>;
74
+ export declare const GEMINI_ASPECT_RATIOS: Record<string, string>;
75
+ export declare function getGeminiClient(): Promise<any>;
76
+ export declare function getGeminiImageModel(): string;
77
+ export declare function generateVariationImageWithGemini(args: {
78
+ prompt: string;
79
+ altText: string;
80
+ aspectRatio?: string;
81
+ quality?: "draft" | "final";
82
+ background?: "transparent" | "opaque" | "auto";
83
+ model?: string;
84
+ log?: ImageLogger;
85
+ signal?: AbortSignal;
86
+ }): Promise<UnsplashImage | null>;
87
+ export declare function resolveUnsplashImage(query: string, options?: UnsplashResolveOptions, logContext?: {
88
+ chatRequestId?: string;
89
+ logger?: ImageLogger;
90
+ }): Promise<UnsplashImage | null>;
91
+ export declare function resolveGdriveImage(query: string, options?: {
92
+ chatRequestId?: string;
93
+ logger?: ImageLogger;
94
+ folderId?: string;
95
+ }): Promise<UnsplashImage | null>;