@agent-native/core 0.114.0 → 0.114.2

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 (223) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/create.ts +23 -0
  5. package/corpus/core/src/cli/skills.ts +24 -0
  6. package/corpus/core/src/client/build-compatibility.ts +77 -0
  7. package/corpus/core/src/client/route-chunk-recovery/index.ts +1 -0
  8. package/corpus/core/src/client/use-action.ts +32 -1
  9. package/corpus/core/src/deploy/build.ts +4 -1
  10. package/corpus/core/src/org/context.ts +56 -30
  11. package/corpus/core/src/server/action-routes.ts +52 -2
  12. package/corpus/core/src/server/auth-plugin.ts +9 -4
  13. package/corpus/core/src/server/better-auth-instance.ts +5 -1
  14. package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
  15. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  16. package/corpus/core/src/templates/default/AGENTS.md +4 -1
  17. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  18. package/corpus/core/src/templates/headless/AGENTS.md +5 -1
  19. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
  20. package/corpus/core/src/templates/workspace-core/AGENTS.md +3 -1
  21. package/corpus/core/src/templates/workspace-root/AGENTS.md +3 -1
  22. package/corpus/core/src/vite/client.ts +17 -0
  23. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  24. package/corpus/templates/analytics/AGENTS.md +5 -0
  25. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  26. package/corpus/templates/assets/AGENTS.md +5 -0
  27. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  28. package/corpus/templates/brain/AGENTS.md +5 -0
  29. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  30. package/corpus/templates/calendar/AGENTS.md +5 -0
  31. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  32. package/corpus/templates/chat/AGENTS.md +4 -1
  33. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  34. package/corpus/templates/clips/AGENTS.md +5 -0
  35. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  36. package/corpus/templates/content/AGENTS.md +7 -0
  37. package/corpus/templates/content/actions/_content-database-personal-view.ts +49 -5
  38. package/corpus/templates/content/actions/_content-document-access.ts +21 -0
  39. package/corpus/templates/content/actions/_content-favorites.ts +91 -0
  40. package/corpus/templates/content/actions/_content-files.ts +79 -23
  41. package/corpus/templates/content/actions/_content-sidebar-state.ts +24 -0
  42. package/corpus/templates/content/actions/_content-space-access.ts +15 -17
  43. package/corpus/templates/content/actions/_content-space-catalog-guards.ts +20 -0
  44. package/corpus/templates/content/actions/_content-spaces.ts +364 -167
  45. package/corpus/templates/content/actions/_database-source-utils.ts +8 -0
  46. package/corpus/templates/content/actions/_database-utils.ts +249 -15
  47. package/corpus/templates/content/actions/_delete-content-space.ts +84 -0
  48. package/corpus/templates/content/actions/_files-system-properties.ts +499 -0
  49. package/corpus/templates/content/actions/_local-file-documents.ts +0 -1
  50. package/corpus/templates/content/actions/_property-utils.ts +18 -9
  51. package/corpus/templates/content/actions/bind-content-database-source-field.ts +3 -0
  52. package/corpus/templates/content/actions/configure-document-property.ts +3 -0
  53. package/corpus/templates/content/actions/create-content-database.ts +2 -2
  54. package/corpus/templates/content/actions/create-content-space.ts +34 -0
  55. package/corpus/templates/content/actions/create-document.ts +4 -1
  56. package/corpus/templates/content/actions/delete-content-space.ts +19 -0
  57. package/corpus/templates/content/actions/delete-database-items.ts +28 -1
  58. package/corpus/templates/content/actions/delete-document-property.ts +3 -0
  59. package/corpus/templates/content/actions/delete-document.ts +39 -2
  60. package/corpus/templates/content/actions/duplicate-database-item.ts +6 -0
  61. package/corpus/templates/content/actions/duplicate-database-items.ts +6 -0
  62. package/corpus/templates/content/actions/duplicate-document-property.ts +3 -0
  63. package/corpus/templates/content/actions/get-content-database.ts +11 -2
  64. package/corpus/templates/content/actions/get-content-sidebar-state.ts +23 -0
  65. package/corpus/templates/content/actions/get-document.ts +32 -7
  66. package/corpus/templates/content/actions/import-content-source.ts +33 -1
  67. package/corpus/templates/content/actions/list-content-spaces.ts +34 -2
  68. package/corpus/templates/content/actions/list-documents.ts +39 -7
  69. package/corpus/templates/content/actions/reorder-document-property.ts +9 -0
  70. package/corpus/templates/content/actions/set-document-property.ts +41 -23
  71. package/corpus/templates/content/actions/share-local-file-document.ts +15 -0
  72. package/corpus/templates/content/actions/submit-content-database-form.ts +7 -0
  73. package/corpus/templates/content/actions/sync-local-folder-source.ts +14 -0
  74. package/corpus/templates/content/actions/update-content-database-view.ts +1 -0
  75. package/corpus/templates/content/actions/update-content-sidebar-state.ts +27 -0
  76. package/corpus/templates/content/actions/update-document.ts +158 -66
  77. package/corpus/templates/content/app/components/EmptyState.tsx +4 -57
  78. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +8 -1
  79. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +519 -178
  80. package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +39 -0
  81. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
  82. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +266 -7
  83. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +430 -152
  84. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +467 -66
  85. package/corpus/templates/content/app/components/editor/database/view-config.ts +9 -3
  86. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +720 -196
  87. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +173 -1
  88. package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +1 -1
  89. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +91 -16
  90. package/corpus/templates/content/app/entry.client.tsx +5 -1
  91. package/corpus/templates/content/app/hooks/use-content-database.ts +84 -3
  92. package/corpus/templates/content/app/hooks/use-content-spaces.ts +60 -2
  93. package/corpus/templates/content/app/hooks/use-create-page.ts +30 -0
  94. package/corpus/templates/content/app/hooks/use-documents.ts +270 -19
  95. package/corpus/templates/content/app/i18n-data.ts +243 -20
  96. package/corpus/templates/content/app/lib/local-folder-picker-safety.ts +54 -0
  97. package/corpus/templates/content/app/lib/local-folder-picker-support.ts +18 -0
  98. package/corpus/templates/content/app/routes/_app.favorites.tsx +30 -0
  99. package/corpus/templates/content/app/routes/_app.local-files.tsx +18 -6
  100. package/corpus/templates/content/changelog/2026-07-17-workspace-files-now-stay-in-a-loading-state-until-automatic-.md +6 -0
  101. package/corpus/templates/content/changelog/2026-07-17-workspace-switching-now-opens-the-selected-files-database-pr.md +6 -0
  102. package/corpus/templates/content/changelog/2026-07-17-workspaces-now-expand-above-their-own-files-list-without-dup.md +6 -0
  103. package/corpus/templates/content/changelog/2026-07-18-create-named-workspaces-each-with-its-own-private-files-data.md +6 -0
  104. package/corpus/templates/content/changelog/2026-07-18-favorites-align-with-workspace-and-file-rows-in-the-sidebar.md +6 -0
  105. package/corpus/templates/content/changelog/2026-07-18-local-folder-selection-no-longer-invokes-the-unsafe-native-p.md +6 -0
  106. package/corpus/templates/content/changelog/2026-07-18-made-content-workspaces-independently-expandable-restored-or.md +6 -0
  107. package/corpus/templates/content/changelog/2026-07-18-page-details-now-live-in-a-shared-info-and-comments-rail-dat.md +6 -0
  108. package/corpus/templates/content/changelog/2026-07-18-workspace-database-views-now-control-sidebar-workspace-navig.md +6 -0
  109. package/corpus/templates/content/changelog/2026-07-18-workspace-navigation-now-aligns-favorites-with-workspace-pag.md +6 -0
  110. package/corpus/templates/content/changelog/2026-07-19-favorites-now-open-as-a-table-and-compact-breadcrumbs-make-w.md +6 -0
  111. package/corpus/templates/content/changelog/2026-07-19-files-databases-now-start-unfiltered-sidebar-rows-exactly-fo.md +6 -0
  112. package/corpus/templates/content/changelog/2026-07-19-sidebar-page-trees-now-open-only-when-expanded-remember-thei.md +6 -0
  113. package/corpus/templates/content/changelog/2026-07-19-workspace-file-databases-now-use-the-workspace-name-open-as-.md +6 -0
  114. package/corpus/templates/content/changelog/2026-07-19-workspace-navigation-now-stays-stable-on-hover-uses-folder-i.md +6 -0
  115. package/corpus/templates/content/changelog/2026-07-19-workspace-rows-now-use-folder-icons-and-deleting-a-user-crea.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-19-workspaces-can-be-created-without-leaving-the-workspaces-dat.md +6 -0
  117. package/corpus/templates/content/e2e/global-setup.ts +3 -2
  118. package/corpus/templates/content/parity/matrix.md +28 -28
  119. package/corpus/templates/content/parity/matrix.ts +8 -2
  120. package/corpus/templates/content/server/db/index.ts +1 -0
  121. package/corpus/templates/content/server/db/schema.ts +10 -0
  122. package/corpus/templates/content/server/lib/documents.ts +12 -7
  123. package/corpus/templates/content/server/plugins/db.ts +17 -0
  124. package/corpus/templates/content/shared/api.ts +9 -0
  125. package/corpus/templates/content/shared/database-form.ts +11 -2
  126. package/corpus/templates/content/vite.config.ts +1 -0
  127. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  128. package/corpus/templates/design/AGENTS.md +5 -0
  129. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  130. package/corpus/templates/dispatch/AGENTS.md +5 -0
  131. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  132. package/corpus/templates/forms/AGENTS.md +5 -0
  133. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  134. package/corpus/templates/macros/AGENTS.md +5 -0
  135. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  136. package/corpus/templates/mail/AGENTS.md +5 -0
  137. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  138. package/corpus/templates/plan/AGENTS.md +5 -0
  139. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  140. package/corpus/templates/slides/AGENTS.md +5 -0
  141. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  142. package/corpus/templates/tasks/AGENTS.md +5 -0
  143. package/dist/cli/create.d.ts.map +1 -1
  144. package/dist/cli/create.js +21 -0
  145. package/dist/cli/create.js.map +1 -1
  146. package/dist/cli/skills.d.ts.map +1 -1
  147. package/dist/cli/skills.js +19 -0
  148. package/dist/cli/skills.js.map +1 -1
  149. package/dist/client/build-compatibility.d.ts +11 -0
  150. package/dist/client/build-compatibility.d.ts.map +1 -0
  151. package/dist/client/build-compatibility.js +56 -0
  152. package/dist/client/build-compatibility.js.map +1 -0
  153. package/dist/client/route-chunk-recovery/index.d.ts +1 -0
  154. package/dist/client/route-chunk-recovery/index.d.ts.map +1 -1
  155. package/dist/client/route-chunk-recovery/index.js +1 -0
  156. package/dist/client/route-chunk-recovery/index.js.map +1 -1
  157. package/dist/client/use-action.d.ts.map +1 -1
  158. package/dist/client/use-action.js +19 -1
  159. package/dist/client/use-action.js.map +1 -1
  160. package/dist/collab/routes.d.ts +1 -1
  161. package/dist/deploy/build.js +2 -1
  162. package/dist/deploy/build.js.map +1 -1
  163. package/dist/notifications/routes.d.ts +1 -1
  164. package/dist/observability/routes.d.ts +3 -3
  165. package/dist/org/context.d.ts +4 -2
  166. package/dist/org/context.d.ts.map +1 -1
  167. package/dist/org/context.js +44 -22
  168. package/dist/org/context.js.map +1 -1
  169. package/dist/progress/routes.d.ts +1 -1
  170. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  171. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  172. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  173. package/dist/resources/handlers.d.ts +1 -1
  174. package/dist/server/action-routes.d.ts.map +1 -1
  175. package/dist/server/action-routes.js +32 -2
  176. package/dist/server/action-routes.js.map +1 -1
  177. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  178. package/dist/server/auth-plugin.d.ts.map +1 -1
  179. package/dist/server/auth-plugin.js +9 -5
  180. package/dist/server/auth-plugin.js.map +1 -1
  181. package/dist/server/better-auth-instance.d.ts.map +1 -1
  182. package/dist/server/better-auth-instance.js +4 -1
  183. package/dist/server/better-auth-instance.js.map +1 -1
  184. package/dist/server/transcribe-voice.d.ts +1 -1
  185. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  186. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  187. package/dist/shared/mcp-embed-headers.js +1 -1
  188. package/dist/shared/mcp-embed-headers.js.map +1 -1
  189. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  190. package/dist/templates/chat/AGENTS.md +4 -1
  191. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  192. package/dist/templates/default/AGENTS.md +4 -1
  193. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  194. package/dist/templates/headless/AGENTS.md +5 -1
  195. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
  196. package/dist/templates/workspace-core/AGENTS.md +3 -1
  197. package/dist/templates/workspace-root/AGENTS.md +3 -1
  198. package/dist/vite/client.d.ts +6 -0
  199. package/dist/vite/client.d.ts.map +1 -1
  200. package/dist/vite/client.js +8 -0
  201. package/dist/vite/client.js.map +1 -1
  202. package/package.json +1 -1
  203. package/src/cli/create.ts +23 -0
  204. package/src/cli/skills.ts +24 -0
  205. package/src/client/build-compatibility.ts +77 -0
  206. package/src/client/route-chunk-recovery/index.ts +1 -0
  207. package/src/client/use-action.ts +32 -1
  208. package/src/deploy/build.ts +4 -1
  209. package/src/org/context.ts +56 -30
  210. package/src/server/action-routes.ts +52 -2
  211. package/src/server/auth-plugin.ts +9 -4
  212. package/src/server/better-auth-instance.ts +5 -1
  213. package/src/shared/mcp-embed-headers.ts +1 -1
  214. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  215. package/src/templates/chat/AGENTS.md +4 -1
  216. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  217. package/src/templates/default/AGENTS.md +4 -1
  218. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +133 -0
  219. package/src/templates/headless/AGENTS.md +5 -1
  220. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +26 -0
  221. package/src/templates/workspace-core/AGENTS.md +3 -1
  222. package/src/templates/workspace-root/AGENTS.md +3 -1
  223. package/src/vite/client.ts +17 -0
@@ -0,0 +1,499 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { accessFilter } from "@agent-native/core/sharing";
4
+ import { and, eq, inArray, isNull, lt, or } from "drizzle-orm";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import type {
8
+ ContentDatabaseViewConfig,
9
+ DocumentProperty,
10
+ DocumentPropertyDefinition,
11
+ DocumentPropertyOption,
12
+ DocumentPropertySystemRole,
13
+ DocumentPropertyValue,
14
+ } from "../shared/api.js";
15
+ import {
16
+ parsePropertyOptions,
17
+ serializePropertyOptions,
18
+ } from "../shared/properties.js";
19
+ import {
20
+ defaultDatabaseViewConfig,
21
+ parseDatabaseViewConfig,
22
+ serializeDatabaseViewConfig,
23
+ } from "./_property-utils.js";
24
+
25
+ type Db = Pick<ReturnType<typeof getDb>, "select" | "insert" | "update">;
26
+ type Database = typeof schema.contentDatabases.$inferSelect;
27
+ type Document = Pick<
28
+ typeof schema.documents.$inferSelect,
29
+ "id" | "spaceId" | "parentId" | "ownerEmail" | "orgId"
30
+ >;
31
+
32
+ const FILES_SYSTEM_PROPERTIES_VERSION = 2;
33
+
34
+ const SYSTEM_PROPERTY_SPECS = [
35
+ {
36
+ role: "files_parent" as const,
37
+ name: "Parent",
38
+ type: "select" as const,
39
+ description: "The authorized parent page or database for this file.",
40
+ position: -200,
41
+ },
42
+ {
43
+ role: "files_source" as const,
44
+ name: "Source",
45
+ type: "multi_select" as const,
46
+ description: "The source system that supplies this file.",
47
+ position: -100,
48
+ },
49
+ ] as const;
50
+
51
+ function systemPropertyId(
52
+ databaseId: string,
53
+ role: DocumentPropertySystemRole,
54
+ ) {
55
+ return `content_files_property_${createHash("sha256")
56
+ .update(`${databaseId}:${role}`)
57
+ .digest("hex")
58
+ .slice(0, 32)}`;
59
+ }
60
+
61
+ function legacyFilesKindPropertyId(databaseId: string) {
62
+ return systemPropertyId(databaseId, "files_kind");
63
+ }
64
+
65
+ export function filesParentPropertyId(databaseId: string) {
66
+ return systemPropertyId(databaseId, "files_parent");
67
+ }
68
+
69
+ function legacyFilesRootFilter(databaseId: string) {
70
+ return {
71
+ key: filesParentPropertyId(databaseId),
72
+ label: "Parent",
73
+ operator: "is_empty" as const,
74
+ value: "",
75
+ };
76
+ }
77
+
78
+ export function defaultFilesDatabaseViewConfig(
79
+ _databaseId: string,
80
+ ): ContentDatabaseViewConfig {
81
+ return defaultDatabaseViewConfig("table");
82
+ }
83
+
84
+ export function migrateFilesDatabaseViewConfig(
85
+ config: ContentDatabaseViewConfig,
86
+ databaseId: string,
87
+ options: { removeLegacyRootFilter?: boolean } = {},
88
+ ): ContentDatabaseViewConfig {
89
+ const legacyKindPropertyId = legacyFilesKindPropertyId(databaseId);
90
+ const legacyRootFilter = legacyFilesRootFilter(databaseId);
91
+ const migrateFilters = (
92
+ filters: ContentDatabaseViewConfig["filters"],
93
+ ): ContentDatabaseViewConfig["filters"] =>
94
+ filters.flatMap((filter) => {
95
+ if (
96
+ options.removeLegacyRootFilter &&
97
+ filter.key === legacyRootFilter.key &&
98
+ filter.operator === legacyRootFilter.operator &&
99
+ filter.value === legacyRootFilter.value
100
+ ) {
101
+ return [];
102
+ }
103
+ if (filter.key !== legacyKindPropertyId) return [filter];
104
+ if (
105
+ filter.operator === "does_not_equal" &&
106
+ filter.value === "database_row"
107
+ ) {
108
+ return [];
109
+ }
110
+ return [];
111
+ });
112
+ return {
113
+ ...config,
114
+ filters: migrateFilters(config.filters),
115
+ views: config.views.map((view) => ({
116
+ ...view,
117
+ filters: migrateFilters(view.filters),
118
+ })),
119
+ };
120
+ }
121
+
122
+ function storedOptions(role: DocumentPropertySystemRole) {
123
+ if (role === "files_source") {
124
+ return {
125
+ options: [{ id: "local", name: "Content", color: "gray" as const }],
126
+ };
127
+ }
128
+ return { options: [] };
129
+ }
130
+
131
+ export async function ensureFilesSystemPropertyDefinitions(args: {
132
+ database: Database;
133
+ db?: Db;
134
+ now?: string;
135
+ }) {
136
+ if (args.database.systemRole !== "files") return;
137
+ const db = args.db ?? getDb();
138
+ const now = args.now ?? new Date().toISOString();
139
+ const existingDefinitions = await db
140
+ .select()
141
+ .from(schema.documentPropertyDefinitions)
142
+ .where(eq(schema.documentPropertyDefinitions.databaseId, args.database.id));
143
+ const existingSourceSystemProperty = existingDefinitions.find(
144
+ (definition) => definition.systemRole === "files_source",
145
+ );
146
+ const sourceIds = (
147
+ await db
148
+ .select({ id: schema.contentDatabaseSources.id })
149
+ .from(schema.contentDatabaseSources)
150
+ .where(eq(schema.contentDatabaseSources.databaseId, args.database.id))
151
+ ).map((source) => source.id);
152
+ const legacySourceOptionIds = [...sourceIds, "local"].sort();
153
+ const legacySourceProperty = existingDefinitions.find((definition) => {
154
+ if (
155
+ definition.systemRole ||
156
+ definition.name !== "Source" ||
157
+ (definition.type !== "select" && definition.type !== "multi_select")
158
+ ) {
159
+ return false;
160
+ }
161
+ const optionIds = (
162
+ parsePropertyOptions(definition.optionsJson).options ?? []
163
+ )
164
+ .map((option) => option.id)
165
+ .sort();
166
+ return (
167
+ sourceIds.length >= 2 &&
168
+ optionIds.length === legacySourceOptionIds.length &&
169
+ optionIds.every((id, index) => id === legacySourceOptionIds[index])
170
+ );
171
+ });
172
+ const adoptedLegacySource =
173
+ !existingSourceSystemProperty && Boolean(legacySourceProperty);
174
+ if (adoptedLegacySource && legacySourceProperty) {
175
+ await db
176
+ .update(schema.documentPropertyDefinitions)
177
+ .set({
178
+ systemRole: "files_source",
179
+ type: "multi_select",
180
+ updatedAt: now,
181
+ })
182
+ .where(
183
+ eq(schema.documentPropertyDefinitions.id, legacySourceProperty.id),
184
+ );
185
+ }
186
+ const existingRoles = new Set(
187
+ existingDefinitions
188
+ .map((definition) => definition.systemRole)
189
+ .filter((role): role is string => Boolean(role)),
190
+ );
191
+ if (legacySourceProperty && !existingSourceSystemProperty) {
192
+ existingRoles.add("files_source");
193
+ }
194
+ const missingDefinitions = SYSTEM_PROPERTY_SPECS.filter(
195
+ (spec) => !existingRoles.has(spec.role),
196
+ ).map((spec) => ({
197
+ id: systemPropertyId(args.database.id, spec.role),
198
+ ownerEmail: args.database.ownerEmail,
199
+ orgId: args.database.orgId,
200
+ databaseId: args.database.id,
201
+ systemRole: spec.role,
202
+ name: spec.name,
203
+ type: spec.type,
204
+ description: spec.description,
205
+ visibility: "always_show",
206
+ optionsJson: serializePropertyOptions(storedOptions(spec.role)),
207
+ position: spec.position,
208
+ createdAt: now,
209
+ updatedAt: now,
210
+ }));
211
+ if (missingDefinitions.length > 0) {
212
+ await db
213
+ .insert(schema.documentPropertyDefinitions)
214
+ .values(missingDefinitions)
215
+ .onConflictDoNothing();
216
+ }
217
+ const parsedStored = parseDatabaseViewConfig(args.database.viewConfigJson);
218
+ const normalizedStored = serializeDatabaseViewConfig(parsedStored);
219
+ const migratedStored = serializeDatabaseViewConfig(
220
+ migrateFilesDatabaseViewConfig(parsedStored, args.database.id, {
221
+ removeLegacyRootFilter:
222
+ args.database.filesSystemPropertiesSeeded <
223
+ FILES_SYSTEM_PROPERTIES_VERSION,
224
+ }),
225
+ );
226
+ const untouchedLegacyDefaults = new Set([
227
+ serializeDatabaseViewConfig(defaultDatabaseViewConfig("sidebar")),
228
+ serializeDatabaseViewConfig(defaultDatabaseViewConfig("table")),
229
+ ]);
230
+ const viewConfigJson = untouchedLegacyDefaults.has(normalizedStored)
231
+ ? serializeDatabaseViewConfig(
232
+ defaultFilesDatabaseViewConfig(args.database.id),
233
+ )
234
+ : migratedStored !== normalizedStored
235
+ ? migratedStored
236
+ : args.database.viewConfigJson;
237
+ if (
238
+ missingDefinitions.length === 0 &&
239
+ !adoptedLegacySource &&
240
+ viewConfigJson === args.database.viewConfigJson &&
241
+ args.database.filesSystemPropertiesSeeded ===
242
+ FILES_SYSTEM_PROPERTIES_VERSION
243
+ ) {
244
+ return;
245
+ }
246
+ await db
247
+ .update(schema.contentDatabases)
248
+ .set({
249
+ viewConfigJson,
250
+ filesSystemPropertiesSeeded: FILES_SYSTEM_PROPERTIES_VERSION,
251
+ updatedAt: now,
252
+ })
253
+ .where(eq(schema.contentDatabases.id, args.database.id));
254
+ }
255
+
256
+ export async function repairFilesSystemPropertyDefinitions() {
257
+ const db = getDb();
258
+ const databases = await db
259
+ .select()
260
+ .from(schema.contentDatabases)
261
+ .where(
262
+ and(
263
+ eq(schema.contentDatabases.systemRole, "files"),
264
+ lt(
265
+ schema.contentDatabases.filesSystemPropertiesSeeded,
266
+ FILES_SYSTEM_PROPERTIES_VERSION,
267
+ ),
268
+ isNull(schema.contentDatabases.deletedAt),
269
+ ),
270
+ );
271
+ for (const database of databases) {
272
+ await db.transaction((tx) =>
273
+ ensureFilesSystemPropertyDefinitions({ database, db: tx }),
274
+ );
275
+ }
276
+ return databases.length;
277
+ }
278
+
279
+ function safeOption(
280
+ id: string,
281
+ name: string,
282
+ color: DocumentPropertyOption["color"] = "gray",
283
+ ): DocumentPropertyOption {
284
+ return { id, name: name.trim() || "Untitled", color };
285
+ }
286
+
287
+ function filesVisibilityFilter(database: Database) {
288
+ return database.orgId
289
+ ? and(
290
+ eq(schema.documents.spaceId, database.spaceId!),
291
+ eq(schema.documents.orgId, database.orgId),
292
+ or(
293
+ eq(schema.documents.visibility, "org"),
294
+ eq(schema.documents.visibility, "public"),
295
+ ),
296
+ or(
297
+ eq(schema.documents.hideFromSearch, 0),
298
+ isNull(schema.documents.hideFromSearch),
299
+ ),
300
+ )
301
+ : and(
302
+ eq(schema.documents.spaceId, database.spaceId!),
303
+ eq(schema.documents.ownerEmail, database.ownerEmail),
304
+ );
305
+ }
306
+
307
+ async function readableDatabaseIds(
308
+ db: Db,
309
+ databaseIds: string[],
310
+ options: { includeSystem?: boolean } = {},
311
+ ) {
312
+ if (databaseIds.length === 0) return new Set<string>();
313
+ const databases = await db
314
+ .select({
315
+ id: schema.contentDatabases.id,
316
+ documentId: schema.contentDatabases.documentId,
317
+ })
318
+ .from(schema.contentDatabases)
319
+ .where(
320
+ and(
321
+ inArray(schema.contentDatabases.id, databaseIds),
322
+ isNull(schema.contentDatabases.deletedAt),
323
+ options.includeSystem === false
324
+ ? isNull(schema.contentDatabases.systemRole)
325
+ : undefined,
326
+ ),
327
+ );
328
+ if (databases.length === 0) return new Set<string>();
329
+ const readableDocuments = await db
330
+ .select({ id: schema.documents.id })
331
+ .from(schema.documents)
332
+ .where(
333
+ and(
334
+ inArray(
335
+ schema.documents.id,
336
+ databases.map((database) => database.documentId),
337
+ ),
338
+ accessFilter(schema.documents, schema.documentShares),
339
+ ),
340
+ );
341
+ const readableDocumentIds = new Set(readableDocuments.map((row) => row.id));
342
+ return new Set(
343
+ databases
344
+ .filter((database) => readableDocumentIds.has(database.documentId))
345
+ .map((database) => database.id),
346
+ );
347
+ }
348
+
349
+ export async function filesSystemPropertyProjection(args: {
350
+ database: Database;
351
+ documents: Document[];
352
+ properties: DocumentProperty[];
353
+ }) {
354
+ const systemProperties = args.properties.filter((property) =>
355
+ SYSTEM_PROPERTY_SPECS.some(
356
+ (spec) => spec.role === property.definition.systemRole,
357
+ ),
358
+ );
359
+ if (args.database.systemRole !== "files" || systemProperties.length === 0) {
360
+ return null;
361
+ }
362
+ const db = getDb();
363
+ const documentIds = args.documents.map((document) => document.id);
364
+ const parentIds = [
365
+ ...new Set(
366
+ args.documents
367
+ .map((document) => document.parentId)
368
+ .filter((id): id is string => Boolean(id)),
369
+ ),
370
+ ];
371
+ const parents = parentIds.length
372
+ ? await db
373
+ .select({ id: schema.documents.id, title: schema.documents.title })
374
+ .from(schema.documents)
375
+ .where(
376
+ and(
377
+ inArray(schema.documents.id, parentIds),
378
+ filesVisibilityFilter(args.database),
379
+ accessFilter(schema.documents, schema.documentShares),
380
+ ),
381
+ )
382
+ : [];
383
+ const parentById = new Map(parents.map((parent) => [parent.id, parent]));
384
+
385
+ const sourceRows = documentIds.length
386
+ ? await db
387
+ .select({
388
+ documentId: schema.contentDatabaseSourceRows.documentId,
389
+ sourceId: schema.contentDatabaseSourceRows.sourceId,
390
+ })
391
+ .from(schema.contentDatabaseSourceRows)
392
+ .where(
393
+ inArray(schema.contentDatabaseSourceRows.documentId, documentIds),
394
+ )
395
+ : [];
396
+ const sourceIds = [...new Set(sourceRows.map((row) => row.sourceId))];
397
+ const sources = sourceIds.length
398
+ ? await db
399
+ .select({
400
+ id: schema.contentDatabaseSources.id,
401
+ databaseId: schema.contentDatabaseSources.databaseId,
402
+ sourceName: schema.contentDatabaseSources.sourceName,
403
+ })
404
+ .from(schema.contentDatabaseSources)
405
+ .where(inArray(schema.contentDatabaseSources.id, sourceIds))
406
+ : [];
407
+ const readableSourceDatabaseIds = await readableDatabaseIds(
408
+ db,
409
+ sources
410
+ .map((source) => source.databaseId)
411
+ .filter((databaseId) => databaseId !== args.database.id),
412
+ );
413
+ readableSourceDatabaseIds.add(args.database.id);
414
+ const readableSourceById = new Map(
415
+ sources
416
+ .filter((source) => readableSourceDatabaseIds.has(source.databaseId))
417
+ .map((source) => [source.id, source]),
418
+ );
419
+ const sourceIdsByDocument = new Map<string, string[]>();
420
+ for (const row of sourceRows) {
421
+ if (!readableSourceById.has(row.sourceId)) continue;
422
+ sourceIdsByDocument.set(row.documentId, [
423
+ ...(sourceIdsByDocument.get(row.documentId) ?? []),
424
+ row.sourceId,
425
+ ]);
426
+ }
427
+
428
+ const parentOptions = parents.map((parent) =>
429
+ safeOption(parent.id, parent.title, "blue"),
430
+ );
431
+ const sourceOptions = [
432
+ safeOption("local", "Content"),
433
+ ...[...readableSourceById.values()].map((source) =>
434
+ safeOption(source.id, source.sourceName, "green"),
435
+ ),
436
+ ];
437
+ const definitionByRole = new Map<
438
+ DocumentPropertySystemRole,
439
+ DocumentPropertyDefinition
440
+ >();
441
+ for (const property of systemProperties) {
442
+ const role = property.definition.systemRole!;
443
+ definitionByRole.set(role, {
444
+ ...property.definition,
445
+ options: {
446
+ options: role === "files_parent" ? parentOptions : sourceOptions,
447
+ },
448
+ });
449
+ }
450
+
451
+ const valuesByDocumentId = new Map<
452
+ string,
453
+ Map<DocumentPropertySystemRole, DocumentPropertyValue>
454
+ >();
455
+ for (const document of args.documents) {
456
+ const sourceValues = [
457
+ ...new Set(sourceIdsByDocument.get(document.id) ?? []),
458
+ ];
459
+ valuesByDocumentId.set(
460
+ document.id,
461
+ new Map<DocumentPropertySystemRole, DocumentPropertyValue>([
462
+ [
463
+ "files_parent",
464
+ document.parentId && parentById.has(document.parentId)
465
+ ? document.parentId
466
+ : null,
467
+ ],
468
+ ["files_source", sourceValues.length > 0 ? sourceValues : ["local"]],
469
+ ]),
470
+ );
471
+ }
472
+ return { definitionByRole, valuesByDocumentId };
473
+ }
474
+
475
+ export function applyFilesSystemPropertyProjection(args: {
476
+ properties: DocumentProperty[];
477
+ projection: NonNullable<
478
+ Awaited<ReturnType<typeof filesSystemPropertyProjection>>
479
+ >;
480
+ documentId?: string;
481
+ }) {
482
+ return args.properties
483
+ .filter((property) => property.definition.systemRole !== "files_kind")
484
+ .map((property) => {
485
+ const role = property.definition.systemRole;
486
+ if (!role) return property;
487
+ return {
488
+ ...property,
489
+ definition:
490
+ args.projection.definitionByRole.get(role) ?? property.definition,
491
+ value: args.documentId
492
+ ? (args.projection.valuesByDocumentId
493
+ .get(args.documentId)
494
+ ?.get(role) ?? null)
495
+ : null,
496
+ editable: false,
497
+ };
498
+ });
499
+ }
@@ -234,7 +234,6 @@ function localFileDocumentsCacheScope() {
234
234
  return JSON.stringify({
235
235
  manifest: process.env.AGENT_NATIVE_MANIFEST,
236
236
  manifestPath: process.env.AGENT_NATIVE_MANIFEST_PATH,
237
- dataMode: process.env.AGENT_NATIVE_DATA_MODE,
238
237
  });
239
238
  }
240
239
 
@@ -159,6 +159,7 @@ export function serializeDatabase(
159
159
  id: database.id,
160
160
  documentId: database.documentId,
161
161
  title: database.title,
162
+ systemRole: database.systemRole,
162
163
  description,
163
164
  viewConfig: parseDatabaseViewConfig(database.viewConfigJson),
164
165
  createdAt: database.createdAt,
@@ -260,10 +261,8 @@ function defaultDatabaseView(
260
261
  ? "Timeline"
261
262
  : type === "form"
262
263
  ? "Form"
263
- : type === "sidebar"
264
- ? "Sidebar"
265
- : "Table",
266
- type,
264
+ : "Table",
265
+ type: type === "sidebar" ? "table" : type,
267
266
  sorts: values.sorts ?? [],
268
267
  filters: values.filters ?? [],
269
268
  filterMode: normalizeDatabaseFilterMode(values.filterMode),
@@ -287,21 +286,23 @@ function normalizeDatabaseView(value: unknown): ContentDatabaseView | null {
287
286
  if (!value || typeof value !== "object") return null;
288
287
  const view = value as Partial<ContentDatabaseView>;
289
288
  if (typeof view.id !== "string" || !view.id.trim()) return null;
289
+ const retiredSidebar = view.type === "sidebar";
290
290
  const type =
291
291
  view.type === "board" ||
292
292
  view.type === "list" ||
293
293
  view.type === "gallery" ||
294
294
  view.type === "calendar" ||
295
295
  view.type === "timeline" ||
296
- view.type === "form" ||
297
- view.type === "sidebar"
296
+ view.type === "form"
298
297
  ? view.type
299
298
  : "table";
300
299
  return {
301
300
  id: view.id,
302
301
  name:
303
302
  typeof view.name === "string" && view.name.trim()
304
- ? view.name.trim()
303
+ ? retiredSidebar && view.name.trim() === "Sidebar"
304
+ ? "Table"
305
+ : view.name.trim()
305
306
  : defaultDatabaseView({}, type).name,
306
307
  type,
307
308
  sorts: Array.isArray(view.sorts) ? view.sorts.filter(isDatabaseSort) : [],
@@ -507,6 +508,9 @@ export async function listPropertiesForDatabase(
507
508
  definition: {
508
509
  id: definition.id,
509
510
  databaseId: definition.databaseId,
511
+ systemRole: definition.systemRole as
512
+ | import("../shared/api.js").DocumentPropertySystemRole
513
+ | null,
510
514
  name: definition.name,
511
515
  type,
512
516
  description: definition.description,
@@ -530,7 +534,7 @@ export async function listPropertiesForDatabase(
530
534
  blockFieldContent: blockContentByPropertyId.get(definition.id),
531
535
  })
532
536
  : parsePropertyValue(storedValue?.valueJson),
533
- editable: !isComputedPropertyType(type),
537
+ editable: !definition.systemRole && !isComputedPropertyType(type),
534
538
  };
535
539
  });
536
540
 
@@ -576,6 +580,9 @@ function serializePropertyDefinition(
576
580
  return {
577
581
  id: definition.id,
578
582
  databaseId: definition.databaseId,
583
+ systemRole: definition.systemRole as
584
+ | import("../shared/api.js").DocumentPropertySystemRole
585
+ | null,
579
586
  name: definition.name,
580
587
  type,
581
588
  description: definition.description,
@@ -700,7 +707,9 @@ export async function listPropertiesForDatabaseDocuments(
700
707
  ),
701
708
  })
702
709
  : parsePropertyValue(storedValue?.valueJson),
703
- editable: !isComputedPropertyType(propertyDefinition.type),
710
+ editable:
711
+ !definition.systemRole &&
712
+ !isComputedPropertyType(propertyDefinition.type),
704
713
  };
705
714
  });
706
715
 
@@ -121,6 +121,9 @@ export default defineAction({
121
121
  if (!property) {
122
122
  throw new Error("Target column does not belong to this database.");
123
123
  }
124
+ if (property.systemRole) {
125
+ throw new Error("System properties cannot be bound to source fields.");
126
+ }
124
127
  // The auto-created "Source" tag is internal row-tagging, never a writable
125
128
  // bind target.
126
129
  if (
@@ -118,6 +118,9 @@ export default defineAction({
118
118
  ),
119
119
  );
120
120
  if (!existing) throw new Error(`Property "${args.id}" not found`);
121
+ if (existing.systemRole) {
122
+ throw new Error("System properties cannot be changed.");
123
+ }
121
124
  if (
122
125
  isComputedPropertyType(existing.type as DocumentPropertyType) &&
123
126
  existing.type !== type
@@ -160,7 +160,7 @@ export async function resolveContentDatabaseSpace(
160
160
  if (!userEmail) throw new Error("no authenticated user");
161
161
  const provisioned = await provisionContentSpaces(db, userEmail);
162
162
  const spaceId = args.spaceId ?? provisioned.personalSpaceId;
163
- await resolveContentSpaceAccess(spaceId, "editor");
163
+ await resolveContentSpaceAccess(spaceId, "contributor");
164
164
  return spaceId;
165
165
  }
166
166
 
@@ -288,7 +288,7 @@ export async function createContentDatabaseRecord(
288
288
  }
289
289
  const spaceAccess = await (
290
290
  options.resolveSpaceAccess ?? resolveContentSpaceAccess
291
- )(spaceId, "editor", { db });
291
+ )(spaceId, "contributor", { db });
292
292
  if (spaceAccess.space.id !== spaceId) {
293
293
  throw new Error("Resolved Content space does not match the request");
294
294
  }
@@ -0,0 +1,34 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { getRequestUserEmail } from "@agent-native/core/server/request-context";
3
+ import { z } from "zod";
4
+
5
+ import { getDb } from "../server/db/index.js";
6
+ import {
7
+ provisionUserContentSpace,
8
+ systemIdsForContentSpace,
9
+ } from "./_content-spaces.js";
10
+
11
+ export default defineAction({
12
+ description:
13
+ "Create a private Content workspace with its own canonical Files database.",
14
+ schema: z.object({
15
+ name: z.string().trim().min(1).max(200),
16
+ requestId: z.string().trim().min(1).max(200),
17
+ propertyValues: z.record(z.string(), z.unknown()).optional(),
18
+ }),
19
+ run: async ({ name, requestId, propertyValues }) => {
20
+ const userEmail = getRequestUserEmail();
21
+ if (!userEmail) throw new Error("no authenticated user");
22
+ const result = await provisionUserContentSpace(getDb(), userEmail, {
23
+ workspaceId: requestId,
24
+ name,
25
+ propertyValues,
26
+ });
27
+ return {
28
+ ...result,
29
+ filesDocumentId: systemIdsForContentSpace(result.spaceId, "files")
30
+ .documentId,
31
+ kind: "user" as const,
32
+ };
33
+ },
34
+ });
@@ -211,7 +211,10 @@ export default defineAction({
211
211
  } else {
212
212
  const provisioned = await provisionContentSpaces(db, currentUserEmail);
213
213
  spaceId = args.spaceId ?? provisioned.personalSpaceId;
214
- const spaceAccess = await resolveContentSpaceAccess(spaceId, "editor");
214
+ const spaceAccess = await resolveContentSpaceAccess(
215
+ spaceId,
216
+ "contributor",
217
+ );
215
218
  ownerEmail = currentUserEmail;
216
219
  orgId = spaceAccess.space.orgId;
217
220
  visibility = orgId ? "org" : "private";
@@ -0,0 +1,19 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { writeAppState } from "@agent-native/core/application-state";
3
+ import { z } from "zod";
4
+
5
+ import { getDb } from "../server/db/index.js";
6
+ import { deleteUserContentSpace } from "./_delete-content-space.js";
7
+
8
+ export default defineAction({
9
+ description:
10
+ "Permanently delete a user-created Content workspace and all content inside it. Personal and organization workspaces cannot be deleted.",
11
+ schema: z.object({
12
+ spaceId: z.string().trim().min(1),
13
+ }),
14
+ run: async ({ spaceId }) => {
15
+ const result = await deleteUserContentSpace(getDb(), spaceId);
16
+ await writeAppState("refresh-signal", { ts: Date.now() });
17
+ return { success: true, ...result };
18
+ },
19
+ });