@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.
- package/LICENSE +201 -0
- package/dist/agent/agent-context.d.ts +19 -0
- package/dist/agent/agent-context.js +67 -0
- package/dist/agent/agent-logger.d.ts +5 -0
- package/dist/agent/agent-logger.js +22 -0
- package/dist/agent/agent-loop-openai.d.ts +8 -0
- package/dist/agent/agent-loop-openai.js +172 -0
- package/dist/agent/agent-loop.d.ts +56 -0
- package/dist/agent/agent-loop.js +167 -0
- package/dist/agent/agent-provider.d.ts +28 -0
- package/dist/agent/agent-provider.js +63 -0
- package/dist/agent/agent-tools.d.ts +28 -0
- package/dist/agent/agent-tools.js +899 -0
- package/dist/agent/context/editing-guidelines.md +46 -0
- package/dist/agent/context/role.md +39 -0
- package/dist/agent/integration-prompt.d.ts +9 -0
- package/dist/agent/integration-prompt.js +154 -0
- package/dist/agent/sites-agent-context.d.ts +12 -0
- package/dist/agent/sites-agent-context.js +316 -0
- package/dist/agent/sites-agent-shared.d.ts +161 -0
- package/dist/agent/sites-agent-shared.js +1101 -0
- package/dist/agent/sites-agent-tools.d.ts +18 -0
- package/dist/agent/sites-agent-tools.js +1227 -0
- package/dist/chat/anthropic-cache.d.ts +20 -0
- package/dist/chat/anthropic-cache.js +54 -0
- package/dist/chat/anthropic-planner.d.ts +98 -0
- package/dist/chat/anthropic-planner.js +1012 -0
- package/dist/chat/changelog-coverage-validator.d.ts +37 -0
- package/dist/chat/changelog-coverage-validator.js +215 -0
- package/dist/chat/chat-pipeline-context.d.ts +211 -0
- package/dist/chat/chat-pipeline-context.js +249 -0
- package/dist/chat/chat-pipeline-deterministic.d.ts +61 -0
- package/dist/chat/chat-pipeline-deterministic.js +407 -0
- package/dist/chat/chat-pipeline-image.d.ts +86 -0
- package/dist/chat/chat-pipeline-image.js +897 -0
- package/dist/chat/chat-pipeline-shared.d.ts +69 -0
- package/dist/chat/chat-pipeline-shared.js +212 -0
- package/dist/chat/chat-pipeline-translation.d.ts +27 -0
- package/dist/chat/chat-pipeline-translation.js +417 -0
- package/dist/chat/chat-pipeline-ui.d.ts +14 -0
- package/dist/chat/chat-pipeline-ui.js +244 -0
- package/dist/chat/chat-pipeline.d.ts +99 -0
- package/dist/chat/chat-pipeline.js +3999 -0
- package/dist/chat/decomposer.d.ts +21 -0
- package/dist/chat/decomposer.js +65 -0
- package/dist/chat/gemini-planner.d.ts +70 -0
- package/dist/chat/gemini-planner.js +541 -0
- package/dist/chat/hallucination-validator.d.ts +36 -0
- package/dist/chat/hallucination-validator.js +110 -0
- package/dist/chat/locale-strings.d.ts +47 -0
- package/dist/chat/locale-strings.js +100 -0
- package/dist/chat/plan-json-schema.d.ts +133 -0
- package/dist/chat/plan-json-schema.js +112 -0
- package/dist/chat/planner-types.d.ts +120 -0
- package/dist/chat/planner-types.js +66 -0
- package/dist/chat/planner.d.ts +148 -0
- package/dist/chat/planner.js +1361 -0
- package/dist/chat/prompts.d.ts +67 -0
- package/dist/chat/prompts.js +356 -0
- package/dist/chat/provider-routing.d.ts +14 -0
- package/dist/chat/provider-routing.js +27 -0
- package/dist/chat/variation-pipeline.d.ts +135 -0
- package/dist/chat/variation-pipeline.js +837 -0
- package/dist/chat/vision-alt-generator.d.ts +35 -0
- package/dist/chat/vision-alt-generator.js +152 -0
- package/dist/cms/adapter.d.ts +62 -0
- package/dist/cms/adapter.js +1 -0
- package/dist/cms/bootstrap.d.ts +17 -0
- package/dist/cms/bootstrap.js +85 -0
- package/dist/cms/editor-api-adapter.d.ts +23 -0
- package/dist/cms/editor-api-adapter.js +71 -0
- package/dist/cms/index.d.ts +4 -0
- package/dist/cms/index.js +3 -0
- package/dist/cms/json-file-adapter.d.ts +11 -0
- package/dist/cms/json-file-adapter.js +62 -0
- package/dist/demo-mode.d.ts +59 -0
- package/dist/demo-mode.js +201 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.js +129 -0
- package/dist/http/chat-stream-resumable.d.ts +108 -0
- package/dist/http/chat-stream-resumable.js +290 -0
- package/dist/http/chat-stream.d.ts +99 -0
- package/dist/http/chat-stream.js +92 -0
- package/dist/image/gdrive-client.d.ts +22 -0
- package/dist/image/gdrive-client.js +215 -0
- package/dist/image/image-helpers.d.ts +95 -0
- package/dist/image/image-helpers.js +488 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/jira/jira-approval.d.ts +22 -0
- package/dist/jira/jira-approval.js +51 -0
- package/dist/jira/jira-client.d.ts +44 -0
- package/dist/jira/jira-client.js +313 -0
- package/dist/jira/jira-poller.d.ts +46 -0
- package/dist/jira/jira-poller.js +184 -0
- package/dist/jira/jira-processor.d.ts +103 -0
- package/dist/jira/jira-processor.js +1085 -0
- package/dist/jira/jira-types.d.ts +117 -0
- package/dist/jira/jira-types.js +38 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +28 -0
- package/dist/migration/mcp-server-stdio.d.ts +8 -0
- package/dist/migration/mcp-server-stdio.js +672 -0
- package/dist/migration/migration-prompt.d.ts +7 -0
- package/dist/migration/migration-prompt.js +197 -0
- package/dist/migration/migration-tools.d.ts +17 -0
- package/dist/migration/migration-tools.js +159 -0
- package/dist/migration/scrape-cache.d.ts +9 -0
- package/dist/migration/scrape-cache.js +19 -0
- package/dist/nlp/deterministic-planner-context.d.ts +141 -0
- package/dist/nlp/deterministic-planner-context.js +362 -0
- package/dist/nlp/deterministic-planner-pages.d.ts +26 -0
- package/dist/nlp/deterministic-planner-pages.js +170 -0
- package/dist/nlp/deterministic-planner-patches.d.ts +80 -0
- package/dist/nlp/deterministic-planner-patches.js +508 -0
- package/dist/nlp/deterministic-planner-refs.d.ts +33 -0
- package/dist/nlp/deterministic-planner-refs.js +164 -0
- package/dist/nlp/deterministic-planner-suggestions.d.ts +49 -0
- package/dist/nlp/deterministic-planner-suggestions.js +579 -0
- package/dist/nlp/deterministic-planner.d.ts +85 -0
- package/dist/nlp/deterministic-planner.js +1631 -0
- package/dist/nlp/intent-detection.d.ts +309 -0
- package/dist/nlp/intent-detection.js +730 -0
- package/dist/nlp/intent-helpers.d.ts +15 -0
- package/dist/nlp/intent-helpers.js +243 -0
- package/dist/nlp/intent-patterns.d.ts +40 -0
- package/dist/nlp/intent-patterns.js +223 -0
- package/dist/nlp/plan-normalizer.d.ts +41 -0
- package/dist/nlp/plan-normalizer.js +1537 -0
- package/dist/ops/destructive-action-gate.d.ts +44 -0
- package/dist/ops/destructive-action-gate.js +90 -0
- package/dist/ops/ops-engine.d.ts +151 -0
- package/dist/ops/ops-engine.js +1394 -0
- package/dist/publish/diff-engine.d.ts +18 -0
- package/dist/publish/diff-engine.js +305 -0
- package/dist/publish/publish-helpers.d.ts +87 -0
- package/dist/publish/publish-helpers.js +521 -0
- package/dist/publish/publish-target-registry.d.ts +7 -0
- package/dist/publish/publish-target-registry.js +61 -0
- package/dist/publish/publish-target.d.ts +81 -0
- package/dist/publish/publish-target.js +1 -0
- package/dist/publish/targets/deploy-hook.d.ts +13 -0
- package/dist/publish/targets/deploy-hook.js +123 -0
- package/dist/publish/targets/git.d.ts +13 -0
- package/dist/publish/targets/git.js +55 -0
- package/dist/publish/targets/site-contract.d.ts +19 -0
- package/dist/publish/targets/site-contract.js +124 -0
- package/dist/state/content-source.d.ts +17 -0
- package/dist/state/content-source.js +1 -0
- package/dist/state/in-memory-content-source.d.ts +27 -0
- package/dist/state/in-memory-content-source.js +51 -0
- package/dist/state/session-lock.d.ts +13 -0
- package/dist/state/session-lock.js +29 -0
- package/dist/state/session-state.d.ts +310 -0
- package/dist/state/session-state.js +1083 -0
- package/dist/state/sqlite-store-singleton.d.ts +31 -0
- package/dist/state/sqlite-store-singleton.js +170 -0
- package/dist/state/sqlite-store.d.ts +135 -0
- package/dist/state/sqlite-store.js +421 -0
- package/dist/telemetry/chat-telemetry.d.ts +105 -0
- package/dist/telemetry/chat-telemetry.js +247 -0
- package/dist/telemetry/eval-candidate-store.d.ts +50 -0
- package/dist/telemetry/eval-candidate-store.js +120 -0
- package/dist/telemetry/feedback-store.d.ts +34 -0
- package/dist/telemetry/feedback-store.js +76 -0
- package/dist/telemetry/jira-telemetry.d.ts +57 -0
- package/dist/telemetry/jira-telemetry.js +68 -0
- package/dist/telemetry/migration-telemetry.d.ts +35 -0
- package/dist/telemetry/migration-telemetry.js +40 -0
- package/dist/telemetry/usage.d.ts +24 -0
- package/dist/telemetry/usage.js +80 -0
- package/dist/tools/builtin-registrations.d.ts +12 -0
- package/dist/tools/builtin-registrations.js +33 -0
- package/dist/tools/builtins/gdrive-browse.d.ts +3 -0
- package/dist/tools/builtins/gdrive-browse.js +68 -0
- package/dist/tools/builtins/image-generate.d.ts +3 -0
- package/dist/tools/builtins/image-generate.js +211 -0
- package/dist/tools/builtins/unsplash-get-by-id.d.ts +23 -0
- package/dist/tools/builtins/unsplash-get-by-id.js +119 -0
- package/dist/tools/builtins/unsplash-search.d.ts +3 -0
- package/dist/tools/builtins/unsplash-search.js +74 -0
- package/dist/tools/executor.d.ts +23 -0
- package/dist/tools/executor.js +169 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/registry.d.ts +21 -0
- package/dist/tools/registry.js +75 -0
- package/dist/tools/runtime.d.ts +27 -0
- package/dist/tools/runtime.js +48 -0
- package/dist/tools/schema-validator.d.ts +24 -0
- package/dist/tools/schema-validator.js +88 -0
- package/dist/tools/types.d.ts +86 -0
- package/dist/tools/types.js +1 -0
- package/dist/variation-images.d.ts +19 -0
- package/dist/variation-images.js +12 -0
- 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;
|