@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,244 @@
1
+ import { normalizeRouteCandidate } from "../nlp/intent-helpers.js";
2
+ import { orderSlugsHomeFirst } from "../state/session-state.js";
3
+ import { imageKeywordsFromQuery } from "../image/image-helpers.js";
4
+ export function sentenceCase(text) {
5
+ const trimmed = text.trim();
6
+ if (!trimmed)
7
+ return trimmed;
8
+ return trimmed[0].toUpperCase() + trimmed.slice(1);
9
+ }
10
+ /**
11
+ * Convert future-tense plan summaries to past tense for post-apply display.
12
+ * Handles "Will verb…" → "Verbed…" and bare imperative "Verb the…" → "Verbed the…"
13
+ */
14
+ const IRREGULAR = {
15
+ set: "Set", find: "Found", rewrite: "Rewrote", get: "Got",
16
+ put: "Put", cut: "Cut", hit: "Hit", run: "Ran", make: "Made",
17
+ keep: "Kept", build: "Built", send: "Sent", write: "Wrote",
18
+ give: "Gave", take: "Took"
19
+ };
20
+ function toPastTense(verb) {
21
+ const v = verb.toLowerCase();
22
+ const irregular = IRREGULAR[v];
23
+ if (irregular)
24
+ return verb[0] === verb[0].toUpperCase() ? irregular : irregular.toLowerCase();
25
+ // silent-e: "replace" → "replaced"
26
+ if (v.endsWith("e"))
27
+ return verb + "d";
28
+ // consonant-y: "copy" → "copied"
29
+ if (v.endsWith("y") && v.length > 2 && !/[aeiou]/.test(v[v.length - 2]))
30
+ return verb.slice(0, -1) + "ied";
31
+ // CVC doubling for short verbs: "stop" → "stopped"
32
+ if (v.length <= 4 && /[^aeiou][aeiou][^aeiouwxy]$/.test(v))
33
+ return verb + verb[verb.length - 1] + "ed";
34
+ return verb + "ed";
35
+ }
36
+ const IMPERATIVE_VERBS = [
37
+ "add", "update", "remove", "set", "replace", "create", "move",
38
+ "duplicate", "rename", "delete", "insert", "change", "find",
39
+ "resolve", "generate", "rewrite", "improve"
40
+ ];
41
+ const IMPERATIVE_RE = new RegExp(`^(${IMPERATIVE_VERBS.join("|")})\\b`, "im");
42
+ export function futureToPastTense(text) {
43
+ return text
44
+ // Generic "will + any verb" → past tense; capitalize if "Will" was capitalized (sentence start)
45
+ .replace(/\b(W|w)ill\s+([a-z]+)\b/gi, (_m, w, verb) => {
46
+ const past = toPastTense(verb);
47
+ return w === "W" ? past[0].toUpperCase() + past.slice(1) : past;
48
+ })
49
+ // Bare imperative at sentence start (limited to known verbs to avoid false positives)
50
+ .replace(IMPERATIVE_RE, (verb) => toPastTense(verb));
51
+ }
52
+ /**
53
+ * Inverse of {@link futureToPastTense}. Used on the approval-gate path
54
+ * (executionMode=plan_only): the LLM emits past tense per prompt rules,
55
+ * but ops have NOT yet been applied — the user still has to click
56
+ * "Approve plan". Flip past → future so the copy matches the UX.
57
+ *
58
+ * Conservative: only rewrites past-tense verbs at sentence boundaries
59
+ * (start of string or after ". ") so we don't mangle mid-sentence prose
60
+ * like "was set to 5" or "updated copy" used as an adjective.
61
+ */
62
+ const PAST_TO_FUTURE = {
63
+ added: "add",
64
+ updated: "update",
65
+ removed: "remove",
66
+ set: "set",
67
+ replaced: "replace",
68
+ created: "create",
69
+ moved: "move",
70
+ duplicated: "duplicate",
71
+ renamed: "rename",
72
+ deleted: "delete",
73
+ inserted: "insert",
74
+ changed: "change",
75
+ found: "find",
76
+ resolved: "resolve",
77
+ generated: "generate",
78
+ rewrote: "rewrite",
79
+ improved: "improve"
80
+ };
81
+ const SENTENCE_START_PAST_RE = new RegExp(`(^|[.!?]\\s+|\\*\\*)(${Object.keys(PAST_TO_FUTURE).join("|")})\\b`, "gi");
82
+ export function pastToFutureTense(text) {
83
+ return text.replace(SENTENCE_START_PAST_RE, (_match, lead, verb) => {
84
+ const base = PAST_TO_FUTURE[verb.toLowerCase()];
85
+ if (!base)
86
+ return `${lead}${verb}`;
87
+ const capitalized = verb[0] === verb[0].toUpperCase();
88
+ const willed = capitalized ? `Will ${base}` : `will ${base}`;
89
+ return `${lead}${willed}`;
90
+ });
91
+ }
92
+ export function firstUrlFromText(text) {
93
+ const match = text.match(/https?:\/\/[^\s"']+/);
94
+ return match ? match[0] : undefined;
95
+ }
96
+ // Detect alt-text values that are actually user instructions / chat prompts
97
+ // rather than image descriptions. The planner occasionally copies the user's
98
+ // message verbatim into imageAlt; treat those as missing so we fall back to
99
+ // a real description.
100
+ const ALT_INSTRUCTION_PATTERN = /^\s*(add|change|turn|make|replace|swap|remove|update|set|use|show|put|insert|generate|create|find|search|pick|choose|give|need|want|please|let'?s|can you|could you|i want|i need)\b/i;
101
+ export function looksLikeUserInstruction(value) {
102
+ return ALT_INSTRUCTION_PATTERN.test(value);
103
+ }
104
+ export function preferredImageAltText(args) {
105
+ const existingAltRaw = typeof args.existingAlt === "string" ? args.existingAlt.trim() : "";
106
+ const existingAlt = existingAltRaw.length > 0 && !looksLikeUserInstruction(existingAltRaw) ? existingAltRaw : "";
107
+ if (existingAlt.length > 0)
108
+ return existingAlt;
109
+ const resolvedAlt = typeof args.resolvedAlt === "string" ? args.resolvedAlt.trim() : "";
110
+ const query = typeof args.query === "string" ? args.query.trim() : "";
111
+ if (!query)
112
+ return resolvedAlt;
113
+ if (!resolvedAlt)
114
+ return sentenceCase(`Photo of ${query}`);
115
+ const queryKeywords = imageKeywordsFromQuery(query, 6);
116
+ if (queryKeywords.length === 0)
117
+ return resolvedAlt;
118
+ const altLower = resolvedAlt.toLowerCase();
119
+ const overlapCount = queryKeywords.filter((keyword) => altLower.includes(keyword.toLowerCase())).length;
120
+ // If Unsplash alt does not describe the requested subject well, prefer a query-based alt.
121
+ if (overlapCount === 0)
122
+ return sentenceCase(`Photo of ${query}`);
123
+ return resolvedAlt;
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Slug collection
127
+ // ---------------------------------------------------------------------------
128
+ export function collectMentionedSlugsFromPlan(plan, fallbackSlug) {
129
+ const seen = new Set();
130
+ const removed = new Set();
131
+ const push = (slug) => {
132
+ if (!slug || typeof slug !== "string")
133
+ return;
134
+ const normalized = normalizeRouteCandidate(slug);
135
+ if (!normalized)
136
+ return;
137
+ seen.add(normalized);
138
+ };
139
+ for (const op of plan.ops) {
140
+ if (op.op === "create_page") {
141
+ push(op.page.slug);
142
+ continue;
143
+ }
144
+ if (op.op === "rename_page") {
145
+ push(op.newPageSlug);
146
+ continue;
147
+ }
148
+ if (op.op === "remove_page") {
149
+ const normalized = normalizeRouteCandidate(op.pageSlug);
150
+ if (normalized)
151
+ removed.add(normalized);
152
+ continue;
153
+ }
154
+ if (op.op === "move_page") {
155
+ push(op.pageSlug);
156
+ push(op.afterPageSlug);
157
+ continue;
158
+ }
159
+ if (op.op === "duplicate_block") {
160
+ if (typeof op.toPageSlug === "string" && op.toPageSlug.length > 0)
161
+ push(op.toPageSlug);
162
+ else
163
+ push(op.pageSlug);
164
+ continue;
165
+ }
166
+ if (op.op === "duplicate_page") {
167
+ // pageSlug is the SOURCE (read-only) — exclude from "Open" chips so the
168
+ // user isn't offered a misleading link back to the page they just
169
+ // copied from. Only newPageSlug is the actual result of the duplicate.
170
+ push(op.newPageSlug);
171
+ push(op.afterPageSlug);
172
+ continue;
173
+ }
174
+ if (op.op === "update_site_config" || op.op === "update_theme")
175
+ continue;
176
+ push(op.pageSlug);
177
+ }
178
+ for (const slug of removed)
179
+ seen.delete(slug);
180
+ // Synthesize an "Open <current>" chip only when the plan produced SOME result
181
+ // but no explicit navigable target (e.g. a site-config / theme edit). A purely
182
+ // destructive plan — deleting a page — has no result page worth opening, so skip
183
+ // the fallback instead of offering a redundant "Open /" pill that points at the
184
+ // page the user is already on (the editor already auto-navigates off a deleted
185
+ // active page via pickUpdatedSlug).
186
+ if (seen.size === 0 && removed.size === 0) {
187
+ const normalizedFallback = normalizeRouteCandidate(fallbackSlug);
188
+ if (normalizedFallback)
189
+ seen.add(normalizedFallback);
190
+ }
191
+ return orderSlugsHomeFirst(Array.from(seen));
192
+ }
193
+ export function collectMentionedSlugsFromOps(ops, fallbackSlug) {
194
+ return collectMentionedSlugsFromPlan({
195
+ intent: "edit_plan",
196
+ summary_for_user: "",
197
+ change_log: [],
198
+ ops
199
+ }, fallbackSlug);
200
+ }
201
+ // ---------------------------------------------------------------------------
202
+ // Plan normalization for UI
203
+ // ---------------------------------------------------------------------------
204
+ export function normalizePlanCopyForUi(plan, currentPage) {
205
+ const rewrite = (text) => text
206
+ .replace(/\bhome page secondary cta\b/gi, "Hero secondary CTA")
207
+ .replace(/\bsecondary cta\b/gi, "Hero secondary CTA")
208
+ .replace(/\bhero block imageurl\b/gi, "Hero block image")
209
+ .replace(/\bimageurl\b/gi, "Hero block image")
210
+ .replace(/\bimagealt\b/gi, "Hero image alt text")
211
+ // Strip internal block IDs like "b_hero_1772150570555" or "block b_hero_..."
212
+ .replace(/\s*\bon\s+(?:block\s+)?b_[a-z]+_\d+\b/gi, "")
213
+ .replace(/\b(?:block\s+)?b_[a-z]+_\d+\b/gi, "");
214
+ const normalizedSummary = rewrite(plan.summary_for_user);
215
+ const normalizedChangeLog = plan.change_log.map(rewrite);
216
+ if (plan.intent !== "edit_plan" || plan.ops.length !== 1)
217
+ return plan;
218
+ const [op] = plan.ops;
219
+ if (op.op !== "update_props") {
220
+ if (normalizedSummary !== plan.summary_for_user || normalizedChangeLog.some((line, idx) => line !== plan.change_log[idx])) {
221
+ return { ...plan, summary_for_user: normalizedSummary, change_log: normalizedChangeLog };
222
+ }
223
+ return plan;
224
+ }
225
+ const block = currentPage.blocks.find((entry) => entry.id === op.blockId);
226
+ if (!block || block.type !== "Hero") {
227
+ if (normalizedSummary !== plan.summary_for_user || normalizedChangeLog.some((line, idx) => line !== plan.change_log[idx])) {
228
+ return { ...plan, summary_for_user: normalizedSummary, change_log: normalizedChangeLog };
229
+ }
230
+ return plan;
231
+ }
232
+ const patch = op.patch;
233
+ const hasSecondaryText = Object.prototype.hasOwnProperty.call(patch, "secondaryCtaText");
234
+ const hasSecondaryHref = Object.prototype.hasOwnProperty.call(patch, "secondaryCtaHref");
235
+ if (!hasSecondaryText && !hasSecondaryHref)
236
+ return plan;
237
+ const nextSummary = "Renamed the Hero secondary CTA.";
238
+ const nextChangeLog = ["Updated the Hero secondary CTA text/link."];
239
+ return {
240
+ ...plan,
241
+ summary_for_user: nextSummary,
242
+ change_log: nextChangeLog
243
+ };
244
+ }
@@ -0,0 +1,99 @@
1
+ import { type EditPlan, type Operation, type PageDoc } from "@avocadostudio-ai/shared";
2
+ import { type ChatPipelineContext } from "./chat-pipeline-shared.ts";
3
+ import { type ChatRequestBody, type ChatResult } from "../nlp/intent-detection.ts";
4
+ import { type ImageSourcePreference } from "../state/session-state.ts";
5
+ import { type SkippedOperation } from "../ops/ops-engine.ts";
6
+ import { demoPlanFromMessage } from "../nlp/deterministic-planner.ts";
7
+ import { generatePlanWithOpenAI, parseIntentWithOpenAI } from "./planner.ts";
8
+ import { generatePlanWithAnthropic, parseIntentWithAnthropic } from "./anthropic-planner.ts";
9
+ import { generatePlanWithGemini, parseIntentWithGemini } from "./gemini-planner.ts";
10
+ import { type ThinkingEvent } from "./planner-types.ts";
11
+ export { sentenceCase, firstUrlFromText, preferredImageAltText, collectMentionedSlugsFromPlan, collectMentionedSlugsFromOps, normalizePlanCopyForUi, futureToPastTense } from "./chat-pipeline-ui.ts";
12
+ export { sanitizeMessageForPlanning, inferTranslationScopeFromMessage, isNonEmptyString, findFullPageTranslationCoverageGap, findExplicitCtaTargetCoverageGap, parseCloneAndTranslateRequest, type CloneAndTranslateRequest, type TranslationScope } from "./chat-pipeline-translation.ts";
13
+ export { shouldPreferFastModelForMessage, shouldUseLlmIntentRouter, compactPlannerContextPack, minimalPlannerContextPack, shouldUseMinimalPlannerContext, shouldPreferFocusedTranslation, shouldEnableReasoningForMessage } from "./chat-pipeline-context.ts";
14
+ export { isRewriteLikeMessage, isPerformanceAwareMessage, isLikelyTextField, collectChangedTextFields, buildMetaChangeLogEntries, buildAiInsightChanges, buildOpChangeLogEntries, deterministicCreatePagePlan, deterministicDuplicatePagePlan, deterministicSelectedTextRewritePlan, shouldReturnDeterministicClarification, fmtSlug } from "./chat-pipeline-deterministic.ts";
15
+ export { blockHasImageUrlProp, parsePath, getValueAtPath, setValueAtPath, deleteValueAtPath, extractIndexedQueries, extractReferencedItemIndices, blockSupportsImageAtPath, detectImagePaths, imageQueryFromItem, shouldPopulateAllChildImages, findImageTargets, rewriteAddBlockToChildImageUpdate, withUnsplashHeroImage, shouldResolveCreatePageHeroImage, resolveHeroImageForCreatePage, detectImageOps } from "./chat-pipeline-image.ts";
16
+ export { type ChatPipelineContext, type DeferredCreatePageImage, GENERATING_IMAGE_PLACEHOLDER, SEARCHING_IMAGE_PLACEHOLDER, isGeneratingPlaceholder, cleanupImagePlaceholders, buildPageDirectory, resolveEffectiveSlug, CancelError, isCancelError, throwIfCanceled, raceCancel, sseWrite, sleepMs, suppressCancelOnly } from "./chat-pipeline-shared.ts";
17
+ /**
18
+ * Appends a source hint token ("unsplash" / "generate image") to the planner
19
+ * message IFF the user's message doesn't already name a source. Avoids
20
+ * contradicting an explicit prompt like "Generate an AI image of X" when the
21
+ * session preference happens to be Unsplash.
22
+ *
23
+ * Exported for unit tests.
24
+ */
25
+ export declare function applyImageSourceHint(plannerMessage: string, sanitizedMessage: string, preference: ImageSourcePreference | undefined): string;
26
+ export declare function isVisionAltGenerationRequest(args: {
27
+ message: string;
28
+ activeEditablePath?: string;
29
+ activeBlockId?: string;
30
+ currentPage: PageDoc;
31
+ }): boolean;
32
+ /**
33
+ * True when every op in the plan is an `update_props` whose patch touches
34
+ * only image-related fields (imageUrl/imageAlt/ogImage/logoUrl). Such plans
35
+ * should skip the approval gate — image resolution is deterministic work.
36
+ *
37
+ * Exported for unit tests.
38
+ */
39
+ export declare function isImageOnlyUpdatePropsPlan(plan: EditPlan): boolean;
40
+ export declare function setGeneratePlanWithOpenAIForTests(fn?: typeof generatePlanWithOpenAI): void;
41
+ export declare function setGeneratePlanWithAnthropicForTests(fn?: typeof generatePlanWithAnthropic): void;
42
+ export declare function setDemoPlanFromMessageForTests(fn?: typeof demoPlanFromMessage): void;
43
+ export declare function setParseIntentWithOpenAIForTests(fn?: typeof parseIntentWithOpenAI): void;
44
+ export declare function setParseIntentWithAnthropicForTests(fn?: typeof parseIntentWithAnthropic): void;
45
+ export declare function setGeneratePlanWithGeminiForTests(fn?: typeof generatePlanWithGemini): void;
46
+ export declare function setParseIntentWithGeminiForTests(fn?: typeof parseIntentWithGemini): void;
47
+ export declare function runChatPipeline(ctx: ChatPipelineContext, body: ChatRequestBody, options?: {
48
+ onPlanningToken?: (token: string) => void;
49
+ onThinking?: (event: ThinkingEvent) => void;
50
+ onFieldDraft?: (event: {
51
+ blockId: string;
52
+ editablePath: string;
53
+ value: string;
54
+ pageSlug?: string;
55
+ }) => void;
56
+ onPlannedOp?: (event: {
57
+ index: number;
58
+ op: Operation;
59
+ }) => void;
60
+ onSummaryChunk?: (text: string) => void;
61
+ onChangeLogEntry?: (entry: string) => void;
62
+ onOpApplied?: (event: {
63
+ index: number;
64
+ total: number;
65
+ op: Operation;
66
+ previewVersion: number;
67
+ focusBlockId?: string;
68
+ updatedSlug?: string;
69
+ }) => void;
70
+ onOpSkipped?: (event: {
71
+ index: number;
72
+ total: number;
73
+ op: Operation;
74
+ reason: SkippedOperation["reason"];
75
+ }) => void;
76
+ onStatusUpdate?: (message: string) => void;
77
+ onImageProgress?: (event: {
78
+ percent: number;
79
+ stage: string;
80
+ }) => void;
81
+ onPlanMeta?: (event: {
82
+ intent: EditPlan["intent"];
83
+ summary: string;
84
+ estimatedOps: number;
85
+ }) => void;
86
+ onRollbackStarted?: (event: {
87
+ appliedCount: number;
88
+ reason: string;
89
+ }) => void;
90
+ onRollbackDone?: (event: {
91
+ restoredVersion: number;
92
+ }) => void;
93
+ signal?: AbortSignal;
94
+ }): Promise<{
95
+ code: number;
96
+ payload: ChatResult | {
97
+ error: string;
98
+ };
99
+ }>;