@agent-native/core 0.101.5 → 0.101.7

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 (149) hide show
  1. package/README.md +1 -1
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +12 -0
  4. package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
  5. package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
  6. package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
  7. package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
  8. package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
  9. package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
  10. package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
  11. package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
  12. package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
  13. package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
  14. package/corpus/core/docs/content/pure-agent-apps.mdx +58 -14
  15. package/corpus/core/package.json +1 -1
  16. package/corpus/core/src/a2a/artifact-response.ts +255 -12
  17. package/corpus/core/src/agent/thread-data-builder.ts +84 -11
  18. package/corpus/core/src/integrations/a2a-continuation-processor.ts +99 -7
  19. package/corpus/core/src/integrations/adapters/discord.ts +3 -1
  20. package/corpus/core/src/integrations/adapters/email.ts +4 -1
  21. package/corpus/core/src/integrations/adapters/google-docs.ts +4 -1
  22. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +3 -1
  23. package/corpus/core/src/integrations/adapters/slack.ts +38 -9
  24. package/corpus/core/src/integrations/adapters/telegram.ts +32 -10
  25. package/corpus/core/src/integrations/adapters/whatsapp.ts +5 -1
  26. package/corpus/core/src/integrations/index.ts +1 -0
  27. package/corpus/core/src/integrations/types.ts +9 -2
  28. package/corpus/core/src/integrations/webhook-handler.ts +69 -45
  29. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +1 -0
  30. package/corpus/templates/analytics/AGENTS.md +6 -0
  31. package/corpus/templates/analytics/README.md +1 -1
  32. package/corpus/templates/analytics/actions/data-source-status.ts +19 -1
  33. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +5 -1
  34. package/corpus/templates/analytics/app/routes/_index.tsx +2 -2
  35. package/corpus/templates/analytics/changelog/2026-07-14-analytics-chat-guides-you-to-connect-a-data-source-with-a-di.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-07-14-analytics-landing-pages-now-clearly-position-the-app-as-an-o.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-07-15-daily-dashboard-emails-now-include-the-complete-dashboard-sc.md +6 -0
  38. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +8 -1
  39. package/corpus/templates/analytics/server/lib/dashboard-report.ts +64 -12
  40. package/corpus/templates/analytics/server/lib/real-data-actions.ts +15 -3
  41. package/corpus/templates/analytics/server/plugins/agent-chat.ts +364 -4
  42. package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +1 -0
  43. package/corpus/templates/clips/app/components/library/folder-tree.tsx +12 -9
  44. package/corpus/templates/clips/app/components/library/library-layout.tsx +19 -16
  45. package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +13 -9
  46. package/corpus/templates/clips/changelog/2026-07-15-folders-and-sidebar-links-are-clickable-again-immediately-af.md +6 -0
  47. package/corpus/templates/clips/changelog/2026-07-15-hovering-a-truncated-folder-name-now-shows-the-full-name.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-15-space-creation-controls-are-now-shown-only-to-organization-a.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-15-vocabulary-terms-can-now-be-deleted-from-the-dictation-dicti.md +6 -0
  50. package/corpus/templates/clips/desktop/src/styles.css +23 -1
  51. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +49 -7
  52. package/corpus/templates/clips/server/lib/recordings.ts +5 -2
  53. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  54. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +35 -21
  55. package/corpus/templates/content/AGENTS.md +29 -7
  56. package/corpus/templates/content/actions/_database-source-utils.ts +30 -16
  57. package/corpus/templates/content/actions/_database-utils.ts +15 -1
  58. package/corpus/templates/content/actions/_local-file-documents.ts +53 -1
  59. package/corpus/templates/content/actions/_property-utils.ts +7 -1
  60. package/corpus/templates/content/actions/configure-document-property.ts +11 -0
  61. package/corpus/templates/content/actions/create-content-database.ts +11 -0
  62. package/corpus/templates/content/actions/create-document.ts +9 -0
  63. package/corpus/templates/content/actions/create-inline-content-database.ts +3 -0
  64. package/corpus/templates/content/actions/export-content-source.ts +1 -0
  65. package/corpus/templates/content/actions/get-content-database.ts +13 -1
  66. package/corpus/templates/content/actions/get-document.ts +12 -2
  67. package/corpus/templates/content/actions/import-content-source.ts +6 -0
  68. package/corpus/templates/content/actions/list-documents.ts +3 -0
  69. package/corpus/templates/content/actions/pull-document.ts +2 -0
  70. package/corpus/templates/content/actions/search-documents.ts +5 -1
  71. package/corpus/templates/content/actions/update-document.ts +16 -1
  72. package/corpus/templates/content/actions/view-screen.ts +15 -2
  73. package/corpus/templates/content/app/components/editor/DescriptionField.tsx +121 -0
  74. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +14 -0
  75. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +223 -34
  76. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +56 -8
  77. package/corpus/templates/content/app/i18n/zh-TW.ts +5 -0
  78. package/corpus/templates/content/app/i18n-data.ts +52 -0
  79. package/corpus/templates/content/changelog/2026-07-14-pages-and-databases-can-describe-themselves-with-guidance-fo.md +6 -0
  80. package/corpus/templates/content/changelog/2026-07-14-slack-follow-ups-now-retain-created-content-identity-and-per.md +6 -0
  81. package/corpus/templates/content/server/db/schema.ts +4 -0
  82. package/corpus/templates/content/server/lib/document-context.ts +76 -0
  83. package/corpus/templates/content/server/lib/public-documents.ts +3 -0
  84. package/corpus/templates/content/server/plugins/db.ts +6 -0
  85. package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +5 -0
  86. package/corpus/templates/content/shared/api.ts +17 -0
  87. package/corpus/templates/content/shared/content-source.ts +7 -0
  88. package/corpus/templates/content/shared/properties.ts +2 -0
  89. package/dist/a2a/artifact-response.d.ts +15 -0
  90. package/dist/a2a/artifact-response.d.ts.map +1 -1
  91. package/dist/a2a/artifact-response.js +204 -5
  92. package/dist/a2a/artifact-response.js.map +1 -1
  93. package/dist/agent/thread-data-builder.d.ts +6 -0
  94. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  95. package/dist/agent/thread-data-builder.js +71 -8
  96. package/dist/agent/thread-data-builder.js.map +1 -1
  97. package/dist/collab/struct-routes.d.ts +1 -1
  98. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  99. package/dist/integrations/a2a-continuation-processor.js +72 -6
  100. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  101. package/dist/integrations/adapters/discord.d.ts.map +1 -1
  102. package/dist/integrations/adapters/discord.js +1 -0
  103. package/dist/integrations/adapters/discord.js.map +1 -1
  104. package/dist/integrations/adapters/email.d.ts.map +1 -1
  105. package/dist/integrations/adapters/email.js +2 -0
  106. package/dist/integrations/adapters/email.js.map +1 -1
  107. package/dist/integrations/adapters/google-docs.d.ts.map +1 -1
  108. package/dist/integrations/adapters/google-docs.js +2 -0
  109. package/dist/integrations/adapters/google-docs.js.map +1 -1
  110. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -1
  111. package/dist/integrations/adapters/microsoft-teams.js +1 -0
  112. package/dist/integrations/adapters/microsoft-teams.js.map +1 -1
  113. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  114. package/dist/integrations/adapters/slack.js +21 -6
  115. package/dist/integrations/adapters/slack.js.map +1 -1
  116. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  117. package/dist/integrations/adapters/telegram.js +11 -2
  118. package/dist/integrations/adapters/telegram.js.map +1 -1
  119. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  120. package/dist/integrations/adapters/whatsapp.js +3 -0
  121. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  122. package/dist/integrations/index.d.ts +1 -1
  123. package/dist/integrations/index.d.ts.map +1 -1
  124. package/dist/integrations/index.js.map +1 -1
  125. package/dist/integrations/types.d.ts +8 -2
  126. package/dist/integrations/types.d.ts.map +1 -1
  127. package/dist/integrations/types.js.map +1 -1
  128. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  129. package/dist/integrations/webhook-handler.js +40 -43
  130. package/dist/integrations/webhook-handler.js.map +1 -1
  131. package/dist/progress/routes.d.ts +1 -1
  132. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  133. package/dist/resources/handlers.d.ts +2 -2
  134. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  135. package/dist/server/agent-chat/action-filters-a2a.js +1 -0
  136. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  137. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  138. package/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
  139. package/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
  140. package/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
  141. package/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
  142. package/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
  143. package/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
  144. package/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
  145. package/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
  146. package/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
  147. package/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
  148. package/docs/content/pure-agent-apps.mdx +58 -14
  149. package/package.json +3 -3
@@ -105,6 +105,12 @@ import {
105
105
  PopoverTrigger,
106
106
  } from "@/components/ui/popover";
107
107
  import { Spinner } from "@/components/ui/spinner";
108
+ import { Textarea } from "@/components/ui/textarea";
109
+ import {
110
+ Tooltip,
111
+ TooltipContent,
112
+ TooltipTrigger,
113
+ } from "@/components/ui/tooltip";
108
114
  import { applySourceFieldPropertyToDatabaseResponse } from "@/hooks/use-content-database";
109
115
  import {
110
116
  useConfigureDocumentProperty,
@@ -551,7 +557,8 @@ export function filterPropertyOptions(
551
557
  return options.filter(
552
558
  (option) =>
553
559
  option.name.toLowerCase().includes(normalizedQuery) ||
554
- option.id.toLowerCase().includes(normalizedQuery),
560
+ option.id.toLowerCase().includes(normalizedQuery) ||
561
+ option.description?.toLowerCase().includes(normalizedQuery),
555
562
  );
556
563
  }
557
564
 
@@ -612,6 +619,76 @@ export function updatePropertyOptionColor(
612
619
  );
613
620
  }
614
621
 
622
+ export function updatePropertyOptionDescription(
623
+ options: DocumentPropertyOption[],
624
+ optionId: string,
625
+ description: string,
626
+ ) {
627
+ return options.map((option) =>
628
+ option.id === optionId ? { ...option, description } : option,
629
+ );
630
+ }
631
+
632
+ /**
633
+ * Keeps successive option edits based on the same local truth until the
634
+ * server catches up. A rename followed immediately by a usage-description
635
+ * edit must not let either request erase the other.
636
+ */
637
+ export function createPropertyOptionUpdateQueue(
638
+ initialOptions: DocumentPropertyOption[],
639
+ persist: (options: DocumentPropertyOption[]) => Promise<unknown>,
640
+ ) {
641
+ let current = initialOptions;
642
+ let tail: Promise<unknown> = Promise.resolve();
643
+
644
+ return {
645
+ replace(options: DocumentPropertyOption[]) {
646
+ current = options;
647
+ },
648
+ enqueue(
649
+ update: (options: DocumentPropertyOption[]) => DocumentPropertyOption[],
650
+ ) {
651
+ current = update(current);
652
+ const snapshot = current;
653
+ tail = tail.catch(() => undefined).then(() => persist(snapshot));
654
+ return tail;
655
+ },
656
+ };
657
+ }
658
+
659
+ type PropertyMetadataSnapshot = Pick<
660
+ DocumentProperty["definition"],
661
+ "name" | "type" | "description" | "visibility" | "options"
662
+ >;
663
+
664
+ /**
665
+ * Serializes property-definition edits against one local snapshot. The action
666
+ * accepts the complete definition, so composing each request from render-time
667
+ * props would let a fast description save restore the name from before an
668
+ * overlapping rename completed.
669
+ */
670
+ export function createPropertyMetadataUpdateQueue(
671
+ initialMetadata: PropertyMetadataSnapshot,
672
+ persist: (metadata: PropertyMetadataSnapshot) => Promise<unknown>,
673
+ ) {
674
+ let current = initialMetadata;
675
+ let tail: Promise<unknown> = Promise.resolve();
676
+
677
+ return {
678
+ replace(metadata: PropertyMetadataSnapshot) {
679
+ current = metadata;
680
+ },
681
+ enqueue(
682
+ update: (metadata: PropertyMetadataSnapshot) => PropertyMetadataSnapshot,
683
+ ) {
684
+ current = update(current);
685
+ const snapshot = current;
686
+ tail = tail.catch(() => undefined).then(() => persist(snapshot));
687
+ return tail;
688
+ },
689
+ };
690
+ }
691
+
615
692
  export function removePropertyOption(
616
693
  options: DocumentPropertyOption[],
617
694
  optionId: string,
@@ -833,7 +910,23 @@ function PropertyRow({
833
910
  ) : (
834
911
  <div className="flex min-w-0 items-center gap-2 text-muted-foreground">
835
912
  <Icon className="size-4 shrink-0" />
836
- <span className="truncate">{property.definition.name}</span>
913
+ {property.definition.description ? (
914
+ <Tooltip>
915
+ <TooltipTrigger asChild>
916
+ <button
917
+ type="button"
918
+ className="truncate text-left outline-none focus-visible:ring-2 focus-visible:ring-ring"
919
+ >
920
+ {property.definition.name}
921
+ </button>
922
+ </TooltipTrigger>
923
+ <TooltipContent className="max-w-64">
924
+ {property.definition.description}
925
+ </TooltipContent>
926
+ </Tooltip>
927
+ ) : (
928
+ <span className="truncate">{property.definition.name}</span>
929
+ )}
837
930
  </div>
838
931
  )}
839
932
  {canEdit && property.editable ? (
@@ -961,7 +1054,25 @@ export function PropertyManagementPopover({
961
1054
  const [open, setOpen] = useState(false);
962
1055
  const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
963
1056
  const [name, setName] = useState(property.definition.name);
1057
+ const [description, setDescription] = useState(
1058
+ property.definition.description,
1059
+ );
964
1060
  const [newOption, setNewOption] = useState("");
1061
+ const persistMetadataSnapshotRef = useRef<
1062
+ (metadata: PropertyMetadataSnapshot) => Promise<unknown>
1063
+ >(async () => undefined);
1064
+ const metadataUpdateQueueRef = useRef(
1065
+ createPropertyMetadataUpdateQueue(
1066
+ {
1067
+ name: property.definition.name,
1068
+ type: property.definition.type,
1069
+ description: property.definition.description,
1070
+ visibility: property.definition.visibility,
1071
+ options: property.definition.options,
1072
+ },
1073
+ (metadata) => persistMetadataSnapshotRef.current(metadata),
1074
+ ),
1075
+ );
965
1076
  const propertyNameInputRef = useRef<HTMLInputElement>(null);
966
1077
  const typeIsLocked = isComputedPropertyType(property.definition.type);
967
1078
  const typeNeedsOptions =
@@ -971,7 +1082,15 @@ export function PropertyManagementPopover({
971
1082
 
972
1083
  function resetDraft() {
973
1084
  setName(property.definition.name);
1085
+ setDescription(property.definition.description);
974
1086
  setNewOption("");
1087
+ metadataUpdateQueueRef.current.replace({
1088
+ name: property.definition.name,
1089
+ type: property.definition.type,
1090
+ description: property.definition.description,
1091
+ visibility: property.definition.visibility,
1092
+ options: property.definition.options,
1093
+ });
975
1094
  }
976
1095
 
977
1096
  useEffect(() => {
@@ -990,17 +1109,34 @@ export function PropertyManagementPopover({
990
1109
  type?: DocumentPropertyType;
991
1110
  visibility?: DocumentPropertyVisibility;
992
1111
  options?: DocumentProperty["definition"]["options"];
1112
+ description?: string;
993
1113
  }) {
994
- const nextType = next.type ?? property.definition.type;
995
- await configure.mutateAsync({
1114
+ await metadataUpdateQueueRef.current.enqueue((current) => ({
1115
+ name: next.name?.trim() || current.name,
1116
+ type: next.type ?? current.type,
1117
+ description: next.description ?? current.description,
1118
+ visibility: next.visibility ?? current.visibility,
1119
+ options: next.options ?? current.options,
1120
+ }));
1121
+ }
1122
+
1123
+ async function updateOptions(
1124
+ update: (options: DocumentPropertyOption[]) => DocumentPropertyOption[],
1125
+ ) {
1126
+ await metadataUpdateQueueRef.current.enqueue((current) => ({
1127
+ ...current,
1128
+ options: {
1129
+ options: update(current.options.options ?? []),
1130
+ },
1131
+ }));
1132
+ }
1133
+
1134
+ persistMetadataSnapshotRef.current = (metadata) =>
1135
+ configure.mutateAsync({
996
1136
  id: property.definition.id,
997
1137
  documentId,
998
- name: next.name?.trim() || property.definition.name,
999
- type: nextType,
1000
- visibility: next.visibility,
1001
- options: next.options ?? property.definition.options,
1138
+ ...metadata,
1002
1139
  });
1003
- }
1004
1140
 
1005
1141
  async function renameProperty() {
1006
1142
  const nextName = name.trim();
@@ -1008,6 +1144,12 @@ export function PropertyManagementPopover({
1008
1144
  await configureProperty({ name: nextName });
1009
1145
  }
1010
1146
 
1147
+ async function updateDescription() {
1148
+ const nextDescription = (description ?? "").trim();
1149
+ if (nextDescription === property.definition.description) return;
1150
+ await configureProperty({ description: nextDescription });
1151
+ }
1152
+
1011
1153
  async function updateType(nextType: DocumentPropertyType) {
1012
1154
  if (nextType === property.definition.type) return;
1013
1155
  await configureProperty({
@@ -1042,39 +1184,39 @@ export function PropertyManagementPopover({
1042
1184
  async function addOption() {
1043
1185
  const optionName = newOption.trim();
1044
1186
  if (!optionName) return;
1045
- const existing = property.definition.options.options ?? [];
1046
- const option = makeOption(
1047
- optionName,
1048
- existing.length,
1049
- existing.map((item) => item.id),
1050
- );
1051
- await configureProperty({
1052
- options: { options: [...existing, option] },
1053
- });
1187
+ await updateOptions((existing) => [
1188
+ ...existing,
1189
+ makeOption(
1190
+ optionName,
1191
+ existing.length,
1192
+ existing.map((item) => item.id),
1193
+ ),
1194
+ ]);
1054
1195
  setNewOption("");
1055
1196
  }
1056
1197
 
1057
1198
  async function removeOption(id: string) {
1058
- await configureProperty({
1059
- options: {
1060
- options: (property.definition.options.options ?? []).filter(
1061
- (option) => option.id !== id,
1062
- ),
1063
- },
1064
- });
1199
+ await updateOptions((options) =>
1200
+ options.filter((option) => option.id !== id),
1201
+ );
1065
1202
  }
1066
1203
 
1067
1204
  async function renameOption(id: string, optionName: string) {
1068
- const options = property.definition.options.options ?? [];
1069
- const nextOptions = renamePropertyOption(options, id, optionName);
1070
- if (nextOptions === options) return;
1071
- await configureProperty({ options: { options: nextOptions } });
1205
+ await updateOptions((options) =>
1206
+ renamePropertyOption(options, id, optionName),
1207
+ );
1072
1208
  }
1073
1209
 
1074
1210
  async function recolorOption(id: string, color: DocumentPropertyOptionColor) {
1075
- const options = property.definition.options.options ?? [];
1076
- const nextOptions = updatePropertyOptionColor(options, id, color);
1077
- await configureProperty({ options: { options: nextOptions } });
1211
+ await updateOptions((options) =>
1212
+ updatePropertyOptionColor(options, id, color),
1213
+ );
1214
+ }
1215
+
1216
+ async function describeOption(id: string, description: string) {
1217
+ await updateOptions((options) =>
1218
+ updatePropertyOptionDescription(options, id, description),
1219
+ );
1078
1220
  }
1079
1221
 
1080
1222
  return (
@@ -1092,6 +1234,7 @@ export function PropertyManagementPopover({
1092
1234
  aria-label={t("editor.properties.propertyMenuFor", {
1093
1235
  name: property.definition.name,
1094
1236
  })}
1237
+ title={property.definition.description || undefined}
1095
1238
  className={cn(
1096
1239
  "flex min-w-0 items-center gap-2 rounded px-1 py-0.5 text-left text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1097
1240
  triggerClassName,
@@ -1138,6 +1281,21 @@ export function PropertyManagementPopover({
1138
1281
  />
1139
1282
  </div>
1140
1283
 
1284
+ <div
1285
+ className="px-2 pb-2 pt-1"
1286
+ onKeyDown={(event) => event.stopPropagation()}
1287
+ >
1288
+ <Textarea
1289
+ rows={2}
1290
+ value={description}
1291
+ aria-label={t("editor.properties.description")}
1292
+ placeholder={t("editor.properties.addPropertyDescription")}
1293
+ onChange={(event) => setDescription(event.target.value)}
1294
+ onBlur={() => void updateDescription()}
1295
+ className="block w-full resize-none rounded border border-transparent bg-muted/30 px-2 py-1.5 text-xs leading-5 text-muted-foreground outline-none placeholder:text-muted-foreground/60 focus:border-input focus:bg-background focus:ring-1 focus:ring-ring"
1296
+ />
1297
+ </div>
1298
+
1141
1299
  <DropdownMenuSub>
1142
1300
  <DropdownMenuSubTrigger>
1143
1301
  <Icon className="mr-2 size-4 text-muted-foreground" />
@@ -1217,6 +1375,9 @@ export function PropertyManagementPopover({
1217
1375
  option={option}
1218
1376
  disabled={configure.isPending}
1219
1377
  onRename={(name) => void renameOption(option.id, name)}
1378
+ onDescriptionChange={(description) =>
1379
+ void describeOption(option.id, description)
1380
+ }
1220
1381
  onColorChange={(color) =>
1221
1382
  void recolorOption(option.id, color)
1222
1383
  }
@@ -1424,21 +1585,27 @@ function PropertyOptionSettingsRow({
1424
1585
  option,
1425
1586
  disabled,
1426
1587
  onRename,
1588
+ onDescriptionChange,
1427
1589
  onColorChange,
1428
1590
  onRemove,
1429
1591
  }: {
1430
1592
  option: DocumentPropertyOption;
1431
1593
  disabled: boolean;
1432
1594
  onRename: (name: string) => void;
1595
+ onDescriptionChange: (description: string) => void;
1433
1596
  onColorChange: (color: DocumentPropertyOptionColor) => void;
1434
1597
  onRemove: () => void;
1435
1598
  }) {
1436
1599
  const t = useT();
1437
1600
  const [draftName, setDraftName] = useState(option.name);
1601
+ const [draftDescription, setDraftDescription] = useState(
1602
+ option.description ?? "",
1603
+ );
1438
1604
 
1439
1605
  useEffect(() => {
1440
1606
  setDraftName(option.name);
1441
- }, [option.name]);
1607
+ setDraftDescription(option.description ?? "");
1608
+ }, [option.description, option.name]);
1442
1609
 
1443
1610
  function submitRename() {
1444
1611
  const nextName = draftName.trim();
@@ -1528,6 +1695,21 @@ function PropertyOptionSettingsRow({
1528
1695
  {t("editor.properties.remove")}
1529
1696
  </button>
1530
1697
  </div>
1698
+ <Textarea
1699
+ rows={1}
1700
+ value={draftDescription}
1701
+ disabled={disabled}
1702
+ aria-label={`${t("editor.properties.description")}: ${option.name}`}
1703
+ placeholder={t("editor.properties.addOptionDescription")}
1704
+ onChange={(event) => setDraftDescription(event.target.value)}
1705
+ onBlur={() => {
1706
+ const nextDescription = draftDescription.trim();
1707
+ if (nextDescription !== (option.description ?? "")) {
1708
+ onDescriptionChange(nextDescription);
1709
+ }
1710
+ }}
1711
+ className="ml-5 block w-[calc(100%-1.25rem)] resize-none rounded border-0 bg-transparent px-1 text-xs leading-5 text-muted-foreground shadow-none placeholder:text-muted-foreground/60 focus:bg-background focus:ring-1 focus:ring-ring"
1712
+ />
1531
1713
  </div>
1532
1714
  );
1533
1715
  }
@@ -2559,7 +2741,14 @@ function OptionValueEditor({
2559
2741
  className="flex w-full items-center justify-between gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-accent"
2560
2742
  onClick={() => void chooseOption(option)}
2561
2743
  >
2562
- <OptionPill option={option} />
2744
+ <span className="min-w-0 flex-1">
2745
+ <OptionPill option={option} />
2746
+ {option.description ? (
2747
+ <span className="mt-0.5 block truncate text-xs text-muted-foreground">
2748
+ {option.description}
2749
+ </span>
2750
+ ) : null}
2751
+ </span>
2563
2752
  {checked ? (
2564
2753
  <IconCheck className="size-4 text-muted-foreground" />
2565
2754
  ) : null}
@@ -5,6 +5,7 @@ import {
5
5
  useBuilderStatus,
6
6
  useCodeMode,
7
7
  useSession,
8
+ useT,
8
9
  } from "@agent-native/core/client";
9
10
  import {
10
11
  AlertDialog,
@@ -200,6 +201,7 @@ import {
200
201
  BuilderSourceReviewDialog,
201
202
  type BuilderReviewPublicationTransitions,
202
203
  } from "../database-sources/BuilderSourceReviewDialog";
204
+ import { DescriptionField } from "../DescriptionField";
203
205
  import { DocumentBlockFields } from "../DocumentBlockFields";
204
206
  import {
205
207
  AddProperty,
@@ -514,6 +516,7 @@ function DatabaseTable({
514
516
  canEdit: boolean;
515
517
  isActive: boolean;
516
518
  }) {
519
+ const t = useT();
517
520
  const navigate = useNavigate();
518
521
  const queryClient = useQueryClient();
519
522
  const [databaseItemLimit, setDatabaseItemLimit] = useState(
@@ -530,6 +533,7 @@ function DatabaseTable({
530
533
  const executeBuilderExecution = useExecuteBuilderSourceExecution(document.id);
531
534
  const setSourceWriteMode = useSetContentDatabaseSourceWriteMode(document.id);
532
535
  const setProperty = useSetDocumentProperty(document.id, document.id);
536
+ const updateDatabaseDocument = useUpdateDocument();
533
537
  const updateView = useUpdateContentDatabaseView(document.id);
534
538
  // A deleted/missing database resolves to the unavailable union (no
535
539
  // `database` field) — treat it as no data; the inline-block wrapper owns
@@ -1578,6 +1582,19 @@ function DatabaseTable({
1578
1582
 
1579
1583
  return (
1580
1584
  <div className="mt-4 min-w-0 w-full max-w-[calc(100vw-var(--content-sidebar-width,0px)-1.5rem)]">
1585
+ <DescriptionField
1586
+ description={document.description}
1587
+ canEdit={canEdit}
1588
+ label={t("editor.properties.description")}
1589
+ placeholder={t("editor.properties.addDatabaseDescription")}
1590
+ className="mb-3"
1591
+ onSave={(description) =>
1592
+ updateDatabaseDocument.mutateAsync({
1593
+ id: document.id,
1594
+ description,
1595
+ })
1596
+ }
1597
+ />
1581
1598
  <div className="mb-1 flex min-h-8 flex-wrap items-center justify-between gap-x-3 gap-y-1 pb-1">
1582
1599
  <DatabaseViewTabs
1583
1600
  viewConfig={viewConfig}
@@ -12129,8 +12146,10 @@ export function databaseBulkMultiSelectFilteredOptions(
12129
12146
  ) {
12130
12147
  const normalizedQuery = query.trim().toLowerCase();
12131
12148
  if (!normalizedQuery) return options;
12132
- return options.filter((option) =>
12133
- option.name.trim().toLowerCase().includes(normalizedQuery),
12149
+ return options.filter(
12150
+ (option) =>
12151
+ option.name.trim().toLowerCase().includes(normalizedQuery) ||
12152
+ option.description?.trim().toLowerCase().includes(normalizedQuery),
12134
12153
  );
12135
12154
  }
12136
12155
 
@@ -13511,7 +13530,14 @@ function DatabaseBulkOptionValueEditor({
13511
13530
  )
13512
13531
  }
13513
13532
  >
13514
- <DatabaseBulkOptionPill option={option} />
13533
+ <span className="min-w-0 flex-1">
13534
+ <DatabaseBulkOptionPill option={option} />
13535
+ {option.description ? (
13536
+ <span className="mt-0.5 block truncate text-xs text-muted-foreground">
13537
+ {option.description}
13538
+ </span>
13539
+ ) : null}
13540
+ </span>
13515
13541
  {presence.presentInAll ? (
13516
13542
  <IconCheck className="size-4 shrink-0 text-muted-foreground" />
13517
13543
  ) : presence.presentInAny ? (
@@ -13560,7 +13586,14 @@ function DatabaseBulkOptionValueEditor({
13560
13586
  disabled={disabled}
13561
13587
  onClick={() => void onApply({ kind: "set", value: option.id })}
13562
13588
  >
13563
- <DatabaseBulkOptionPill option={option} />
13589
+ <span className="min-w-0 flex-1">
13590
+ <DatabaseBulkOptionPill option={option} />
13591
+ {option.description ? (
13592
+ <span className="mt-0.5 block truncate text-xs text-muted-foreground">
13593
+ {option.description}
13594
+ </span>
13595
+ ) : null}
13596
+ </span>
13564
13597
  </button>
13565
13598
  ))}
13566
13599
  </div>
@@ -15858,6 +15891,24 @@ function DatabaseGroupedTableSection({
15858
15891
  );
15859
15892
  }
15860
15893
 
15894
+ export function databaseItemPropertyForColumn(
15895
+ item: ContentDatabaseItem,
15896
+ columnProperty: DocumentProperty,
15897
+ ): DocumentProperty {
15898
+ const itemProperty = item.properties.find(
15899
+ (candidate) => candidate.definition.id === columnProperty.definition.id,
15900
+ );
15901
+ if (!itemProperty) return columnProperty;
15902
+
15903
+ // Row payloads own values, not schema meaning. Always pair the row's value
15904
+ // with the database's current canonical definition so a recently described
15905
+ // option cannot remain invisible in an older row snapshot.
15906
+ return {
15907
+ ...itemProperty,
15908
+ definition: columnProperty.definition,
15909
+ };
15910
+ }
15911
+
15861
15912
  function DatabaseTableRow({
15862
15913
  item,
15863
15914
  properties,
@@ -15942,10 +15993,7 @@ function DatabaseTableRow({
15942
15993
  onPreview={onPreview}
15943
15994
  />
15944
15995
  {properties.map((property) => {
15945
- const itemProperty =
15946
- item.properties.find(
15947
- (candidate) => candidate.definition.id === property.definition.id,
15948
- ) ?? property;
15996
+ const itemProperty = databaseItemPropertyForColumn(item, property);
15949
15997
  const bodyCellHydrationPending =
15950
15998
  itemProperty.definition.type === "blocks" &&
15951
15999
  databaseItemBodyHydrationIsPending(item);
@@ -357,6 +357,11 @@ const messages = {
357
357
  pasteFileOrMediaLink: "貼上檔案或媒體連結",
358
358
  personOrEmail: "個人或電子郵件",
359
359
  propertyMenuFor: "{{name}} 的屬性選單",
360
+ description: "說明",
361
+ addPageDescription: "新增此頁面的說明…",
362
+ addDatabaseDescription: "新增此資料庫的說明…",
363
+ addPropertyDescription: "說明此屬性應包含的內容…",
364
+ addOptionDescription: "何時應使用此選項?",
360
365
  propertyName: "物業名稱",
361
366
  readOnly: "唯讀",
362
367
  remove: "刪除",
@@ -2271,6 +2271,11 @@ const editorPropertiesMessages = {
2271
2271
  pasteFileOrMediaLink: "Paste file or media link",
2272
2272
  personOrEmail: "Person or email",
2273
2273
  propertyMenuFor: "Property menu for {{name}}",
2274
+ description: "Description",
2275
+ addPageDescription: "Add a description of this page…",
2276
+ addDatabaseDescription: "Add a description of this database…",
2277
+ addPropertyDescription: "Describe what belongs in this property…",
2278
+ addOptionDescription: "When should this option be used?",
2274
2279
  propertyName: "Property name",
2275
2280
  readOnly: "Read-only",
2276
2281
  remove: "Remove",
@@ -5298,6 +5303,11 @@ const editorMessagesByLocale = {
5298
5303
  pasteFileOrMediaLink: "粘贴文件或媒体链接",
5299
5304
  personOrEmail: "个人或电子邮件",
5300
5305
  propertyMenuFor: "{{name}} 的属性菜单",
5306
+ description: "说明",
5307
+ addPageDescription: "添加此页面的说明…",
5308
+ addDatabaseDescription: "添加此数据库的说明…",
5309
+ addPropertyDescription: "描述此属性中应包含的内容…",
5310
+ addOptionDescription: "何时应使用此选项?",
5301
5311
  propertyName: "物业名称",
5302
5312
  readOnly: "只读",
5303
5313
  remove: "删除",
@@ -5629,6 +5639,11 @@ const editorMessagesByLocale = {
5629
5639
  pasteFileOrMediaLink: "Pegar archivo o enlace multimedia",
5630
5640
  personOrEmail: "Persona o correo electrónico",
5631
5641
  propertyMenuFor: "Menú de propiedades para {{name}}",
5642
+ description: "Descripción",
5643
+ addPageDescription: "Añade una descripción de esta página…",
5644
+ addDatabaseDescription: "Añade una descripción de esta base de datos…",
5645
+ addPropertyDescription: "Describe qué corresponde a esta propiedad…",
5646
+ addOptionDescription: "¿Cuándo debe usarse esta opción?",
5632
5647
  propertyName: "Nombre de la propiedad",
5633
5648
  readOnly: "Sólo lectura",
5634
5649
  remove: "Quitar",
@@ -5971,6 +5986,12 @@ const editorMessagesByLocale = {
5971
5986
  pasteFileOrMediaLink: "Coller un fichier ou un lien multimédia",
5972
5987
  personOrEmail: "Personne ou email",
5973
5988
  propertyMenuFor: "Menu Propriétés pour {{name}}",
5989
+ description: "Description",
5990
+ addPageDescription: "Ajoutez une description de cette page…",
5991
+ addDatabaseDescription:
5992
+ "Ajoutez une description de cette base de données…",
5993
+ addPropertyDescription: "Décrivez ce qui appartient à cette propriété…",
5994
+ addOptionDescription: "Quand cette option doit-elle être utilisée ?",
5974
5995
  propertyName: "Nom de la propriété",
5975
5996
  readOnly: "Lecture seule",
5976
5997
  remove: "Supprimer",
@@ -6313,6 +6334,12 @@ const editorMessagesByLocale = {
6313
6334
  pasteFileOrMediaLink: "Datei- oder Medienlink einfügen",
6314
6335
  personOrEmail: "Person oder E-Mail",
6315
6336
  propertyMenuFor: "Eigenschaftenmenü für {{name}}",
6337
+ description: "Beschreibung",
6338
+ addPageDescription: "Beschreibung dieser Seite hinzufügen…",
6339
+ addDatabaseDescription: "Beschreibung dieser Datenbank hinzufügen…",
6340
+ addPropertyDescription:
6341
+ "Beschreiben Sie, was in diese Eigenschaft gehört…",
6342
+ addOptionDescription: "Wann sollte diese Option verwendet werden?",
6316
6343
  propertyName: "Eigenschaftsname",
6317
6344
  readOnly: "Schreibgeschützt",
6318
6345
  remove: "Entfernen",
@@ -6655,6 +6682,11 @@ const editorMessagesByLocale = {
6655
6682
  pasteFileOrMediaLink: "ファイルまたはメディアのリンクを貼り付けます",
6656
6683
  personOrEmail: "個人または電子メール",
6657
6684
  propertyMenuFor: "{{name}}のプロパティメニュー",
6685
+ description: "説明",
6686
+ addPageDescription: "このページの説明を追加…",
6687
+ addDatabaseDescription: "このデータベースの説明を追加…",
6688
+ addPropertyDescription: "このプロパティに入る内容を説明…",
6689
+ addOptionDescription: "このオプションはいつ使いますか?",
6658
6690
  propertyName: "プロパティ名",
6659
6691
  readOnly: "読み取り専用",
6660
6692
  remove: "削除",
@@ -6990,6 +7022,11 @@ const editorMessagesByLocale = {
6990
7022
  pasteFileOrMediaLink: "파일 또는 미디어 링크 붙여넣기",
6991
7023
  personOrEmail: "개인 또는 이메일",
6992
7024
  propertyMenuFor: "{{name}}의 속성 메뉴",
7025
+ description: "설명",
7026
+ addPageDescription: "이 페이지에 대한 설명 추가…",
7027
+ addDatabaseDescription: "이 데이터베이스에 대한 설명 추가…",
7028
+ addPropertyDescription: "이 속성에 들어갈 내용을 설명…",
7029
+ addOptionDescription: "이 옵션은 언제 사용해야 하나요?",
6993
7030
  propertyName: "속성 이름",
6994
7031
  readOnly: "읽기 전용",
6995
7032
  remove: "제거",
@@ -7328,6 +7365,11 @@ const editorMessagesByLocale = {
7328
7365
  pasteFileOrMediaLink: "Colar arquivo ou link de mídia",
7329
7366
  personOrEmail: "Pessoa ou e-mail",
7330
7367
  propertyMenuFor: "Menu de propriedades para {{name}}",
7368
+ description: "Descrição",
7369
+ addPageDescription: "Adicione uma descrição desta página…",
7370
+ addDatabaseDescription: "Adicione uma descrição deste banco de dados…",
7371
+ addPropertyDescription: "Descreva o que pertence a esta propriedade…",
7372
+ addOptionDescription: "Quando esta opção deve ser usada?",
7331
7373
  propertyName: "Nome da propriedade",
7332
7374
  readOnly: "Somente leitura",
7333
7375
  remove: "Remover",
@@ -7665,6 +7707,11 @@ const editorMessagesByLocale = {
7665
7707
  pasteFileOrMediaLink: "फ़ाइल या मीडिया लिंक चिपकाएँ",
7666
7708
  personOrEmail: "व्यक्ति या ईमेल",
7667
7709
  propertyMenuFor: "{{name}} के लिए संपत्ति मेनू",
7710
+ description: "विवरण",
7711
+ addPageDescription: "इस पेज का विवरण जोड़ें…",
7712
+ addDatabaseDescription: "इस डेटाबेस का विवरण जोड़ें…",
7713
+ addPropertyDescription: "बताएं कि इस प्रॉपर्टी में क्या होना चाहिए…",
7714
+ addOptionDescription: "इस विकल्प का उपयोग कब होना चाहिए?",
7668
7715
  propertyName: "संपत्ति का नाम",
7669
7716
  readOnly: "केवल पढ़ने योग्य",
7670
7717
  remove: "हटाओ",
@@ -7996,6 +8043,11 @@ const editorMessagesByLocale = {
7996
8043
  pasteFileOrMediaLink: "الصق الملف أو رابط الوسائط",
7997
8044
  personOrEmail: "الشخص أو البريد الإلكتروني",
7998
8045
  propertyMenuFor: "قائمة الخصائص لـ {{name}}",
8046
+ description: "الوصف",
8047
+ addPageDescription: "أضف وصفًا لهذه الصفحة…",
8048
+ addDatabaseDescription: "أضف وصفًا لقاعدة البيانات هذه…",
8049
+ addPropertyDescription: "صف ما الذي ينتمي إلى هذه الخاصية…",
8050
+ addOptionDescription: "متى يجب استخدام هذا الخيار؟",
7999
8051
  propertyName: "اسم العقار",
8000
8052
  readOnly: "للقراءة فقط",
8001
8053
  remove: "إزالة",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Pages and databases can describe themselves, with guidance for properties and select options.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Slack follow-ups now retain created Content identity and person-field intent across corrections.