@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,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;