@blinkk/root-cms 3.0.1-beta.2 → 3.0.1-beta.3

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/dist/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getServerVersion,
3
3
  serializeAiConfig
4
- } from "./chunk-ATPWU3CU.js";
4
+ } from "./chunk-NXEXOHBD.js";
5
5
  import "./chunk-CRK7N6RR.js";
6
6
  import {
7
7
  getCollectionSchema,
@@ -99,10 +99,12 @@ function createCmsTools() {
99
99
  })
100
100
  }),
101
101
  doc_updateField: tool({
102
- description: 'Update a single field on a draft CMS document by JSON path. Use dotted paths (e.g. "hero.title") and array indices (e.g. "sections.0.heading"). The value is validated against the field schema and the call is rejected if the shape is wrong (e.g. passing a string to a richtext field). Only updates the draft version; users must publish separately. Always confirm with the user before calling.',
102
+ description: 'Update a single field on a draft CMS document by JSON path. Paths are relative to the doc fields object; do not prefix them with "fields.". Use dotted paths (e.g. "hero.title") and zero-based array indices. For example, update the first module title with path "content.modules.0.title". To append or remove array items, first read the current doc, then set the whole array path (e.g. "content.modules") to the updated array. The value is validated against the field schema and the call is rejected if the shape is wrong (e.g. passing a string to a richtext field). Only updates the draft version; users must publish separately. Always confirm with the user before calling.',
103
103
  inputSchema: z.object({
104
104
  docId: z.string(),
105
- path: z.string().describe("Dotted JSON path within the fields object."),
105
+ path: z.string().describe(
106
+ 'Dotted JSON path within the fields object, e.g. "content.modules.0.title".'
107
+ ),
106
108
  value: z.any().describe("JSON value to set at the path.")
107
109
  })
108
110
  }),
@@ -272,6 +274,12 @@ function findImageModel(config, modelId) {
272
274
  const defaultId = config.defaultImageModel || imageModels[0]?.id;
273
275
  return imageModels.find((m) => m.id === defaultId) || null;
274
276
  }
277
+ function normalizeExecutionMode(value) {
278
+ if (value === "read" || value === "suggest" || value === "approve" || value === "auto") {
279
+ return value;
280
+ }
281
+ return "approve";
282
+ }
275
283
  function requireDefaultModel(rootConfig) {
276
284
  const config = getAiConfig(rootConfig);
277
285
  if (!config) {
@@ -416,10 +424,59 @@ async function generateChatTitle(model, messages) {
416
424
  }
417
425
  return fallback;
418
426
  }
427
+ function buildExecutionModePrompt(mode) {
428
+ const common = [
429
+ "Root AI execution workflow:",
430
+ "- For content-changing tasks, first gather the relevant context with read tools.",
431
+ "- Before the first write, briefly state a plan that names the target docs, fields, intended changes, assumptions, and validation checks.",
432
+ "- Never claim a draft change was applied until the matching tool output reports success.",
433
+ "- After write tools finish, provide a short receipt with changed docs, changed fields, validation result, and a reminder that publishing remains manual."
434
+ ];
435
+ if (mode === "read") {
436
+ common.push(
437
+ "",
438
+ "Current execution mode: Read only.",
439
+ "- You only have read-only CMS tools.",
440
+ "- Do not propose tool writes or ask for approval to write. Answer from the context you can read."
441
+ );
442
+ } else if (mode === "suggest") {
443
+ common.push(
444
+ "",
445
+ "Current execution mode: Suggest changes.",
446
+ "- You only have read-only CMS tools.",
447
+ "- Do not call write tools. Provide proposed edits, field paths, and rationale for the user to review."
448
+ );
449
+ } else if (mode === "approve") {
450
+ common.push(
451
+ "",
452
+ "Current execution mode: Ask before writing.",
453
+ "- Write tools are available, but the UI will pause each draft write for user approval with a diff.",
454
+ "- Call write tools only after you have enough context to make a specific, reviewable change.",
455
+ "- If the user rejects a write, revise the plan instead of retrying the same write."
456
+ );
457
+ } else {
458
+ common.push(
459
+ "",
460
+ "Current execution mode: Auto-apply draft edits.",
461
+ "- Draft-only write tools may run without an approval pause.",
462
+ "- Keep edits narrowly scoped to the user request and summarize the exact draft changes afterward."
463
+ );
464
+ }
465
+ return common.join("\n");
466
+ }
419
467
  async function runChatStream(options) {
420
- const { rootConfig, model, config, messages, cmsClient, user, chatId } = options;
468
+ const {
469
+ rootConfig,
470
+ model,
471
+ config,
472
+ messages,
473
+ cmsClient,
474
+ user,
475
+ chatId,
476
+ executionMode = "approve"
477
+ } = options;
421
478
  const languageModel = resolveLanguageModel(model);
422
- const tools = model.capabilities?.tools === false ? {} : createCmsTools();
479
+ const tools = model.capabilities?.tools === false ? {} : executionMode === "read" || executionMode === "suggest" ? createReadOnlyCmsTools() : createCmsTools();
423
480
  const basePrompt = config.systemPrompt || [
424
481
  "You are an assistant embedded in the Root CMS admin UI.",
425
482
  "Help the user explore and edit content, answer questions about",
@@ -427,7 +484,10 @@ async function runChatStream(options) {
427
484
  "Be concise and use markdown for rich responses."
428
485
  ].join(" ");
429
486
  const rootMd = await readRootMd(rootConfig.rootDir);
430
- const systemPrompt = buildSystemPrompt(basePrompt, rootMd);
487
+ const systemPrompt = buildSystemPrompt(
488
+ [basePrompt, "", buildExecutionModePrompt(executionMode)].join("\n"),
489
+ rootMd
490
+ );
431
491
  const modelMessages = await convertToModelMessages(messages, { tools });
432
492
  const result = streamText({
433
493
  model: languageModel,
@@ -716,7 +776,7 @@ function extractJsonFromResponse(responseText) {
716
776
  // package.json
717
777
  var package_default = {
718
778
  name: "@blinkk/root-cms",
719
- version: "3.0.1-beta.2",
779
+ version: "3.0.1-beta.3",
720
780
  author: "s@blinkk.com",
721
781
  license: "MIT",
722
782
  engines: {
@@ -885,7 +945,7 @@ var package_default = {
885
945
  yjs: "13.6.27"
886
946
  },
887
947
  peerDependencies: {
888
- "@blinkk/root": "3.0.1-beta.2",
948
+ "@blinkk/root": "3.0.1-beta.3",
889
949
  "firebase-admin": ">=11",
890
950
  "firebase-functions": ">=4"
891
951
  },
@@ -909,6 +969,7 @@ export {
909
969
  serializeAiConfig,
910
970
  getAiConfig,
911
971
  findModel,
972
+ normalizeExecutionMode,
912
973
  ChatStore,
913
974
  runChatStream,
914
975
  runEditObjectStream,
package/dist/plugin.js CHANGED
@@ -6,12 +6,13 @@ import {
6
6
  generatePublishMessage,
7
7
  getAiConfig,
8
8
  getServerVersion,
9
+ normalizeExecutionMode,
9
10
  runChatStream,
10
11
  runEditObjectStream,
11
12
  serializeAiConfig,
12
13
  summarizeDiff,
13
14
  translateString
14
- } from "./chunk-ATPWU3CU.js";
15
+ } from "./chunk-NXEXOHBD.js";
15
16
  import {
16
17
  SearchIndexService,
17
18
  runCronJobs
@@ -555,6 +556,7 @@ function api(server, options) {
555
556
  res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
556
557
  return;
557
558
  }
559
+ const executionMode = normalizeExecutionMode(body.executionMode);
558
560
  const cmsClient = new RootCMSClient(req.rootConfig);
559
561
  const store = new ChatStore(cmsClient, req.user.email);
560
562
  const requestedChatId = typeof body.chatId === "string" ? body.chatId.trim() : "";
@@ -582,7 +584,8 @@ function api(server, options) {
582
584
  model,
583
585
  messages,
584
586
  chatId,
585
- user: req.user.email
587
+ user: req.user.email,
588
+ executionMode
586
589
  });
587
590
  streamResponse.headers.set("x-root-cms-chat-id", chatId);
588
591
  await pipeWebResponse(streamResponse, res);