@agent-native/core 0.101.7 → 0.101.10

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 (153) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +22 -0
  3. package/corpus/core/docs/content/deployment.mdx +9 -7
  4. package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
  5. package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
  6. package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
  7. package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
  8. package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
  9. package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
  10. package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
  11. package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
  12. package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
  13. package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
  16. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
  17. package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
  18. package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
  19. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
  20. package/corpus/core/src/server/auth.ts +25 -1
  21. package/corpus/core/src/server/builder-browser.ts +353 -1
  22. package/corpus/core/src/server/core-routes-plugin.ts +601 -223
  23. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
  24. package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
  25. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
  26. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
  27. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
  28. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
  29. package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
  30. package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
  31. package/corpus/templates/content/AGENTS.md +3 -2
  32. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
  33. package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
  34. package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
  35. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
  36. package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
  37. package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
  38. package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
  39. package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
  40. package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
  41. package/corpus/templates/content/actions/_database-source-utils.ts +1041 -154
  42. package/corpus/templates/content/actions/_database-utils.ts +89 -12
  43. package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
  44. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
  45. package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
  46. package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
  47. package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
  48. package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
  49. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
  50. package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
  51. package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
  52. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
  53. package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
  54. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +27 -15
  55. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
  56. package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
  57. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2353 -576
  58. package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
  59. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
  60. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
  61. package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
  62. package/corpus/templates/content/app/global.css +18 -1
  63. package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
  64. package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
  65. package/corpus/templates/content/app/i18n/zh-TW.ts +25 -0
  66. package/corpus/templates/content/app/i18n-data.ts +287 -0
  67. package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
  68. package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
  69. package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
  70. package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
  71. package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
  72. package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
  73. package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
  74. package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
  75. package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
  76. package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
  77. package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
  78. package/corpus/templates/content/parity/matrix.md +27 -25
  79. package/corpus/templates/content/parity/matrix.ts +50 -2
  80. package/corpus/templates/content/server/db/schema.ts +47 -0
  81. package/corpus/templates/content/server/plugins/auth.ts +1 -0
  82. package/corpus/templates/content/server/plugins/db.ts +53 -0
  83. package/corpus/templates/content/shared/api.ts +70 -1
  84. package/corpus/templates/content/shared/builder-mdx.ts +705 -15
  85. package/corpus/templates/content/shared/nfm.ts +3 -3
  86. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
  87. package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
  88. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
  89. package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
  90. package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
  91. package/corpus/templates/design/app/i18n/en-US.ts +1 -0
  92. package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
  93. package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
  94. package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
  95. package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
  96. package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
  97. package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
  98. package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
  99. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  100. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
  101. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
  102. package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
  103. package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
  104. package/corpus/templates/design/shared/review-anchor.ts +48 -0
  105. package/corpus/templates/plan/server/plan-mdx.ts +53 -7
  106. package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
  107. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
  108. package/dist/client/review/ReviewCommentComposer.js +3 -3
  109. package/dist/client/review/ReviewCommentComposer.js.map +1 -1
  110. package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
  111. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
  112. package/dist/client/review/ReviewThreadPanel.js +6 -4
  113. package/dist/client/review/ReviewThreadPanel.js.map +1 -1
  114. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  115. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  116. package/dist/client/settings/SettingsPanel.js +11 -4
  117. package/dist/client/settings/SettingsPanel.js.map +1 -1
  118. package/dist/client/settings/useBuilderStatus.d.ts +8 -4
  119. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  120. package/dist/client/settings/useBuilderStatus.js +19 -6
  121. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  122. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
  123. package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
  124. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
  125. package/dist/collab/awareness.d.ts +2 -2
  126. package/dist/collab/awareness.d.ts.map +1 -1
  127. package/dist/observability/routes.d.ts +5 -5
  128. package/dist/resources/handlers.d.ts +1 -1
  129. package/dist/secrets/routes.d.ts +9 -9
  130. package/dist/server/auth.d.ts +1 -0
  131. package/dist/server/auth.d.ts.map +1 -1
  132. package/dist/server/auth.js +19 -2
  133. package/dist/server/auth.js.map +1 -1
  134. package/dist/server/builder-browser.d.ts +78 -0
  135. package/dist/server/builder-browser.d.ts.map +1 -1
  136. package/dist/server/builder-browser.js +246 -1
  137. package/dist/server/builder-browser.js.map +1 -1
  138. package/dist/server/core-routes-plugin.d.ts +41 -0
  139. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  140. package/dist/server/core-routes-plugin.js +260 -14
  141. package/dist/server/core-routes-plugin.js.map +1 -1
  142. package/docs/content/deployment.mdx +9 -7
  143. package/docs/content/locales/ar-SA/deployment.mdx +9 -7
  144. package/docs/content/locales/de-DE/deployment.mdx +9 -7
  145. package/docs/content/locales/es-ES/deployment.mdx +9 -7
  146. package/docs/content/locales/fr-FR/deployment.mdx +9 -7
  147. package/docs/content/locales/hi-IN/deployment.mdx +9 -7
  148. package/docs/content/locales/ja-JP/deployment.mdx +9 -7
  149. package/docs/content/locales/ko-KR/deployment.mdx +9 -7
  150. package/docs/content/locales/pt-BR/deployment.mdx +9 -7
  151. package/docs/content/locales/zh-CN/deployment.mdx +9 -7
  152. package/docs/content/locales/zh-TW/deployment.mdx +9 -7
  153. package/package.json +2 -2
@@ -43,6 +43,11 @@ import {
43
43
  partitionAllDayEvents,
44
44
  } from "@/lib/all-day-layout";
45
45
  import { getEventDisplayColor, allOtherDeclined } from "@/lib/event-colors";
46
+ import {
47
+ getFirstVisibleOutOfOfficeDayIndex,
48
+ getOutOfOfficeSegment,
49
+ isOutOfOfficeEvent,
50
+ } from "@/lib/out-of-office";
46
51
  import {
47
52
  shouldSuppressAfterPopoverClose,
48
53
  shouldSuppressCreatePointerDown,
@@ -56,6 +61,7 @@ import {
56
61
  } from "@/lib/working-location";
57
62
 
58
63
  import { EventDetailPopover } from "./EventDetailPopover";
64
+ import { OutOfOfficeEvent } from "./OutOfOfficeEvent";
59
65
  import { shouldRenderWeekDragSegment } from "./week-drag-segment";
60
66
 
61
67
  interface WeekViewProps {
@@ -285,6 +291,7 @@ interface WeekEventCardProps {
285
291
  onDraftUpdate?: WeekViewProps["onDraftUpdate"];
286
292
  onDraftCreate?: WeekViewProps["onDraftCreate"];
287
293
  onDraftDiscard?: WeekViewProps["onDraftDiscard"];
294
+ onPopoverOpenChange: (event: CalendarEvent, open: boolean) => void;
288
295
  }
289
296
 
290
297
  /**
@@ -320,6 +327,7 @@ const WeekEventCard = memo(function WeekEventCard({
320
327
  onDraftUpdate,
321
328
  onDraftCreate,
322
329
  onDraftDiscard,
330
+ onPopoverOpenChange,
323
331
  }: WeekEventCardProps) {
324
332
  const t = useT();
325
333
  const li = layout.get(event.id) ?? {
@@ -558,6 +566,7 @@ const WeekEventCard = memo(function WeekEventCard({
558
566
  onDraftUpdate={onDraftUpdate}
559
567
  onDraftCreate={onDraftCreate}
560
568
  onDraftDiscard={onDraftDiscard}
569
+ onOpenChange={(open) => onPopoverOpenChange(event, open)}
561
570
  >
562
571
  {eventButton}
563
572
  </EventDetailPopover>
@@ -618,6 +627,7 @@ export const WeekView = memo(function WeekView({
618
627
  const GUTTER_WIDTH = isMobile ? MOBILE_GUTTER_WIDTH : DESKTOP_GUTTER_WIDTH;
619
628
  const [now, setNow] = useState(new Date());
620
629
  const [focusedEventId, setFocusedEventId] = useState<string | null>(null);
630
+ const focusedEventIdRef = useRef<string | null>(null);
621
631
  const currentTimeRef = useRef<HTMLDivElement>(null);
622
632
  const scrollContainerRef = useRef<HTMLDivElement>(null);
623
633
  const allDayContainerRef = useRef<HTMLDivElement>(null);
@@ -628,6 +638,7 @@ export const WeekView = memo(function WeekView({
628
638
  useEffect(() => {
629
639
  function handleKey(e: KeyboardEvent) {
630
640
  if (e.key === "Escape") {
641
+ focusedEventIdRef.current = null;
631
642
  setFocusedEventId(null);
632
643
  setFocusedEvent(null);
633
644
  }
@@ -675,7 +686,15 @@ export const WeekView = memo(function WeekView({
675
686
  // Separate all-day and timed events
676
687
  const allDayEvents = useMemo(() => events.filter((e) => e.allDay), [events]);
677
688
 
678
- const timedEvents = useMemo(() => events.filter((e) => !e.allDay), [events]);
689
+ const outOfOfficeEvents = useMemo(
690
+ () => events.filter((event) => !event.allDay && isOutOfOfficeEvent(event)),
691
+ [events],
692
+ );
693
+
694
+ const timedEvents = useMemo(
695
+ () => events.filter((event) => !event.allDay && !isOutOfOfficeEvent(event)),
696
+ [events],
697
+ );
679
698
 
680
699
  const { workingLocations, regularEvents } = useMemo(
681
700
  () => partitionAllDayEvents(allDayEvents),
@@ -849,6 +868,22 @@ export const WeekView = memo(function WeekView({
849
868
 
850
869
  const canDrag = !!onEventTimeChange;
851
870
 
871
+ const handleEventPopoverOpenChange = useCallback(
872
+ (event: CalendarEvent, open: boolean) => {
873
+ if (open) {
874
+ focusedEventIdRef.current = event.id;
875
+ setFocusedEventId(event.id);
876
+ setFocusedEvent(event);
877
+ return;
878
+ }
879
+ if (focusedEventIdRef.current !== event.id) return;
880
+ focusedEventIdRef.current = null;
881
+ setFocusedEventId(null);
882
+ setFocusedEvent(null);
883
+ },
884
+ [setFocusedEvent],
885
+ );
886
+
852
887
  const handleEventPointerDown = useCallback(
853
888
  (
854
889
  e: React.PointerEvent,
@@ -856,6 +891,7 @@ export const WeekView = memo(function WeekView({
856
891
  isStart: boolean,
857
892
  dayIndex: number,
858
893
  ) => {
894
+ focusedEventIdRef.current = event.id;
859
895
  setFocusedEventId(event.id);
860
896
  setFocusedEvent(event);
861
897
  if (
@@ -1262,6 +1298,16 @@ export const WeekView = memo(function WeekView({
1262
1298
  }
1263
1299
  }
1264
1300
 
1301
+ const visibleOutOfOfficeEvents = outOfOfficeEvents.filter(
1302
+ (event) => {
1303
+ const overrides = getDragOverrides(event.id);
1304
+ return (
1305
+ getOutOfOfficeSegment(event, day) !== null ||
1306
+ (dragEventId === event.id && overrides?.dayIndex === dayIndex)
1307
+ );
1308
+ },
1309
+ );
1310
+
1265
1311
  return (
1266
1312
  <div
1267
1313
  key={day.toISOString()}
@@ -1338,6 +1384,73 @@ export const WeekView = memo(function WeekView({
1338
1384
  </div>
1339
1385
  )}
1340
1386
 
1387
+ {/* Native Google out-of-office context sits behind meetings. */}
1388
+ {!isLoading &&
1389
+ visibleOutOfOfficeEvents.map((event, markerIndex) => {
1390
+ const isBeingDragged = dragEventId === event.id;
1391
+ const overrides = getDragOverrides(event.id);
1392
+ const canonicalDayIndex =
1393
+ getFirstVisibleOutOfOfficeDayIndex(event, days);
1394
+ return (
1395
+ <OutOfOfficeEvent
1396
+ key={`${event._tempId ?? event.id}:${day.toISOString()}`}
1397
+ event={event}
1398
+ day={day}
1399
+ hourHeight={HOUR_HEIGHT}
1400
+ color={
1401
+ getEventDisplayColor(event, prefs) ??
1402
+ "hsl(var(--primary))"
1403
+ }
1404
+ label={t("eventForm.outOfOffice")}
1405
+ markerIndex={markerIndex}
1406
+ compactMarker
1407
+ canDrag={canDrag}
1408
+ isBeingDragged={isBeingDragged}
1409
+ isDragging={isDragging}
1410
+ isDragTargetDay={overrides?.dayIndex === dayIndex}
1411
+ overrideTop={overrides?.top ?? null}
1412
+ overrideHeight={overrides?.height ?? null}
1413
+ onMovePointerDown={(pointerEvent, startsOnDay) =>
1414
+ handleEventPointerDown(
1415
+ pointerEvent,
1416
+ event,
1417
+ startsOnDay,
1418
+ dayIndex,
1419
+ )
1420
+ }
1421
+ onResizeTopPointerDown={(pointerEvent) =>
1422
+ handleResizeTopPointerDown(
1423
+ pointerEvent,
1424
+ event.id,
1425
+ dayIndex,
1426
+ )
1427
+ }
1428
+ onResizeBottomPointerDown={(pointerEvent) =>
1429
+ handleResizeBottomPointerDown(
1430
+ pointerEvent,
1431
+ event.id,
1432
+ dayIndex,
1433
+ )
1434
+ }
1435
+ shouldSuppressClick={shouldSuppressClick}
1436
+ onDelete={onDeleteEvent}
1437
+ isDraft={draftEventIds.includes(event.id)}
1438
+ defaultOpen={
1439
+ quickEditEventId === event.id &&
1440
+ dayIndex === canonicalDayIndex
1441
+ }
1442
+ onTitleSave={onQuickEditSave}
1443
+ onDismissNew={onQuickEditCancel}
1444
+ onDraftUpdate={onDraftUpdate}
1445
+ onDraftCreate={onDraftCreate}
1446
+ onDraftDiscard={onDraftDiscard}
1447
+ onOpenChange={(open) =>
1448
+ handleEventPopoverOpenChange(event, open)
1449
+ }
1450
+ />
1451
+ );
1452
+ })}
1453
+
1341
1454
  {/* Skeleton events when loading */}
1342
1455
  {isLoading &&
1343
1456
  WEEK_SKELETONS[dayIndex]?.map(
@@ -1402,6 +1515,7 @@ export const WeekView = memo(function WeekView({
1402
1515
  onDraftUpdate={onDraftUpdate}
1403
1516
  onDraftCreate={onDraftCreate}
1404
1517
  onDraftDiscard={onDraftDiscard}
1518
+ onPopoverOpenChange={handleEventPopoverOpenChange}
1405
1519
  />
1406
1520
  );
1407
1521
  })}
@@ -0,0 +1,45 @@
1
+ import type { CalendarEvent } from "@shared/api";
2
+ import { addDays, differenceInMinutes, parseISO, startOfDay } from "date-fns";
3
+
4
+ export interface OutOfOfficeSegment {
5
+ topMinutes: number;
6
+ durationMinutes: number;
7
+ startsOnDay: boolean;
8
+ endsOnDay: boolean;
9
+ }
10
+
11
+ export function isOutOfOfficeEvent(
12
+ event: Pick<CalendarEvent, "eventType">,
13
+ ): boolean {
14
+ return event.eventType === "outOfOffice";
15
+ }
16
+
17
+ /** Return the portion of a timed out-of-office event visible on one day. */
18
+ export function getOutOfOfficeSegment(
19
+ event: Pick<CalendarEvent, "start" | "end">,
20
+ day: Date,
21
+ ): OutOfOfficeSegment | null {
22
+ const eventStart = parseISO(event.start);
23
+ const eventEnd = parseISO(event.end);
24
+ const dayStart = startOfDay(day);
25
+ const dayEnd = addDays(dayStart, 1);
26
+
27
+ if (eventStart >= dayEnd || eventEnd <= dayStart) return null;
28
+
29
+ const segmentStart = eventStart > dayStart ? eventStart : dayStart;
30
+ const segmentEnd = eventEnd < dayEnd ? eventEnd : dayEnd;
31
+
32
+ return {
33
+ topMinutes: Math.max(0, differenceInMinutes(segmentStart, dayStart)),
34
+ durationMinutes: Math.max(1, differenceInMinutes(segmentEnd, segmentStart)),
35
+ startsOnDay: eventStart >= dayStart && eventStart < dayEnd,
36
+ endsOnDay: eventEnd > dayStart && eventEnd <= dayEnd,
37
+ };
38
+ }
39
+
40
+ export function getFirstVisibleOutOfOfficeDayIndex(
41
+ event: Pick<CalendarEvent, "start" | "end">,
42
+ days: Date[],
43
+ ): number {
44
+ return days.findIndex((day) => getOutOfOfficeSegment(event, day) !== null);
45
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Out-of-office blocks now sit behind meetings, and event stacking resets after closing details.
@@ -123,13 +123,14 @@ cd templates/content && pnpm action <name> [args]
123
123
  | `change-content-database-source-role` | `--databaseId <id>` or `--documentId <id> --sourceId <id> --relationshipMode items\|details [--join <json>]` | Change an attached source between adding rows and adding matched detail columns without removing the source |
124
124
  | `refresh-content-database-source` | `--databaseId <id>` or `--documentId <id>` | Refresh the read-only source status envelope; large Builder CMS sources may return a partial fetch, and another refresh continues loading remaining rows |
125
125
  | `process-builder-body-hydration` | `--sourceId <id> [--documentId <id>] [--limit <n>]` | Hydrate queued Builder article bodies into readable Content markdown with preserved source-component markers |
126
- | `set-content-database-source-write-mode` | `--databaseId <id>` or `--documentId <id> --liveWritesEnabled true\|false [--allowedWriteModes <json>]` | Enable/disable per-source Builder live writes; enabling is allowed only for `agent-native-blog-article-test` with explicit modes |
126
+ | `set-content-database-source-write-mode` | `--databaseId <id>` or `--documentId <id> [--sourceId <id>] --writeMode read_only\|stage_only\|publish_updates [--allowPublicationTransitions true\|false]` | Set the per-source Builder write tier; requires page admin access and remains read-only by default |
127
127
  | `stage-builder-revision` | `--databaseId <id>` or `--documentId <id>` | Stage pending local Builder CMS changes as a local-only save-revision record; never calls Builder |
128
128
  | `review-content-database-source-change-set` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> --decision approve\|reject [--note]` | Approve or reject a local source change-set review record without provider writes |
129
129
  | `stage-builder-source-bulk-update` | `--databaseId <id>` or `--documentId <id> --itemIds <json-array>` or `--documentIds <json-array> --field <json> [--sourceId <id>] [--dryRun true\|false]` | Preview or stage one bounded field update across selected source-backed Builder rows; apply still goes through Builder review/execution |
130
130
  | `prepare-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--pushModeConfirmation autosave\|draft\|publish]` | Prepare a dry-run Builder execution gate for approved field/body changes with request semantics/idempotency key; never calls Builder |
131
131
  | `validate-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--idempotencyKey <key>]` | Validate/replay a prepared Builder execution gate locally as a dry run; never calls Builder |
132
- | `execute-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--idempotencyKey <key>] [--pushModeConfirmation autosave\|draft\|publish]` | Execute a guarded live Builder write only when approved, validated, enabled, idempotent, and targeting `agent-native-blog-article-test` |
132
+ | `execute-builder-source-execution` | `--databaseId <id>` or `--documentId <id> --changeSetId <id> [--idempotencyKey <key>] [--pushModeConfirmation autosave\|draft\|publish]` | Execute a guarded live Builder write only when approved, validated, enabled for that source, and idempotent |
133
+ | `cancel-prepared-builder-source-update` | `--databaseId <id>` or `--documentId <id> --sourceId <id> --changeSetId <id> [--note <text>]` | Cancel an exact prepared Builder update only when every associated execution is provably pre-dispatch; preserves review and execution history and never calls Builder |
133
134
  | `add-database-item` | `--databaseId <id> [--title <text>] [--propertyValues <json>]` | Add a page row to a database, optionally seeding property values |
134
135
  | `submit-content-database-form` | `--databaseId <id> [--viewId <form-view-id>] [--title <text>] [--propertyValues <json>]` | Submit one form response atomically; validates required questions, resolves option labels/IDs, writes Blocks correctly, verifies persistence, and returns the exact row page link |
135
136
  | `duplicate-database-item` | `--itemId <id>` or `--documentId <id> [--title <text>]` | Duplicate exactly one database row page and its stored property values; for two or more rows, use `duplicate-database-items` once |
@@ -0,0 +1,87 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+
3
+ import { BUILDER_CMS_SAFE_WRITE_MODEL } from "../shared/api.js";
4
+ import { readBuilderCmsContentEntries } from "./_builder-cms-read-client.js";
5
+ import type { BuilderCmsSourceEntry } from "./_builder-cms-source-adapter.js";
6
+ import { BUILDER_CMS_EXECUTION_MARKER_FIELD } from "./_builder-cms-write-adapter.js";
7
+
8
+ export interface BuilderCmsIntentMatch {
9
+ id: string;
10
+ title: string;
11
+ lastUpdated: string | null;
12
+ published: string | null;
13
+ }
14
+
15
+ export function matchBuilderCmsSafeModelIntentEntries(
16
+ entries: BuilderCmsSourceEntry[],
17
+ args: {
18
+ marker?: string;
19
+ exactTitle?: string;
20
+ intendedFields?: Record<string, unknown>;
21
+ },
22
+ ) {
23
+ const identityMatches = entries.filter((entry) => {
24
+ const data = rawRecord(entry.rawEntry?.data);
25
+ if (args.marker) {
26
+ return data?.[BUILDER_CMS_EXECUTION_MARKER_FIELD] === args.marker;
27
+ }
28
+ const title = data?.title;
29
+ return (
30
+ typeof title === "string" && title.trim() === args.exactTitle?.trim()
31
+ );
32
+ });
33
+ const matchingIntent = identityMatches.filter((entry) => {
34
+ const data = rawRecord(entry.rawEntry?.data);
35
+ return Object.entries(args.intendedFields ?? {}).every(([key, value]) =>
36
+ isDeepStrictEqual(data?.[key], value),
37
+ );
38
+ });
39
+ return {
40
+ count: identityMatches.length,
41
+ matchingIntentCount: matchingIntent.length,
42
+ matches: identityMatches.map(compactMatch),
43
+ };
44
+ }
45
+
46
+ function rawRecord(value: unknown): Record<string, unknown> | null {
47
+ return value && typeof value === "object" && !Array.isArray(value)
48
+ ? (value as Record<string, unknown>)
49
+ : null;
50
+ }
51
+
52
+ function compactMatch(entry: BuilderCmsSourceEntry): BuilderCmsIntentMatch {
53
+ const raw = rawRecord(entry.rawEntry);
54
+ return {
55
+ id: entry.id,
56
+ title: entry.title,
57
+ lastUpdated:
58
+ typeof raw?.lastUpdated === "string" ||
59
+ typeof raw?.lastUpdated === "number"
60
+ ? String(raw.lastUpdated)
61
+ : entry.updatedAt || null,
62
+ published: typeof raw?.published === "string" ? raw.published : null,
63
+ };
64
+ }
65
+
66
+ export async function lookupBuilderCmsSafeModelIntent(args: {
67
+ marker?: string;
68
+ exactTitle?: string;
69
+ intendedFields?: Record<string, unknown>;
70
+ }) {
71
+ if (!args.marker && !args.exactTitle) {
72
+ throw new Error("Provide an exact execution marker or exact title.");
73
+ }
74
+ const result = await readBuilderCmsContentEntries({
75
+ model: BUILDER_CMS_SAFE_WRITE_MODEL,
76
+ rawData: true,
77
+ requirePrivateKey: true,
78
+ limit: 10_000,
79
+ });
80
+ if (result.state !== "live" || result.progress.partial) {
81
+ throw new Error(
82
+ result.message ??
83
+ "Builder safe-model lookup was incomplete; reconciliation cannot proceed.",
84
+ );
85
+ }
86
+ return matchBuilderCmsSafeModelIntentEntries(result.entries, args);
87
+ }