@blinkk/root-cms 3.1.2 → 3.1.4

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 (136) hide show
  1. package/dist/app.js +1 -4
  2. package/dist/chunk-MYN76V3L.js +764 -0
  3. package/dist/{chunk-4VZTYUQ7.js → chunk-NC3CRIFA.js} +191 -13
  4. package/dist/core/ai-tools.d.ts +82 -42
  5. package/dist/core/ai.d.ts +54 -218
  6. package/dist/core/plugin.d.ts +10 -4
  7. package/dist/functions.js +1 -2
  8. package/dist/plugin.js +42 -208
  9. package/dist/shared/ai/models.d.ts +110 -0
  10. package/dist/shared/ai/prompt-utils.d.ts +59 -0
  11. package/dist/ui/AIPage-4W4MFELY.js +1 -0
  12. package/dist/ui/{AssetsPage-KLGNE6D6.js → AssetsPage-QUL2NBWK.js} +1 -1
  13. package/dist/ui/CollectionPage-7VU6SUUP.js +1 -0
  14. package/dist/ui/ComparePage-DCP5QHDE.js +1 -0
  15. package/dist/ui/DataPage-TUVFBALT.js +1 -0
  16. package/dist/ui/DataSourcePage-YE2ZOHYI.js +1 -0
  17. package/dist/ui/DocTranslationsPage-3ILTQOP2.js +1 -0
  18. package/dist/ui/DocumentPage-JFQ3Y2GH.js +1 -0
  19. package/dist/ui/EditDataSourcePage-WHPZDMD4.js +1 -0
  20. package/dist/ui/EditReleasePage-QKWHIURV.js +1 -0
  21. package/dist/ui/EmbeddedAIPage-SQLOV4IT.js +1 -0
  22. package/dist/ui/{EmbeddedDocumentPage-4TLMCDSO.js → EmbeddedDocumentPage-6FYHUHTE.js} +1 -1
  23. package/dist/ui/{ImageEditorDialog-GGS4VFEA.js → ImageEditorDialog-Q44YTLB5.js} +1 -1
  24. package/dist/ui/LogsPage-RC5O722W.js +1 -0
  25. package/dist/ui/NewDataSourcePage-QXW54CHQ.js +1 -0
  26. package/dist/ui/NewReleasePage-TKYWVTOH.js +1 -0
  27. package/dist/ui/{NotFoundPage-A6OCYREA.js → NotFoundPage-RMNPVSD3.js} +1 -1
  28. package/dist/ui/{ProjectPage-UHRXNN7O.js → ProjectPage-VY4EJX6J.js} +1 -1
  29. package/dist/ui/ReleasePage-VZAL4KUI.js +1 -0
  30. package/dist/ui/ReleasesPage-A4BLUNJE.js +1 -0
  31. package/dist/ui/SettingsPage-MP3YHQDP.js +1 -0
  32. package/dist/ui/{SidebarToolsPage-VXVRZ2C3.js → SidebarToolsPage-S5DMIEQ6.js} +1 -1
  33. package/dist/ui/{TaskPage-LDPRN3G6.js → TaskPage-LCBDMA4I.js} +1 -1
  34. package/dist/ui/TasksPage-72KVSUQY.js +1 -0
  35. package/dist/ui/TranslationsArbPage-B7TIRREV.js +2 -0
  36. package/dist/ui/TranslationsEditPage-VQG4YHA6.js +1 -0
  37. package/dist/ui/{TranslationsPage-674JU6L4.js → TranslationsPage-6VCVMNXI.js} +1 -1
  38. package/dist/ui/ai-tools-7S4URTMT.js +1 -0
  39. package/dist/ui/chunk-2PDMFZBF.js +1 -0
  40. package/dist/ui/{chunk-SKQECSUR.js → chunk-3WQCVKK4.js} +1 -1
  41. package/dist/ui/{chunk-WMICEDOT.js → chunk-4N6CPIOQ.js} +1 -1
  42. package/dist/ui/{chunk-NJMNOITA.js → chunk-5SY2SVEX.js} +1 -1
  43. package/dist/ui/chunk-62VPTZJL.js +1 -0
  44. package/dist/ui/{chunk-GT5EQSEX.js → chunk-6P75AZ3B.js} +1 -1
  45. package/dist/ui/{chunk-CAY5NM4Z.js → chunk-74OB6FM4.js} +1 -1
  46. package/dist/ui/{chunk-XWX2622O.js → chunk-76PG55CM.js} +1 -1
  47. package/dist/ui/chunk-AAR7DBEN.js +110 -0
  48. package/dist/ui/{chunk-JS6TK4OP.js → chunk-ATTW36EM.js} +1 -1
  49. package/dist/ui/chunk-AZ2SK2ZK.js +1 -0
  50. package/dist/ui/chunk-CSUKZZK5.js +1 -0
  51. package/dist/ui/chunk-DZYYWCHX.js +3 -0
  52. package/dist/ui/{chunk-NSOTG64N.js → chunk-EIMRPYCD.js} +1 -1
  53. package/dist/ui/{chunk-HARRKJFZ.js → chunk-ELKXEMOU.js} +1 -1
  54. package/dist/ui/{chunk-LROR6N3B.js → chunk-ETPZ3TXX.js} +2 -2
  55. package/dist/ui/{chunk-LROR6N3B.js.LEGAL.txt → chunk-ETPZ3TXX.js.LEGAL.txt} +1 -0
  56. package/dist/ui/{chunk-J5E457OI.js → chunk-EYVQCOAD.js} +1 -1
  57. package/dist/ui/{chunk-Q7IU4QCY.js → chunk-F4F5MH7A.js} +1 -1
  58. package/dist/ui/chunk-FF4CLAZS.js +1 -0
  59. package/dist/ui/{chunk-W6LP22IK.js → chunk-GCMX5V3P.js} +1 -1
  60. package/dist/ui/{chunk-FJLNXXE3.js → chunk-H3PYPGNR.js} +1 -1
  61. package/dist/ui/{chunk-WFQU7V3W.js → chunk-HVYNDEA7.js} +1 -1
  62. package/dist/ui/{chunk-TVYAZN2Q.js → chunk-IMXZTYAA.js} +1 -1
  63. package/dist/ui/{chunk-YXSXWFLZ.js → chunk-J5KDO4RM.js} +1 -1
  64. package/dist/ui/chunk-JSWRHHTZ.js +1 -0
  65. package/dist/ui/{chunk-MSYTAJBV.js → chunk-K3UM5OQF.js} +1 -1
  66. package/dist/ui/{chunk-BW2MKGNP.js → chunk-KBJ4AK5O.js} +1 -1
  67. package/dist/ui/{chunk-4FGASIIX.js → chunk-L3O3CLWO.js} +1 -1
  68. package/dist/ui/{chunk-JTIQBW33.js → chunk-LC6WLVRS.js} +1 -1
  69. package/dist/ui/{chunk-VJUMFJIX.js → chunk-MAZJRNAL.js} +1 -1
  70. package/dist/ui/{chunk-3E6CASIN.js → chunk-MYKJ6VAM.js} +1 -1
  71. package/dist/ui/chunk-NEA3XU6N.js +19 -0
  72. package/dist/ui/{chunk-UUVAD4U4.js → chunk-PR7NB5VK.js} +1 -1
  73. package/dist/ui/{chunk-QF3GPZTI.js → chunk-QHOZXW7Y.js} +1 -1
  74. package/dist/ui/chunk-RN4XVVWD.js +1 -0
  75. package/dist/ui/{chunk-PTPYMVFW.js → chunk-SLLTYDVX.js} +1 -1
  76. package/dist/ui/chunk-SWTNOWNE.js +1 -0
  77. package/dist/ui/{chunk-JBHRZ4EX.js → chunk-SYDHFYRC.js} +1 -1
  78. package/dist/ui/{chunk-ZMS6NIGG.js → chunk-UHK5N75L.js} +1 -1
  79. package/dist/ui/chunk-V3G3554T.js +31 -0
  80. package/dist/ui/chunk-VB5VMEN7.js +1 -0
  81. package/dist/ui/{chunk-2EPQJUGM.js → chunk-VRRRFAIV.js} +1 -1
  82. package/dist/ui/{chunk-4ALFT4ES.js → chunk-W2ZAVRAT.js} +56 -56
  83. package/dist/ui/{chunk-P2Z66DHV.js → chunk-W3ICOHMF.js} +1 -1
  84. package/dist/ui/{chunk-ENQKSLGJ.js → chunk-WAT22UHZ.js} +1 -1
  85. package/dist/ui/chunk-WN7HUDGY.js +1 -0
  86. package/dist/ui/{chunk-BUNR5EU3.js → chunk-WS37MDM3.js} +1 -1
  87. package/dist/ui/chunk-XACLTYBB.js +5 -0
  88. package/dist/ui/chunk-XJUQOX4E.js +1 -0
  89. package/dist/ui/{chunk-QHJOTXYB.js → chunk-XM35ZJBQ.js} +1 -1
  90. package/dist/ui/{chunk-VD6G5NV4.js → chunk-XOWWPE7X.js} +12 -12
  91. package/dist/ui/chunk-YV5SGR24.js +1 -0
  92. package/dist/ui/ui.css +1 -1
  93. package/dist/ui/ui.js +1 -1
  94. package/package.json +3 -3
  95. package/dist/chunk-AJDMNTZQ.js +0 -187
  96. package/dist/chunk-X5CNR4CK.js +0 -1411
  97. package/dist/ui/AIPage-2N5UYBRX.js +0 -1
  98. package/dist/ui/CollectionPage-D7IG6526.js +0 -1
  99. package/dist/ui/ComparePage-726NIJW5.js +0 -1
  100. package/dist/ui/DataPage-YMSQCIVK.js +0 -1
  101. package/dist/ui/DataSourcePage-7VPFUG3K.js +0 -1
  102. package/dist/ui/DocTranslationsPage-FO7O76VK.js +0 -1
  103. package/dist/ui/DocumentPage-M7SJ4ITQ.js +0 -1
  104. package/dist/ui/EditDataSourcePage-Y7YK6IWT.js +0 -1
  105. package/dist/ui/EditReleasePage-XZ4RHHRT.js +0 -1
  106. package/dist/ui/EmbeddedAIPage-RJOUIPF2.js +0 -1
  107. package/dist/ui/LogsPage-IGDDZGEI.js +0 -1
  108. package/dist/ui/NewDataSourcePage-YUPRHL7F.js +0 -1
  109. package/dist/ui/NewReleasePage-6HHLYOQP.js +0 -1
  110. package/dist/ui/ReleasePage-V2JMPWX7.js +0 -1
  111. package/dist/ui/ReleasesPage-SCR4SVKN.js +0 -1
  112. package/dist/ui/SettingsPage-5XKHPG6K.js +0 -1
  113. package/dist/ui/TasksPage-DIXUWYXM.js +0 -1
  114. package/dist/ui/TranslationsArbPage-QPD3ZBT4.js +0 -2
  115. package/dist/ui/TranslationsEditPage-475Z7UPC.js +0 -1
  116. package/dist/ui/ai-tools-4DWMFQS6.js +0 -1
  117. package/dist/ui/chunk-2RAVKX2Z.js +0 -19
  118. package/dist/ui/chunk-2S5HMST6.js +0 -1
  119. package/dist/ui/chunk-344GZOGG.js +0 -84
  120. package/dist/ui/chunk-4ISFTE2G.js +0 -5
  121. package/dist/ui/chunk-AGFPVTBA.js +0 -1
  122. package/dist/ui/chunk-D3UIZHRK.js +0 -1
  123. package/dist/ui/chunk-HO3W4RVG.js +0 -1
  124. package/dist/ui/chunk-KRNQ6L35.js +0 -1
  125. package/dist/ui/chunk-N3JSLTMV.js +0 -1
  126. package/dist/ui/chunk-NWHGT476.js +0 -1
  127. package/dist/ui/chunk-RHAJNURN.js +0 -1
  128. package/dist/ui/chunk-SENZVSVZ.js +0 -1
  129. package/dist/ui/chunk-TOGLZDK3.js +0 -1
  130. package/dist/ui/chunk-TPJCPCRR.js +0 -1
  131. package/dist/ui/chunk-XMESZUC5.js +0 -1
  132. package/dist/ui/chunk-YMBBWVBB.js +0 -3
  133. /package/dist/ui/{ImageEditorDialog-GGS4VFEA.js.LEGAL.txt → ImageEditorDialog-Q44YTLB5.js.LEGAL.txt} +0 -0
  134. /package/dist/ui/{chunk-NJMNOITA.js.LEGAL.txt → chunk-5SY2SVEX.js.LEGAL.txt} +0 -0
  135. /package/dist/ui/{chunk-4ALFT4ES.js.LEGAL.txt → chunk-W2ZAVRAT.js.LEGAL.txt} +0 -0
  136. /package/dist/ui/{chunk-VD6G5NV4.js.LEGAL.txt → chunk-XOWWPE7X.js.LEGAL.txt} +0 -0
package/dist/core/ai.d.ts CHANGED
@@ -1,98 +1,26 @@
1
1
  import { RootConfig } from '@blinkk/root';
2
- import { ImageModel, LanguageModel, UIMessage } from 'ai';
3
- import { Timestamp } from 'firebase-admin/firestore';
4
- import { RootCMSClient } from './client.js';
5
- import { Collection } from './schema.js';
2
+ import { AiConfig, AiExecutionMode, AiModelConfig, resolveLanguageModel } from '../shared/ai/models.js';
3
+ import { deriveChatTitle } from '../shared/ai/prompt-utils.js';
4
+ export type { AiConfig, AiExecutionMode, AiModelCapabilities, AiModelConfig, AiProvider, } from '../shared/ai/models.js';
5
+ export { normalizeExecutionMode, resolveImageModel, resolveLanguageModel, withBrowserHeaders, } from '../shared/ai/models.js';
6
+ export { buildTitlePrompt, buildTitlePromptContext, deriveChatTitle, extractJsonFromResponse, mergeIncomingMessage, sanitizeDanglingToolCalls, sanitizeGeneratedTitle, stripUndefined, TITLE_GENERATION_SYSTEM_PROMPT, } from '../shared/ai/prompt-utils.js';
6
7
  /** Filename of the project-level instructions file loaded into the AI prompt. */
7
8
  export declare const ROOT_MD_FILENAME = "ROOT.md";
8
- export type AiExecutionMode = 'read' | 'approve' | 'auto';
9
+ /** Default base system prompt for the Root AI chat. */
10
+ export declare const DEFAULT_CHAT_SYSTEM_PROMPT: string;
9
11
  /**
10
- * Provider type for an AI model. Use `openai-compatible` for any OpenAI-style
11
- * endpoint (e.g. local Ollama, vLLM, OpenRouter).
12
+ * Strips secrets from `AiConfig` before sending to the browser. Used by the
13
+ * `/cms/api/ai.config` endpoint and the inlined `window.__ROOT_CTX.ai` to
14
+ * populate the model picker. The key-bearing connection config is sent
15
+ * separately (and only for the selected model) via `serializeAiClientModel`.
12
16
  */
13
- export type AiProvider = 'openai' | 'openai-compatible' | 'anthropic' | 'google';
14
- /**
15
- * Configuration for a single chat model.
16
- *
17
- * Inspired by Ollama's `Modelfile` and LiteLLM's model config: each entry maps
18
- * a CMS-facing id to a provider, model id and credentials.
19
- */
20
- export interface AiModelConfig {
21
- /** Stable id used by the CMS UI and stored alongside chat history. */
22
- id: string;
23
- /** Optional human-readable label rendered in the model picker. */
24
- label?: string;
25
- /** Optional description shown under the label. */
26
- description?: string;
27
- /** AI provider/family to route requests to. */
28
- provider: AiProvider;
29
- /**
30
- * Provider-specific model id (e.g. `gpt-4o`, `claude-opus-4-5`,
31
- * `gemini-2.5-pro`). Defaults to `id` if omitted.
32
- */
33
- modelId?: string;
34
- /** API key for the provider. */
35
- apiKey?: string;
36
- /** Override the provider's base URL (required for `openai-compatible`). */
37
- baseURL?: string;
38
- /** Custom headers to send with each request. */
39
- headers?: Record<string, string>;
40
- /** Capabilities advertised to the UI. */
41
- capabilities?: {
42
- /** Whether the model can call tools. Defaults to `true`. */
43
- tools?: boolean;
44
- /** Whether the model can stream reasoning/thinking. Defaults to `false`. */
45
- reasoning?: boolean;
46
- /** Whether the model accepts image attachments. Defaults to `false`. */
47
- attachments?: boolean;
48
- };
49
- }
50
- /**
51
- * Full AI config registered on the cms plugin.
52
- */
53
- export interface AiConfig {
54
- /** Models exposed in the model picker. The first entry is the default. */
55
- models: AiModelConfig[];
56
- /** Id of the default model. Defaults to the first model in `models`. */
57
- defaultModel?: string;
58
- /**
59
- * Image generation models. Used by the image generator and any other
60
- * features that produce images. Only the `openai` and `google` providers
61
- * support image generation.
62
- */
63
- imageModels?: AiModelConfig[];
64
- /** Id of the default image model. Defaults to the first entry in `imageModels`. */
65
- defaultImageModel?: string;
66
- /**
67
- * Optional system prompt prepended to every conversation. If a `ROOT.md`
68
- * file exists at the project root, its contents are appended to this
69
- * prompt automatically.
70
- */
71
- systemPrompt?: string;
72
- /** Maximum tool-loop steps before stopping. Defaults to 10. */
73
- maxSteps?: number;
74
- }
75
- interface ChatRecord {
76
- id: string;
77
- createdBy: string;
78
- createdAt: Timestamp;
79
- modifiedAt: Timestamp;
80
- modelId?: string;
81
- title?: string;
82
- messages: UIMessage[];
83
- }
84
- /** Resolves an `AiModelConfig` to an AI SDK `LanguageModel` instance. */
85
- export declare function resolveLanguageModel(model: AiModelConfig): LanguageModel;
86
- /** Resolves an `AiModelConfig` to an AI SDK `ImageModel` instance. */
87
- export declare function resolveImageModel(model: AiModelConfig): ImageModel;
88
- /** Strips secrets from `AiConfig` before sending to the browser. */
89
17
  export declare function serializeAiConfig(config: AiConfig): {
90
18
  defaultModel: string;
91
19
  models: {
92
20
  id: string;
93
21
  label: string;
94
22
  description: string | undefined;
95
- provider: AiProvider;
23
+ provider: import("../shared/ai/models.js").AiProvider;
96
24
  capabilities: {
97
25
  tools: boolean;
98
26
  reasoning: boolean;
@@ -101,61 +29,37 @@ export declare function serializeAiConfig(config: AiConfig): {
101
29
  }[];
102
30
  imageGenerationEnabled: boolean;
103
31
  };
32
+ /**
33
+ * Serializes a single chat model's full connection config — INCLUDING the API
34
+ * key, base URL and custom headers — for direct browser-to-provider calls.
35
+ *
36
+ * Security note: this intentionally exposes the provider API key to the
37
+ * authenticated CMS client (the project opted into direct client-side calls).
38
+ * Only return it from authenticated endpoints, and only for the model the user
39
+ * actually selected.
40
+ */
41
+ export declare function serializeAiClientModel(model: AiModelConfig): {
42
+ id: string;
43
+ label: string;
44
+ description: string | undefined;
45
+ provider: import("../shared/ai/models.js").AiProvider;
46
+ modelId: string;
47
+ apiKey: string | undefined;
48
+ baseURL: string | undefined;
49
+ headers: Record<string, string> | undefined;
50
+ capabilities: {
51
+ tools: boolean;
52
+ reasoning: boolean;
53
+ attachments: boolean;
54
+ };
55
+ };
56
+ export type SerializedAiClientModel = ReturnType<typeof serializeAiClientModel>;
104
57
  /** Returns the AI config registered on the CMS plugin, or `null`. */
105
58
  export declare function getAiConfig(rootConfig: RootConfig): AiConfig | null;
106
59
  /** Returns the model config matching `modelId`, or the default model. */
107
60
  export declare function findModel(config: AiConfig, modelId?: string): AiModelConfig | null;
108
61
  /** Returns the image model config matching `modelId`, or the default image model. */
109
62
  export declare function findImageModel(config: AiConfig, modelId?: string): AiModelConfig | null;
110
- export declare function normalizeExecutionMode(value: unknown): AiExecutionMode;
111
- /**
112
- * Persists chat sessions to Firestore so that users can resume past
113
- * conversations from the chat history sidebar.
114
- */
115
- export declare class ChatStore {
116
- cmsClient: RootCMSClient;
117
- user: string;
118
- constructor(cmsClient: RootCMSClient, user: string);
119
- collection(): FirebaseFirestore.CollectionReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
120
- createChat(options?: {
121
- id?: string;
122
- modelId?: string;
123
- }): Promise<ChatRecord>;
124
- getChat(id: string): Promise<ChatRecord | null>;
125
- listChats(options?: {
126
- limit?: number;
127
- }): Promise<ChatRecord[]>;
128
- deleteChat(id: string): Promise<void>;
129
- updateMessages(id: string, messages: UIMessage[], options?: {
130
- modelId?: string;
131
- title?: string;
132
- }): Promise<void>;
133
- }
134
- /**
135
- * Merges the latest incoming message into the persisted chat history.
136
- *
137
- * The client posts only the last message; the server holds the rest in
138
- * Firestore. After client-side tool execution the SDK resubmits the *same*
139
- * assistant message — now carrying tool results — under the id it was
140
- * streamed with. That id already exists in the persisted history (saved by
141
- * `onFinish` before the results came back), so a naive append would
142
- * duplicate it: the stored copy still has unresolved tool calls, which
143
- * convert to `tool_use` blocks with no following `tool_result` and the model
144
- * provider rejects the request.
145
- *
146
- * Replacing the matched entry (and dropping anything after it) keeps message
147
- * ids unique and the tool-call/result pairing intact. A genuinely new
148
- * message id is appended as usual.
149
- */
150
- export declare function mergeIncomingMessage(stored: UIMessage[], incoming: UIMessage): UIMessage[];
151
- /**
152
- * Synthesizes an aborted result for any tool-call part still awaiting one, so
153
- * every `tool_use` keeps a matching `tool_result` when the history is handed
154
- * to the model. Left unresolved, such parts (from an abandoned turn) make the
155
- * provider reject the next request. Returns the original array unchanged when
156
- * there is nothing to repair.
157
- */
158
- export declare function sanitizeDanglingToolCalls(messages: UIMessage[]): UIMessage[];
159
63
  /**
160
64
  * Reads the project's `ROOT.md` file, if present. Mirrors the convention used
161
65
  * by tools like `AGENTS.md` and `CLAUDE.md`: developers can drop a markdown
@@ -173,106 +77,44 @@ export declare function readRootMd(rootDir: string): Promise<string | null>;
173
77
  * distinguish it from the framework-provided instructions.
174
78
  */
175
79
  export declare function buildSystemPrompt(basePrompt: string, rootMd: string | null): string;
176
- /** Derives a short title from the first user message (used as fallback). */
177
- export declare function deriveChatTitle(messages: UIMessage[]): string;
178
- /**
179
- * Extracts the first user turn and first assistant text turn as a plain-text
180
- * transcript, suitable for the title-generation prompt. Skips tool-call
181
- * parts, reasoning chunks, and attachments so the model sees the
182
- * conversational substance and nothing else.
183
- *
184
- * Only the opening exchange is included on purpose — the chat title should
185
- * describe what the conversation is *about*, anchored on the user's initial
186
- * ask, not drift as later follow-ups arrive.
187
- */
188
- export declare function buildTitlePromptContext(messages: UIMessage[]): string;
189
- /**
190
- * Strips the cruft LLMs commonly emit around a title — leading "Title:"
191
- * preamble, surrounding quotes, trailing punctuation, internal newlines —
192
- * and enforces a hard length cap.
193
- */
194
- export declare function sanitizeGeneratedTitle(raw: string): string;
195
80
  /**
196
- * Uses the AI model to generate a short summary title for the chat. The
197
- * prompt is anchored on the user's initial question (and, if available, the
198
- * assistant's first reply) so the title captures *intent and topic* rather
199
- * than echoing the first line verbatim.
81
+ * Uses the AI model to generate a short summary title for a chat. Kept for
82
+ * server-side callers; the browser chat generates titles client-side using the
83
+ * shared `TITLE_GENERATION_SYSTEM_PROMPT` + `buildTitlePrompt` helpers.
200
84
  *
201
85
  * Falls back to `deriveChatTitle` if the generation fails or returns an
202
86
  * empty result.
203
87
  */
204
- export declare function generateChatTitle(model: LanguageModel, messages: UIMessage[]): Promise<string>;
205
- export interface RunChatStreamOptions {
88
+ export declare function generateChatTitle(model: ReturnType<typeof resolveLanguageModel>, messages: Parameters<typeof deriveChatTitle>[0]): Promise<string>;
89
+ export declare function buildExecutionModePrompt(mode: AiExecutionMode): string;
90
+ export interface BuildChatSystemPromptOptions {
206
91
  rootConfig: RootConfig;
207
- cmsClient: RootCMSClient;
208
92
  config: AiConfig;
209
- model: AiModelConfig;
210
- messages: UIMessage[];
211
- chatId: string;
212
- user: string;
213
- executionMode?: AiExecutionMode;
214
- /**
215
- * Title already saved on this chat. When non-empty the server will NOT
216
- * regenerate it on this turn — titles are derived once, on the first
217
- * assistant response, so they describe the original ask instead of
218
- * thrashing as follow-up messages arrive.
219
- */
220
- existingTitle?: string;
93
+ executionMode: AiExecutionMode;
221
94
  /**
222
95
  * When set, tells the model which document the user is currently viewing
223
96
  * in the CMS UI so phrases like "this document" can be resolved.
224
97
  */
225
98
  activeDocId?: string;
226
- /**
227
- * Loads a single collection's schema. The caller owns dev-vs-prod schema
228
- * resolution (Vite SSR vs. prebuilt `dist/collections/*.json`).
229
- */
230
- loadCollection: (collectionId: string) => Promise<Collection | null>;
231
- /** Loads every project collection keyed by id. */
232
- loadAllCollections: () => Promise<Record<string, Collection>>;
233
99
  }
234
- export declare function buildExecutionModePrompt(mode: AiExecutionMode): string;
235
100
  /**
236
- * Builds a streaming response that proxies the model's UI message stream
237
- * directly to the client. Persists the final message list to Firestore once
238
- * the stream finishes.
101
+ * Assembles the chat system prompt: base prompt + workspace context + tool
102
+ * safety policy + execution-mode workflow + (optional) active-doc context,
103
+ * with `ROOT.md` appended when present.
239
104
  */
240
- export declare function runChatStream(options: RunChatStreamOptions): Promise<Response>;
241
- export interface RunEditObjectStreamOptions {
105
+ export declare function buildChatSystemPrompt(options: BuildChatSystemPromptOptions): Promise<string>;
106
+ export interface BuildEditSystemPromptOptions {
242
107
  rootConfig: RootConfig;
243
- cmsClient: RootCMSClient;
244
- user: string;
245
- config: AiConfig;
246
- model: AiModelConfig;
247
- messages: UIMessage[];
248
108
  /** The JSON object the user is editing. Injected as context for the model. */
249
109
  editData: unknown;
250
- loadCollection: (collectionId: string) => Promise<Collection | null>;
251
- loadAllCollections: () => Promise<Record<string, Collection>>;
252
110
  }
253
111
  /**
254
- * Streaming variant used by the array-item "Edit with AI" diff-viewer flow.
255
- *
256
- * Differences from `runChatStream`:
257
- *
258
- * - Uses an edit-specific system prompt that injects the JSON the user is
259
- * editing and the project's `root-cms.d.ts` types, and instructs the model
260
- * to end every response with a fenced ```json block containing the
261
- * complete proposed new JSON. The client extracts that block to populate
262
- * the diff viewer.
263
- * - Tools are filtered to read-only (see `createReadOnlyCmsTools`). The user
264
- * approves changes via the modal's "Save" button, so the model must not
265
- * mutate Firestore directly.
266
- * - No Firestore persistence — edit sessions are ephemeral and don't show up
267
- * in the user's chat history.
268
- */
269
- export declare function runEditObjectStream(options: RunEditObjectStreamOptions): Promise<Response>;
270
- /**
271
- * Recursively removes `undefined` values from an object/array. Returns a new
272
- * structure; the input is not mutated. Used to clean payloads before sending
273
- * them to Firestore, which rejects `undefined` outright.
112
+ * Assembles the system prompt for the array-item "Edit with AI" diff-viewer
113
+ * flow: the edit instructions + output-format contract + read-only tool policy
114
+ * + the project's `root-cms.d.ts` types + the JSON being edited (wrapped as
115
+ * untrusted data), with `ROOT.md` appended when present.
274
116
  */
275
- export declare function stripUndefined<T>(value: T): T;
117
+ export declare function buildEditSystemPrompt(options: BuildEditSystemPromptOptions): Promise<string>;
276
118
  export interface SummarizeDiffOptions {
277
119
  before: Record<string, any> | null;
278
120
  after: Record<string, any> | null;
@@ -322,9 +164,3 @@ export interface GenerateImageResult {
322
164
  * to storage.
323
165
  */
324
166
  export declare function generateImage(rootConfig: RootConfig, options: GenerateImageOptions): Promise<GenerateImageResult>;
325
- /**
326
- * Extracts JSON from an AI response that may contain markdown code blocks.
327
- * @internal
328
- */
329
- export declare function extractJsonFromResponse(responseText: string): string;
330
- export {};
@@ -182,11 +182,17 @@ export type CMSPluginOptions = {
182
182
  onAction?: (action: Action) => any;
183
183
  /**
184
184
  * Configures the Root CMS AI chat (`/cms/ai`). Provide one or more models
185
- * — the CMS proxies user requests directly to the configured providers.
185
+ * — each entry declares the provider, model id and credentials. The shape
186
+ * mirrors open source tools like Ollama and LiteLLM.
186
187
  *
187
- * The shape mirrors open source tools like Ollama and LiteLLM: each entry
188
- * declares the provider, model id and credentials. Keys live on the server,
189
- * never on the client.
188
+ * IMPORTANT: the streaming chat (`/cms/ai`, the document-editor AI panel and
189
+ * the "Edit with AI" modal) calls the provider DIRECTLY from the browser —
190
+ * SSE proxying through the server did not work on App Engine / Firebase
191
+ * Hosting. The selected model's API key is therefore delivered to the
192
+ * authenticated CMS client. Use keys scoped/billed for this trusted,
193
+ * signed-in audience. The one-shot helpers (diff summaries, publish
194
+ * messages, translations, alt text, image generation) still run server-side,
195
+ * so their keys stay on the server.
190
196
  *
191
197
  * Example:
192
198
  * ```ts
package/dist/functions.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  runCronJobs
3
- } from "./chunk-AJDMNTZQ.js";
4
- import "./chunk-4VZTYUQ7.js";
3
+ } from "./chunk-NC3CRIFA.js";
5
4
  import "./chunk-AFBSITEO.js";
6
5
  import "./chunk-J72JK4KR.js";
7
6
  import "./chunk-MLKGABMK.js";