@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,69 @@
1
+ import type { Logger } from "../logger.ts";
2
+ import { CancelError as _CancelError, isCancelError as _isCancelError } from "../errors.ts";
3
+ import type { createChatTelemetryStore } from "../telemetry/chat-telemetry.ts";
4
+ import type { EvalCandidateStore } from "../telemetry/eval-candidate-store.ts";
5
+ import type { AIProvider, ModelKey } from "../state/session-state.ts";
6
+ import type { ToolRuntime } from "../tools/runtime.ts";
7
+ export type ChatPipelineContext = {
8
+ log: Logger;
9
+ chatTelemetry: ReturnType<typeof createChatTelemetryStore>;
10
+ evalCandidates?: EvalCandidateStore;
11
+ modelLookup: Record<AIProvider, Record<ModelKey, string>>;
12
+ availableProviders: AIProvider[];
13
+ toolRuntime: ToolRuntime;
14
+ };
15
+ export declare const GENERATING_IMAGE_PLACEHOLDER: string;
16
+ export declare const SEARCHING_IMAGE_PLACEHOLDER: string;
17
+ /** Returns true if the URL is a shimmer placeholder used during deferred image generation/search. */
18
+ /**
19
+ * A native image tool call deferred during planning so text ops stream
20
+ * immediately. The real tool is executed post-apply and the result is
21
+ * patched into the preview via follow-up SSE events.
22
+ */
23
+ export type DeferredNativeImageCall = {
24
+ toolName: "image.generate" | "unsplash.search";
25
+ input: Record<string, unknown>;
26
+ placeholderUrl: string;
27
+ };
28
+ /** Tool names whose execution is deferred to avoid blocking text streaming */
29
+ export declare const DEFERRABLE_IMAGE_TOOLS: Set<string>;
30
+ export declare function isGeneratingPlaceholder(url: string): boolean;
31
+ /** Remove any "Generating image..." SVG placeholders left in session state (e.g. after cancel). */
32
+ export declare function cleanupImagePlaceholders(session: string): number;
33
+ /** Metadata for a deferred create_page Hero image that still needs resolution. */
34
+ export type DeferredCreatePageImage = {
35
+ pageSlug: string;
36
+ blockId: string;
37
+ query: string;
38
+ pageTitle: string;
39
+ sectionContext: string;
40
+ };
41
+ /** Build a compact page directory from the session draft for the system prompt.
42
+ * Results are cached per session and invalidated when pages change. */
43
+ export declare function buildPageDirectory(session: string): string;
44
+ export declare function isVariationRequestMessage(message: string): boolean;
45
+ export declare function variationVerbIntent(message: string): "show" | "generate";
46
+ export declare function resolveEffectiveSlug(args: {
47
+ session: string;
48
+ requestedSlug: string;
49
+ activeBlockId?: string;
50
+ }): string;
51
+ export declare const CancelError: typeof _CancelError;
52
+ export declare const isCancelError: typeof _isCancelError;
53
+ export declare function throwIfCanceled(signal?: AbortSignal): void;
54
+ /** Race a promise against an abort signal. Rejects with CancelError if signal fires first. */
55
+ export declare function raceCancel<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T>;
56
+ /** Parse "Suggested next actions:" bullet list from summary text. */
57
+ export declare function parseSuggestionsFromSummary(text: string): {
58
+ summary: string;
59
+ suggestions: string[];
60
+ };
61
+ export declare function sseWrite(reply: {
62
+ raw: NodeJS.WritableStream;
63
+ }, payload: unknown): void;
64
+ export declare function sleepMs(durationMs: number): Promise<void>;
65
+ /**
66
+ * Suppress cancel errors on a promise but log any other errors at WARN.
67
+ * Use instead of `.catch(() => {})` to avoid hiding real failures.
68
+ */
69
+ export declare function suppressCancelOnly(promise: Promise<unknown>, log: Logger, label: string): void;
@@ -0,0 +1,212 @@
1
+ import { CancelError as _CancelError, isCancelError as _isCancelError } from "../errors.js";
2
+ import { toErrorDetail } from "../ops/ops-engine.js";
3
+ import { versions, getSessionDraft, getPage, orderSlugsHomeFirst, getContextCache, setContextCache } from "../state/session-state.js";
4
+ import { normalizeVariationTypos } from "./chat-pipeline-translation.js";
5
+ // ---------------------------------------------------------------------------
6
+ // Deferred image placeholder
7
+ // ---------------------------------------------------------------------------
8
+ /** Build a shimmer SVG data URI placeholder with a custom icon and label. */
9
+ function buildShimmerPlaceholder(icon, label) {
10
+ return [
11
+ `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 600'%3E`,
12
+ `%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='0'%3E`,
13
+ `%3Cstop offset='0%25' stop-color='%23e2e8f0'/%3E`,
14
+ `%3Cstop offset='50%25' stop-color='%23f1f5f9'%3E%3Canimate attributeName='offset' values='0;1;0' dur='2s' repeatCount='indefinite'/%3E%3C/stop%3E`,
15
+ `%3Cstop offset='100%25' stop-color='%23e2e8f0'/%3E`,
16
+ `%3C/linearGradient%3E%3C/defs%3E`,
17
+ `%3Crect width='1200' height='600' fill='url(%23g)'/%3E`,
18
+ icon,
19
+ `%3Ctext x='616' y='310' text-anchor='middle' fill='%2394a3b8' font-family='system-ui' font-size='36' font-weight='500'%3E${label}%3C/text%3E`,
20
+ `%3C/svg%3E`
21
+ ].join("");
22
+ }
23
+ // AI sparkle icon — 4-point diamond star with companion sparkle
24
+ const SPARKLE_ICON = [
25
+ `%3Cg transform='translate(448,296)' fill='%2394a3b8'%3E`,
26
+ `%3Cpath d='M0-12C1 -4 4-1 12 0 4 1 1 4 0 12-1 4-4 1-12 0-4-1-1-4 0-12Z'%3E`,
27
+ `%3Canimate attributeName='opacity' values='0.5;1;0.5' dur='2s' repeatCount='indefinite'/%3E%3C/path%3E`,
28
+ `%3Cpath d='M11-9C11.4-7 12.6-5.8 15-5.5 12.6-5.2 11.4-4 11-2 10.6-4 9.4-5.2 7-5.5 9.4-5.8 10.6-7 11-9Z' opacity='0.6'%3E`,
29
+ `%3Canimate attributeName='opacity' values='0.3;0.8;0.3' dur='1.6s' repeatCount='indefinite'/%3E%3C/path%3E`,
30
+ `%3C/g%3E`
31
+ ].join("");
32
+ // Magnifying glass icon
33
+ const SEARCH_ICON = [
34
+ `%3Cg transform='translate(460,280)' fill='none' stroke='%2394a3b8' stroke-width='3'%3E`,
35
+ `%3Ccircle cx='0' cy='0' r='10'%3E%3Canimate attributeName='opacity' values='0.4;1;0.4' dur='1.8s' repeatCount='indefinite'/%3E%3C/circle%3E`,
36
+ `%3Cline x1='7' y1='7' x2='14' y2='14'%3E%3Canimate attributeName='opacity' values='0.4;1;0.4' dur='1.8s' repeatCount='indefinite'/%3E%3C/line%3E`,
37
+ `%3C/g%3E`
38
+ ].join("");
39
+ export const GENERATING_IMAGE_PLACEHOLDER = buildShimmerPlaceholder(SPARKLE_ICON, "Generating image%E2%80%A6");
40
+ export const SEARCHING_IMAGE_PLACEHOLDER = buildShimmerPlaceholder(SEARCH_ICON, "Searching%E2%80%A6");
41
+ /** Tool names whose execution is deferred to avoid blocking text streaming */
42
+ export const DEFERRABLE_IMAGE_TOOLS = new Set(["image.generate", "unsplash.search"]);
43
+ export function isGeneratingPlaceholder(url) {
44
+ if (url.startsWith("data:image/svg+xml,") && (url.includes("Generating%20image") || url.includes("Generating image") || url.includes("Searching")))
45
+ return true;
46
+ if (url.includes("placehold.co") && (url.includes("Generating") || url.includes("Searching")))
47
+ return true;
48
+ return false;
49
+ }
50
+ /**
51
+ * Recursively walk an object and clear any string values that match the
52
+ * generating-image placeholder. Handles nested arrays (e.g. `cards[i].imageUrl`,
53
+ * `images[i].imageUrl`) and arbitrary prop depth.
54
+ */
55
+ function clearPlaceholdersDeep(obj) {
56
+ if (obj === null || obj === undefined || typeof obj !== "object")
57
+ return 0;
58
+ let count = 0;
59
+ if (Array.isArray(obj)) {
60
+ for (const item of obj) {
61
+ count += clearPlaceholdersDeep(item);
62
+ }
63
+ return count;
64
+ }
65
+ const record = obj;
66
+ for (const key of Object.keys(record)) {
67
+ const val = record[key];
68
+ if (typeof val === "string" && isGeneratingPlaceholder(val)) {
69
+ record[key] = "";
70
+ count += 1;
71
+ }
72
+ else if (typeof val === "object" && val !== null) {
73
+ count += clearPlaceholdersDeep(val);
74
+ }
75
+ }
76
+ return count;
77
+ }
78
+ /** Remove any "Generating image..." SVG placeholders left in session state (e.g. after cancel). */
79
+ export function cleanupImagePlaceholders(session) {
80
+ const draft = getSessionDraft(session);
81
+ let total = 0;
82
+ for (const [, page] of draft) {
83
+ for (const block of page.blocks) {
84
+ total += clearPlaceholdersDeep(block.props);
85
+ }
86
+ }
87
+ return total;
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // Utility
91
+ // ---------------------------------------------------------------------------
92
+ /** Build a compact page directory from the session draft for the system prompt.
93
+ * Results are cached per session and invalidated when pages change. */
94
+ export function buildPageDirectory(session) {
95
+ const cached = getContextCache(session);
96
+ const currentVersion = versions.get(session) ?? 0;
97
+ if (cached && cached.version === currentVersion && typeof cached.pageDirectory === "string") {
98
+ return cached.pageDirectory;
99
+ }
100
+ const draft = getSessionDraft(session);
101
+ if (draft.size === 0)
102
+ return "";
103
+ const slugs = orderSlugsHomeFirst(Array.from(draft.keys()));
104
+ const result = slugs
105
+ .map((slug) => {
106
+ const page = draft.get(slug);
107
+ const blockTypes = page.blocks.map((b) => b.type).join(", ");
108
+ return ` ${slug} "${page.title}" (${blockTypes})`;
109
+ })
110
+ .join("\n");
111
+ setContextCache(session, { version: currentVersion, pageDirectory: result });
112
+ return result;
113
+ }
114
+ export function isVariationRequestMessage(message) {
115
+ const normalized = normalizeVariationTypos(message.toLowerCase());
116
+ return (/\b(variations?|alternatives?|variants?|options)\b/.test(normalized) &&
117
+ /\b(generate|create|make|show|give|produce|draft)\b/.test(normalized));
118
+ }
119
+ // Disambiguate "show me the variations again" from "generate new variations".
120
+ // "fresh" verbs always mean regenerate even when paired with "show".
121
+ export function variationVerbIntent(message) {
122
+ const normalized = normalizeVariationTypos(message.toLowerCase());
123
+ if (/\b(new|different|another|more|fresh|regenerate|again)\b/.test(normalized))
124
+ return "generate";
125
+ if (/\b(show|see|view|reopen|reshow|display)\b/.test(normalized))
126
+ return "show";
127
+ return "generate";
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // Effective slug resolution
131
+ // ---------------------------------------------------------------------------
132
+ export function resolveEffectiveSlug(args) {
133
+ const { session, requestedSlug, activeBlockId } = args;
134
+ if (!activeBlockId)
135
+ return requestedSlug;
136
+ const current = getPage(session, requestedSlug);
137
+ if (current?.blocks.some((block) => block.id === activeBlockId))
138
+ return requestedSlug;
139
+ const draft = getSessionDraft(session);
140
+ for (const [slug, page] of draft) {
141
+ if (page.blocks.some((block) => block.id === activeBlockId))
142
+ return slug;
143
+ }
144
+ return requestedSlug;
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Cancel error — re-exported from errors.ts for backward compatibility
148
+ // ---------------------------------------------------------------------------
149
+ export const CancelError = _CancelError;
150
+ export const isCancelError = _isCancelError;
151
+ export function throwIfCanceled(signal) {
152
+ if (signal?.aborted)
153
+ throw new _CancelError(signal.reason ?? "user_canceled");
154
+ }
155
+ /** Race a promise against an abort signal. Rejects with CancelError if signal fires first. */
156
+ export function raceCancel(promise, signal) {
157
+ if (!signal)
158
+ return promise;
159
+ if (signal.aborted)
160
+ return Promise.reject(new _CancelError(signal.reason ?? "user_canceled"));
161
+ return Promise.race([
162
+ promise,
163
+ new Promise((_, reject) => {
164
+ signal.addEventListener("abort", () => reject(new _CancelError(signal.reason ?? "user_canceled")), { once: true });
165
+ })
166
+ ]);
167
+ }
168
+ // ---------------------------------------------------------------------------
169
+ // SSE write helper
170
+ // ---------------------------------------------------------------------------
171
+ /** Parse "Suggested next actions:" bullet list from summary text. */
172
+ export function parseSuggestionsFromSummary(text) {
173
+ const marker = /\n*(?:suggested\s+(?:next\s+)?actions|next\s+steps|you\s+(?:could|might)\s+(?:also|want\s+to)):\s*\n/i;
174
+ const match = text.match(marker);
175
+ if (!match || match.index === undefined)
176
+ return { summary: text.trim(), suggestions: [] };
177
+ const before = text.slice(0, match.index).trim();
178
+ const after = text.slice(match.index + match[0].length);
179
+ const suggestions = after
180
+ .split("\n")
181
+ .map(line => line.replace(/^[-•*]\s*/, "").replace(/\*\*/g, "").trim())
182
+ .filter(line => line.length > 5 && line.length < 120)
183
+ .slice(0, 4);
184
+ return { summary: before, suggestions };
185
+ }
186
+ export function sseWrite(reply, payload) {
187
+ const stream = reply.raw;
188
+ if (stream.destroyed || stream.writableEnded === true || stream.writable === false)
189
+ return;
190
+ try {
191
+ stream.write(`data: ${JSON.stringify(payload)}\n\n`);
192
+ }
193
+ catch {
194
+ // Client disconnected; ignore write errors for SSE.
195
+ }
196
+ }
197
+ export function sleepMs(durationMs) {
198
+ if (!Number.isFinite(durationMs) || durationMs <= 0)
199
+ return Promise.resolve();
200
+ return new Promise((resolve) => setTimeout(resolve, durationMs));
201
+ }
202
+ /**
203
+ * Suppress cancel errors on a promise but log any other errors at WARN.
204
+ * Use instead of `.catch(() => {})` to avoid hiding real failures.
205
+ */
206
+ export function suppressCancelOnly(promise, log, label) {
207
+ promise.catch((err) => {
208
+ if (!_isCancelError(err)) {
209
+ log.warn({ event: "suppressed_non_cancel_error", label, error: toErrorDetail(err) }, `${label}: suppressed non-cancel error`);
210
+ }
211
+ });
212
+ }
@@ -0,0 +1,27 @@
1
+ import type { EditPlan, PageDoc } from "@avocadostudio-ai/shared";
2
+ export type TranslationScope = "page" | "component" | "none";
3
+ export declare function sanitizeMessageForPlanning(message: string): string;
4
+ export declare function inferTranslationScopeFromMessage(message: string): TranslationScope;
5
+ export type CloneAndTranslateRequest = {
6
+ /** Explicit target route if the user named one ("into /test"), else undefined. */
7
+ newSlug?: string;
8
+ /** Canonical English language name for the follow-up translation message. */
9
+ languageName: string;
10
+ /** ISO 639-1 code (uppercased) for tagging the clone's title, e.g. "ES". */
11
+ languageCode: string;
12
+ };
13
+ export declare function parseCloneAndTranslateRequest(message: string): CloneAndTranslateRequest | null;
14
+ export declare function isNonEmptyString(value: unknown): boolean;
15
+ export declare function findExplicitCtaTargetCoverageGap(args: {
16
+ plan: EditPlan;
17
+ message: string;
18
+ currentPage: PageDoc;
19
+ slug: string;
20
+ }): string | null;
21
+ export declare function findFullPageTranslationCoverageGap(args: {
22
+ plan: EditPlan;
23
+ message: string;
24
+ currentPage: PageDoc;
25
+ slug: string;
26
+ }): string | null;
27
+ export declare function normalizeVariationTypos(text: string): string;