@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,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-plan change_log coverage validator.
|
|
3
|
+
*
|
|
4
|
+
* When a planner returns an edit_plan with N ops, the user reads change_log
|
|
5
|
+
* to decide whether to approve. If change_log is shorter than ops[], the
|
|
6
|
+
* user is approving blind to the uncovered ops — a silent bait-and-switch
|
|
7
|
+
* that erodes trust. If change_log is LONGER than ops[], the planner is
|
|
8
|
+
* describing changes it won't actually make (e.g. emits `duplicate_page`
|
|
9
|
+
* alone but narrates 4 follow-up `update_props` edits in change_log); the
|
|
10
|
+
* approval UI then promises content the apply step will silently drop.
|
|
11
|
+
*
|
|
12
|
+
* For the under-coverage case, the validator appends a synthesized bullet
|
|
13
|
+
* per uncovered op. For the over-coverage case, it trims trailing entries
|
|
14
|
+
* so the rendered plan matches what will actually run. Both directions
|
|
15
|
+
* report counts for telemetry so we can track how often planners drift.
|
|
16
|
+
*/
|
|
17
|
+
import { type EditPlan, type PageDoc } from "@avocadostudio-ai/shared";
|
|
18
|
+
export type ChangelogCoverageResult = {
|
|
19
|
+
plan: EditPlan;
|
|
20
|
+
missingCount: number;
|
|
21
|
+
synthesizedEntries: string[];
|
|
22
|
+
extraCount: number;
|
|
23
|
+
droppedEntries: string[];
|
|
24
|
+
/** Number of update_props entries rewritten because they named the wrong field (#13). */
|
|
25
|
+
fieldMislabelCount: number;
|
|
26
|
+
relabeledEntries: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Ensure change_log has at least one entry per op for edit_plan intents.
|
|
30
|
+
* When short, synthesize a generic entry per uncovered op so the user sees
|
|
31
|
+
* an accurate count of changes. Non-edit_plan intents and empty ops lists
|
|
32
|
+
* are passed through unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateChangelogCoverage(args: {
|
|
35
|
+
plan: EditPlan;
|
|
36
|
+
draft: Map<string, PageDoc>;
|
|
37
|
+
}): ChangelogCoverageResult;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-plan change_log coverage validator.
|
|
3
|
+
*
|
|
4
|
+
* When a planner returns an edit_plan with N ops, the user reads change_log
|
|
5
|
+
* to decide whether to approve. If change_log is shorter than ops[], the
|
|
6
|
+
* user is approving blind to the uncovered ops — a silent bait-and-switch
|
|
7
|
+
* that erodes trust. If change_log is LONGER than ops[], the planner is
|
|
8
|
+
* describing changes it won't actually make (e.g. emits `duplicate_page`
|
|
9
|
+
* alone but narrates 4 follow-up `update_props` edits in change_log); the
|
|
10
|
+
* approval UI then promises content the apply step will silently drop.
|
|
11
|
+
*
|
|
12
|
+
* For the under-coverage case, the validator appends a synthesized bullet
|
|
13
|
+
* per uncovered op. For the over-coverage case, it trims trailing entries
|
|
14
|
+
* so the rendered plan matches what will actually run. Both directions
|
|
15
|
+
* report counts for telemetry so we can track how often planners drift.
|
|
16
|
+
*/
|
|
17
|
+
import { getBlockMeta, getPropDisplayName } from "@avocadostudio-ai/shared";
|
|
18
|
+
// Distinctive label tokens (len ≥ 4) used to tell sibling fields apart —
|
|
19
|
+
// "secondary" distinguishes "Secondary CTA Text" from "CTA Text". Short shared
|
|
20
|
+
// tokens ("cta", "text") are dropped so overlap doesn't mask a wrong-field claim.
|
|
21
|
+
function labelTokens(label) {
|
|
22
|
+
return label
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.split(/[^a-z0-9]+/)
|
|
25
|
+
.filter((t) => t.length >= 4);
|
|
26
|
+
}
|
|
27
|
+
const word = (t) => new RegExp(`\\b${t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
28
|
+
/**
|
|
29
|
+
* A change_log/summary entry for an update_props op "mislabels" when it names a
|
|
30
|
+
* field the op does NOT touch (a sibling on the same block) by a distinctive token
|
|
31
|
+
* and mentions none of the fields it DOES touch. That's an affirmatively false
|
|
32
|
+
* label — the user approves "rename the secondary CTA" while their heading,
|
|
33
|
+
* subheading, and primary CTA get rewritten (#13). Returns an accurate replacement
|
|
34
|
+
* string, or null when the narration is fine / can't be judged confidently.
|
|
35
|
+
*/
|
|
36
|
+
function detectFieldMislabel(args) {
|
|
37
|
+
const { op, narration, draft } = args;
|
|
38
|
+
const block = draft.get(op.pageSlug)?.blocks.find((b) => b.id === op.blockId);
|
|
39
|
+
const meta = block ? getBlockMeta(block.type) : undefined;
|
|
40
|
+
if (!block || !meta)
|
|
41
|
+
return null;
|
|
42
|
+
const changedKeys = Object.keys(op.patch ?? {});
|
|
43
|
+
if (changedKeys.length === 0)
|
|
44
|
+
return null;
|
|
45
|
+
const siblingKeys = Object.keys(meta.fields ?? {}).filter((k) => !changedKeys.includes(k));
|
|
46
|
+
if (siblingKeys.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
const name = meta.displayName ?? block.type;
|
|
49
|
+
const text = narration.toLowerCase();
|
|
50
|
+
// The block name itself ("Hero") shows up in most field labels ("Hero image")
|
|
51
|
+
// and in the narration ("the Hero block"), so its tokens carry no field signal —
|
|
52
|
+
// ignore them, or "Hero image" would match every mention of the Hero block.
|
|
53
|
+
const ignore = new Set(labelTokens(name));
|
|
54
|
+
const changedTokens = new Set(changedKeys.flatMap((k) => labelTokens(getPropDisplayName(block.type, k))).concat(changedKeys.map((k) => k.toLowerCase())));
|
|
55
|
+
// A patch on a LIST field (e.g. `cards`) legitimately edits its ITEM fields (a
|
|
56
|
+
// card's title/description). Those item-field names share tokens with scalar
|
|
57
|
+
// siblings — a CardGrid has both a scalar `title` ("Section title") and a card
|
|
58
|
+
// `title`. Without folding the changed list's item-field tokens in, accurate
|
|
59
|
+
// narration like "Renamed the first card title." reads as naming the scalar
|
|
60
|
+
// sibling and we'd overwrite it with a vaguer label. Treat them as "changed".
|
|
61
|
+
for (const k of changedKeys) {
|
|
62
|
+
const listMeta = meta.listFields?.[k];
|
|
63
|
+
if (!listMeta)
|
|
64
|
+
continue;
|
|
65
|
+
for (const [itemKey, itemMeta] of Object.entries(listMeta.itemFields ?? {})) {
|
|
66
|
+
changedTokens.add(itemKey.toLowerCase());
|
|
67
|
+
for (const t of labelTokens(itemMeta.label ?? itemKey))
|
|
68
|
+
changedTokens.add(t);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const siblingTokens = new Set(siblingKeys.flatMap((k) => labelTokens(getPropDisplayName(block.type, k))));
|
|
72
|
+
// Does the narration name a sibling field by a token unique to it (not shared
|
|
73
|
+
// with a changed field, not the block name)?
|
|
74
|
+
const namesWrongSibling = siblingKeys.some((k) => labelTokens(getPropDisplayName(block.type, k))
|
|
75
|
+
.filter((t) => !changedTokens.has(t) && !ignore.has(t))
|
|
76
|
+
.some((t) => word(t).test(text)));
|
|
77
|
+
if (!namesWrongSibling)
|
|
78
|
+
return null;
|
|
79
|
+
// Does it reference any CHANGED field by a token NOT shared with a sibling or
|
|
80
|
+
// the block name?
|
|
81
|
+
const mentionsChanged = changedKeys.some((k) => {
|
|
82
|
+
const tokens = labelTokens(getPropDisplayName(block.type, k)).filter((t) => !siblingTokens.has(t) && !ignore.has(t));
|
|
83
|
+
tokens.push(k.toLowerCase());
|
|
84
|
+
return tokens.some((t) => word(t).test(text));
|
|
85
|
+
});
|
|
86
|
+
if (mentionsChanged)
|
|
87
|
+
return null;
|
|
88
|
+
const labels = changedKeys.map((k) => getPropDisplayName(block.type, k));
|
|
89
|
+
return { replacement: `Will update the ${name} ${labels.join(", ")}.` };
|
|
90
|
+
}
|
|
91
|
+
function lookupBlockTypeName(args) {
|
|
92
|
+
const { blockId, pageSlug, draft } = args;
|
|
93
|
+
const page = draft.get(pageSlug);
|
|
94
|
+
const hit = page?.blocks.find((b) => b.id === blockId);
|
|
95
|
+
if (!hit)
|
|
96
|
+
return undefined;
|
|
97
|
+
return getBlockMeta(hit.type)?.displayName ?? hit.type;
|
|
98
|
+
}
|
|
99
|
+
function describeBlockOp(args) {
|
|
100
|
+
const name = lookupBlockTypeName(args);
|
|
101
|
+
return name ? `Will ${args.verb} the ${name} block.` : `Will ${args.verb} a block.`;
|
|
102
|
+
}
|
|
103
|
+
function describeOp(op, draft) {
|
|
104
|
+
switch (op.op) {
|
|
105
|
+
case "update_props":
|
|
106
|
+
return describeBlockOp({ verb: "update", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
107
|
+
case "add_block": {
|
|
108
|
+
const name = getBlockMeta(op.block.type)?.displayName ?? op.block.type;
|
|
109
|
+
return `Will add a new ${name} block.`;
|
|
110
|
+
}
|
|
111
|
+
case "remove_block":
|
|
112
|
+
return describeBlockOp({ verb: "remove", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
113
|
+
case "move_block":
|
|
114
|
+
return describeBlockOp({ verb: "reorder", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
115
|
+
case "duplicate_block":
|
|
116
|
+
return describeBlockOp({ verb: "duplicate", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
117
|
+
case "add_item":
|
|
118
|
+
return describeBlockOp({ verb: "add an item to", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
119
|
+
case "update_item":
|
|
120
|
+
return describeBlockOp({ verb: "update an item in", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
121
|
+
case "remove_item":
|
|
122
|
+
return describeBlockOp({ verb: "remove an item from", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
123
|
+
case "move_item":
|
|
124
|
+
return describeBlockOp({ verb: "reorder an item in", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
125
|
+
case "reorder_items":
|
|
126
|
+
return describeBlockOp({ verb: "reorder the items in", blockId: op.blockId, pageSlug: op.pageSlug, draft });
|
|
127
|
+
case "reorder_blocks":
|
|
128
|
+
return `Will reorder the sections on ${op.pageSlug}.`;
|
|
129
|
+
case "create_page":
|
|
130
|
+
return `Will create page ${op.page.slug}.`;
|
|
131
|
+
case "remove_page":
|
|
132
|
+
return `Will remove page ${op.pageSlug}.`;
|
|
133
|
+
case "rename_page":
|
|
134
|
+
return `Will rename page ${op.pageSlug}.`;
|
|
135
|
+
case "move_page":
|
|
136
|
+
return `Will reorder page ${op.pageSlug}.`;
|
|
137
|
+
case "duplicate_page":
|
|
138
|
+
return `Will duplicate page ${op.pageSlug}.`;
|
|
139
|
+
case "update_page_meta":
|
|
140
|
+
return "Will update page metadata.";
|
|
141
|
+
case "update_site_config":
|
|
142
|
+
return "Will update site settings.";
|
|
143
|
+
case "update_theme":
|
|
144
|
+
return "Will update the site theme.";
|
|
145
|
+
default: {
|
|
146
|
+
const _exhaustive = op;
|
|
147
|
+
return "Will apply an additional change.";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Ensure change_log has at least one entry per op for edit_plan intents.
|
|
153
|
+
* When short, synthesize a generic entry per uncovered op so the user sees
|
|
154
|
+
* an accurate count of changes. Non-edit_plan intents and empty ops lists
|
|
155
|
+
* are passed through unchanged.
|
|
156
|
+
*/
|
|
157
|
+
export function validateChangelogCoverage(args) {
|
|
158
|
+
const { plan, draft } = args;
|
|
159
|
+
const empty = {
|
|
160
|
+
plan,
|
|
161
|
+
missingCount: 0,
|
|
162
|
+
synthesizedEntries: [],
|
|
163
|
+
extraCount: 0,
|
|
164
|
+
droppedEntries: [],
|
|
165
|
+
fieldMislabelCount: 0,
|
|
166
|
+
relabeledEntries: []
|
|
167
|
+
};
|
|
168
|
+
if (plan.intent !== "edit_plan")
|
|
169
|
+
return empty;
|
|
170
|
+
const opCount = plan.ops.length;
|
|
171
|
+
// Field-level pass (#13): an update_props entry can be the right COUNT yet name
|
|
172
|
+
// the wrong field. Relabel each mislabeled update_props entry from the op's
|
|
173
|
+
// actual patch; for a single-op plan also correct the summary, which carries the
|
|
174
|
+
// same false claim. Runs before the count logic so it applies at equal counts.
|
|
175
|
+
const relabeledEntries = [];
|
|
176
|
+
for (let i = 0; i < plan.ops.length; i++) {
|
|
177
|
+
const op = plan.ops[i];
|
|
178
|
+
if (op.op !== "update_props")
|
|
179
|
+
continue;
|
|
180
|
+
const entry = i < plan.change_log.length ? plan.change_log[i] : "";
|
|
181
|
+
const narration = opCount === 1 ? `${entry} ${plan.summary_for_user ?? ""}` : entry;
|
|
182
|
+
const mislabel = detectFieldMislabel({ op, narration, draft });
|
|
183
|
+
if (!mislabel)
|
|
184
|
+
continue;
|
|
185
|
+
if (i < plan.change_log.length)
|
|
186
|
+
plan.change_log[i] = mislabel.replacement;
|
|
187
|
+
if (opCount === 1)
|
|
188
|
+
plan.summary_for_user = mislabel.replacement;
|
|
189
|
+
relabeledEntries.push(mislabel.replacement);
|
|
190
|
+
}
|
|
191
|
+
const changeCount = plan.change_log.length;
|
|
192
|
+
if (changeCount === opCount) {
|
|
193
|
+
return { ...empty, fieldMislabelCount: relabeledEntries.length, relabeledEntries };
|
|
194
|
+
}
|
|
195
|
+
// Empty ops list with non-empty change_log is almost always a planner
|
|
196
|
+
// confusion (intent should have been content_answer). Leave it for
|
|
197
|
+
// higher-level handling rather than wiping the user-facing copy here.
|
|
198
|
+
if (opCount === 0)
|
|
199
|
+
return empty;
|
|
200
|
+
if (changeCount < opCount) {
|
|
201
|
+
const missingCount = opCount - changeCount;
|
|
202
|
+
const synthesizedEntries = [];
|
|
203
|
+
for (let i = changeCount; i < opCount; i++) {
|
|
204
|
+
synthesizedEntries.push(describeOp(plan.ops[i], draft));
|
|
205
|
+
}
|
|
206
|
+
plan.change_log = [...plan.change_log, ...synthesizedEntries];
|
|
207
|
+
return { plan, missingCount, synthesizedEntries, extraCount: 0, droppedEntries: [], fieldMislabelCount: relabeledEntries.length, relabeledEntries };
|
|
208
|
+
}
|
|
209
|
+
// changeCount > opCount: planner described more changes than it emitted ops
|
|
210
|
+
// for. Trim trailing entries so the approval UI matches reality.
|
|
211
|
+
const extraCount = changeCount - opCount;
|
|
212
|
+
const droppedEntries = plan.change_log.slice(opCount);
|
|
213
|
+
plan.change_log = plan.change_log.slice(0, opCount);
|
|
214
|
+
return { plan, missingCount: 0, synthesizedEntries: [], extraCount, droppedEntries, fieldMislabelCount: relabeledEntries.length, relabeledEntries };
|
|
215
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { plannerContextPack } from "../nlp/deterministic-planner.ts";
|
|
2
|
+
import type { EditPlan, PageDoc } from "@avocadostudio-ai/shared";
|
|
3
|
+
import { type TranslationScope } from "./chat-pipeline-translation.ts";
|
|
4
|
+
export declare function shouldPreferFastModelForMessage(message: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Returns true when the message is complex/ambiguous enough that we should
|
|
7
|
+
* auto-escalate to an Anthropic thinking-capable model with extended thinking
|
|
8
|
+
* enabled. Designed to complement `shouldPreferFastModelForMessage` (which
|
|
9
|
+
* downgrades simple edits) — this one upgrades.
|
|
10
|
+
*
|
|
11
|
+
* Positive signals:
|
|
12
|
+
* - Multi-step / decomposable request
|
|
13
|
+
* - Clarification follow-up (user is correcting an earlier result)
|
|
14
|
+
* - Structural verbs suggesting site-level reasoning (restructure, redesign,
|
|
15
|
+
* reorganize, rewrite tone, convert into, persuasive, narrative)
|
|
16
|
+
* - Long request with multiple quoted values (compound multi-field)
|
|
17
|
+
*/
|
|
18
|
+
export declare function shouldEnableReasoningForMessage(message: string): boolean;
|
|
19
|
+
export declare function shouldUseLlmIntentRouter(message: string): boolean;
|
|
20
|
+
export declare function compactPlannerContextPack(args: {
|
|
21
|
+
contextPack: ReturnType<typeof plannerContextPack>;
|
|
22
|
+
message: string;
|
|
23
|
+
translationScope: TranslationScope;
|
|
24
|
+
}): {
|
|
25
|
+
selected: {
|
|
26
|
+
blockId: string | null;
|
|
27
|
+
blockType: string | null;
|
|
28
|
+
editablePath: string | null;
|
|
29
|
+
block: {
|
|
30
|
+
id: string;
|
|
31
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
32
|
+
props: Record<string, unknown>;
|
|
33
|
+
selectedEditablePath: string | null;
|
|
34
|
+
selectedEditableValue: {} | null;
|
|
35
|
+
} | null;
|
|
36
|
+
imageUrlForVision: string | null;
|
|
37
|
+
};
|
|
38
|
+
neighbors: {
|
|
39
|
+
previous: {
|
|
40
|
+
id: string;
|
|
41
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
42
|
+
} | null;
|
|
43
|
+
next: {
|
|
44
|
+
id: string;
|
|
45
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
46
|
+
} | null;
|
|
47
|
+
};
|
|
48
|
+
pageOutline: {
|
|
49
|
+
id: string;
|
|
50
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
51
|
+
props: Record<string, unknown>;
|
|
52
|
+
arrayProps: Record<string, {
|
|
53
|
+
length: number;
|
|
54
|
+
labels?: string[];
|
|
55
|
+
}>;
|
|
56
|
+
}[];
|
|
57
|
+
pageMeta: import("@avocadostudio-ai/shared").PageMeta | null;
|
|
58
|
+
pageIntent: string;
|
|
59
|
+
recentSuccessfulEdits: {
|
|
60
|
+
at: string;
|
|
61
|
+
summary: string;
|
|
62
|
+
ops: ("update_props" | "create_page" | "add_block" | "remove_block" | "move_block" | "duplicate_block" | "add_item" | "update_item" | "remove_item" | "move_item" | "reorder_items" | "reorder_blocks" | "rename_page" | "remove_page" | "move_page" | "duplicate_page" | "update_page_meta" | "update_site_config" | "update_theme")[];
|
|
63
|
+
}[];
|
|
64
|
+
resolvedReferences: {
|
|
65
|
+
target: {
|
|
66
|
+
id: string;
|
|
67
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
68
|
+
} | null;
|
|
69
|
+
anchor: {
|
|
70
|
+
id: string;
|
|
71
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
72
|
+
} | null;
|
|
73
|
+
mentionedBlocks: {
|
|
74
|
+
id: string;
|
|
75
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
76
|
+
reason: string;
|
|
77
|
+
}[];
|
|
78
|
+
};
|
|
79
|
+
siteConfig: {
|
|
80
|
+
name: string | null;
|
|
81
|
+
logo: string | null;
|
|
82
|
+
navLabels: Record<string, string>;
|
|
83
|
+
navGroups: Record<string, string[]>;
|
|
84
|
+
defaultNavLabels: Record<string, string>;
|
|
85
|
+
editPath: string;
|
|
86
|
+
};
|
|
87
|
+
attachments?: {
|
|
88
|
+
id: string;
|
|
89
|
+
kind: "image" | "pdf";
|
|
90
|
+
url: string;
|
|
91
|
+
mimeType: string;
|
|
92
|
+
name?: string | undefined;
|
|
93
|
+
bytes?: number | undefined;
|
|
94
|
+
}[] | undefined;
|
|
95
|
+
route: string;
|
|
96
|
+
pageRoutes: string[];
|
|
97
|
+
blockCount: number;
|
|
98
|
+
};
|
|
99
|
+
export declare function minimalPlannerContextPack(args: {
|
|
100
|
+
contextPack: ReturnType<typeof plannerContextPack>;
|
|
101
|
+
}): {
|
|
102
|
+
selected: {
|
|
103
|
+
blockId: string | null;
|
|
104
|
+
blockType: string | null;
|
|
105
|
+
editablePath: string | null;
|
|
106
|
+
block: {
|
|
107
|
+
id: string;
|
|
108
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
109
|
+
props: Record<string, unknown>;
|
|
110
|
+
selectedEditablePath: string | null;
|
|
111
|
+
selectedEditableValue: {} | null;
|
|
112
|
+
} | null;
|
|
113
|
+
imageUrlForVision: string | null;
|
|
114
|
+
};
|
|
115
|
+
neighbors: {
|
|
116
|
+
previous: {
|
|
117
|
+
id: string;
|
|
118
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
119
|
+
} | null;
|
|
120
|
+
next: {
|
|
121
|
+
id: string;
|
|
122
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
123
|
+
} | null;
|
|
124
|
+
};
|
|
125
|
+
pageOutline: {
|
|
126
|
+
id: string;
|
|
127
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
128
|
+
props: Record<string, unknown>;
|
|
129
|
+
arrayProps: Record<string, {
|
|
130
|
+
length: number;
|
|
131
|
+
labels?: string[];
|
|
132
|
+
}>;
|
|
133
|
+
}[];
|
|
134
|
+
pageMeta: import("@avocadostudio-ai/shared").PageMeta | null;
|
|
135
|
+
pageIntent: string;
|
|
136
|
+
recentSuccessfulEdits: {
|
|
137
|
+
at: string;
|
|
138
|
+
summary: string;
|
|
139
|
+
ops: ("update_props" | "create_page" | "add_block" | "remove_block" | "move_block" | "duplicate_block" | "add_item" | "update_item" | "remove_item" | "move_item" | "reorder_items" | "reorder_blocks" | "rename_page" | "remove_page" | "move_page" | "duplicate_page" | "update_page_meta" | "update_site_config" | "update_theme")[];
|
|
140
|
+
}[];
|
|
141
|
+
resolvedReferences: {
|
|
142
|
+
target: {
|
|
143
|
+
id: string;
|
|
144
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
145
|
+
} | null;
|
|
146
|
+
anchor: {
|
|
147
|
+
id: string;
|
|
148
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
149
|
+
} | null;
|
|
150
|
+
mentionedBlocks: {
|
|
151
|
+
id: string;
|
|
152
|
+
type: import("@avocadostudio-ai/shared").BlockType;
|
|
153
|
+
reason: string;
|
|
154
|
+
}[];
|
|
155
|
+
};
|
|
156
|
+
siteConfig: {
|
|
157
|
+
name: string | null;
|
|
158
|
+
logo: string | null;
|
|
159
|
+
navLabels: Record<string, string>;
|
|
160
|
+
navGroups: Record<string, string[]>;
|
|
161
|
+
defaultNavLabels: Record<string, string>;
|
|
162
|
+
editPath: string;
|
|
163
|
+
};
|
|
164
|
+
attachments?: {
|
|
165
|
+
id: string;
|
|
166
|
+
kind: "image" | "pdf";
|
|
167
|
+
url: string;
|
|
168
|
+
mimeType: string;
|
|
169
|
+
name?: string | undefined;
|
|
170
|
+
bytes?: number | undefined;
|
|
171
|
+
}[] | undefined;
|
|
172
|
+
route: string;
|
|
173
|
+
pageRoutes: string[];
|
|
174
|
+
blockCount: number;
|
|
175
|
+
};
|
|
176
|
+
export declare function shouldUseMinimalPlannerContext(args: {
|
|
177
|
+
message: string;
|
|
178
|
+
translationScope: TranslationScope;
|
|
179
|
+
activeBlockId?: string;
|
|
180
|
+
activeEditablePath?: string;
|
|
181
|
+
}): boolean;
|
|
182
|
+
export declare function shouldPreferFocusedTranslation(args: {
|
|
183
|
+
message: string;
|
|
184
|
+
inferredScope: TranslationScope;
|
|
185
|
+
activeBlockId?: string;
|
|
186
|
+
}): boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Uses the deterministic planner's intent analysis to classify message
|
|
189
|
+
* complexity, providing more accurate context packing decisions than
|
|
190
|
+
* the pure-regex heuristics in shouldPreferFastModelForMessage.
|
|
191
|
+
*
|
|
192
|
+
* Returns "simple" when the intent is fully resolvable from the message
|
|
193
|
+
* (add/remove/move with clear target, or update with extracted patch).
|
|
194
|
+
* Returns "standard" otherwise.
|
|
195
|
+
*/
|
|
196
|
+
export declare function classifyMessageComplexity(args: {
|
|
197
|
+
message: string;
|
|
198
|
+
currentPage: PageDoc;
|
|
199
|
+
activeBlockId?: string;
|
|
200
|
+
activeEditablePath?: string;
|
|
201
|
+
translationScope: TranslationScope;
|
|
202
|
+
}): "simple" | "standard";
|
|
203
|
+
/**
|
|
204
|
+
* Checks whether a router plan is too shallow for the message's intent.
|
|
205
|
+
*
|
|
206
|
+
* Returns true when the message signals content generation need (e.g.,
|
|
207
|
+
* "add a compelling hero about our mission") but the router plan only
|
|
208
|
+
* has default/placeholder props. In this case the full planner should
|
|
209
|
+
* be preferred.
|
|
210
|
+
*/
|
|
211
|
+
export declare function isRouterPlanTooShallow(message: string, plan: EditPlan): boolean;
|