@agent-native/core 0.154.4 → 0.155.0

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 (207) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
  3. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
  4. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
  5. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
  6. package/corpus/templates/analytics/AGENTS.md +3 -0
  7. package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
  8. package/corpus/templates/analytics/actions/bigquery.ts +1 -1
  9. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
  10. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  11. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
  12. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
  13. package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
  14. package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
  17. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  18. package/corpus/templates/calendar/AGENTS.md +2 -0
  19. package/corpus/templates/calendar/DEVELOPING.md +4 -4
  20. package/corpus/templates/calendar/actions/get-settings.ts +4 -11
  21. package/corpus/templates/calendar/actions/update-availability.ts +15 -4
  22. package/corpus/templates/calendar/actions/update-settings.ts +15 -4
  23. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
  24. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
  25. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
  26. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
  27. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
  28. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
  29. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
  30. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
  31. package/corpus/templates/calendar/app/i18n-data.ts +30 -0
  32. package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
  33. package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
  34. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
  35. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
  36. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
  37. package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
  38. package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
  39. package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
  40. package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
  41. package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
  42. package/corpus/templates/calendar/shared/api.ts +1 -0
  43. package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
  44. package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
  45. package/corpus/templates/calendar/shared/settings.ts +44 -0
  46. package/corpus/templates/clips/AGENTS.md +2 -0
  47. package/corpus/templates/clips/actions/add-comment.ts +5 -2
  48. package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
  49. package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
  50. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
  51. package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
  52. package/corpus/templates/clips/actions/update-comment.ts +9 -2
  53. package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
  54. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
  55. package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
  56. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
  57. package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
  58. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
  59. package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
  60. package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
  61. package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
  62. package/corpus/templates/clips/app/root.tsx +2 -1
  63. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
  64. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
  65. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
  66. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +14 -7
  67. package/corpus/templates/clips/app/routes/share.$shareId.tsx +8 -4
  68. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
  69. package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
  70. package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
  71. package/corpus/templates/clips/desktop/src/app.tsx +116 -19
  72. package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
  73. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
  74. package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
  75. package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
  76. package/corpus/templates/clips/desktop/src/styles.css +117 -0
  77. package/corpus/templates/clips/server/db/index.ts +10 -0
  78. package/corpus/templates/clips/server/db/schema.ts +1 -0
  79. package/corpus/templates/clips/server/lib/recordings.ts +3 -1
  80. package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
  81. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  82. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
  83. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
  84. package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
  85. package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
  86. package/corpus/templates/content/.agents/skills/content/SKILL.md +2 -2
  87. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +20 -4
  88. package/corpus/templates/content/actions/_database-row-mutation.ts +1490 -0
  89. package/corpus/templates/content/actions/_database-utils.ts +44 -0
  90. package/corpus/templates/content/actions/_property-utils.ts +4 -1
  91. package/corpus/templates/content/actions/add-comment.ts +2 -1
  92. package/corpus/templates/content/actions/add-database-item.ts +62 -240
  93. package/corpus/templates/content/actions/configure-document-property.ts +165 -1
  94. package/corpus/templates/content/actions/delete-document-property.ts +18 -0
  95. package/corpus/templates/content/actions/set-document-property.ts +78 -1
  96. package/corpus/templates/content/actions/update-comment.ts +1 -1
  97. package/corpus/templates/content/actions/update-database-item.ts +62 -0
  98. package/corpus/templates/content/actions/upsert-database-item-by-key.ts +43 -796
  99. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
  100. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +10 -8
  101. package/corpus/templates/content/app/hooks/use-content-database.ts +33 -32
  102. package/corpus/templates/content/changelog/2026-08-10-database-row-actions-now-validate-exact-schemas-and-safe-ret.md +6 -0
  103. package/corpus/templates/content/parity/matrix.md +1 -1
  104. package/corpus/templates/content/parity/matrix.ts +2 -0
  105. package/corpus/templates/content/server/db/schema.ts +39 -3
  106. package/corpus/templates/content/server/plugins/db.ts +30 -0
  107. package/corpus/templates/content/shared/api.ts +75 -2
  108. package/corpus/templates/design/actions/generate-design.ts +10 -7
  109. package/corpus/templates/design/actions/present-design-variants.ts +35 -6
  110. package/corpus/templates/design/actions/run-design-audit.ts +33 -0
  111. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
  112. package/corpus/templates/design/shared/design-review.ts +1 -0
  113. package/corpus/templates/design/shared/html-integrity.ts +66 -0
  114. package/corpus/templates/dispatch/.env.example +5 -0
  115. package/corpus/templates/dispatch/AGENTS.md +24 -19
  116. package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
  117. package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
  118. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
  119. package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
  120. package/corpus/templates/factory/AGENTS.md +5 -1
  121. package/corpus/templates/factory/README.md +23 -0
  122. package/corpus/templates/factory/actions/view-screen.ts +17 -0
  123. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
  124. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
  125. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
  126. package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
  127. package/corpus/templates/factory/app/routes/factory.tsx +41 -0
  128. package/corpus/templates/factory/package.json +1 -0
  129. package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
  130. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
  131. package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
  132. package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
  133. package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
  134. package/corpus/templates/mail/vite.config.ts +7 -0
  135. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
  136. package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
  137. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  138. package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
  139. package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
  140. package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
  141. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
  142. package/dist/action.d.ts +22 -0
  143. package/dist/action.js +27 -0
  144. package/dist/agent/production-agent.js +5 -1
  145. package/dist/agent/thread-data-builder.js +17 -1
  146. package/dist/cli/skills-content/help.js +6 -4
  147. package/dist/cli/skills.js +17 -2
  148. package/dist/cli/workspacify.d.ts +5 -0
  149. package/dist/cli/workspacify.js +65 -8
  150. package/dist/client/AgentPanel.d.ts +5 -0
  151. package/dist/client/AgentPanel.js +20 -4
  152. package/dist/client/AssistantChat.d.ts +21 -0
  153. package/dist/client/AssistantChat.js +66 -68
  154. package/dist/client/active-run-state.d.ts +14 -1
  155. package/dist/client/active-run-state.js +0 -0
  156. package/dist/client/agent-chat-adapter.js +107 -5
  157. package/dist/client/analytics.js +9 -2
  158. package/dist/client/chat/tool-call-display.js +4 -4
  159. package/dist/client/chat-first/apps-rail.js +9 -4
  160. package/dist/client/chat-first/primary-nav.d.ts +2 -1
  161. package/dist/client/chat-first/primary-nav.js +3 -3
  162. package/dist/client/chat-first/types.d.ts +5 -1
  163. package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
  164. package/dist/client/markdown/InlineMarkdown.js +65 -0
  165. package/dist/client/markdown/index.d.ts +1 -0
  166. package/dist/client/markdown/index.js +1 -0
  167. package/dist/client/review/ReviewThreadPanel.js +2 -1
  168. package/dist/client/run-stream-ownership.d.ts +39 -0
  169. package/dist/client/run-stream-ownership.js +65 -0
  170. package/dist/collab/routes.d.ts +1 -1
  171. package/dist/collab/struct-routes.d.ts +1 -1
  172. package/dist/feature-flags/index.d.ts +1 -1
  173. package/dist/feature-flags/index.js +1 -1
  174. package/dist/feature-flags/store.d.ts +10 -0
  175. package/dist/feature-flags/store.js +112 -1
  176. package/dist/guards/no-env-credentials.js +4 -0
  177. package/dist/index.browser.d.ts +1 -1
  178. package/dist/index.browser.js +1 -1
  179. package/dist/index.d.ts +2 -2
  180. package/dist/index.js +2 -2
  181. package/dist/integrations/adapters/slack.js +5 -1
  182. package/dist/localization/default-messages.d.ts +1 -0
  183. package/dist/localization/default-messages.js +1 -0
  184. package/dist/notifications/routes.d.ts +1 -1
  185. package/dist/observability/routes.d.ts +1 -1
  186. package/dist/progress/routes.d.ts +1 -1
  187. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  188. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  189. package/dist/resources/agents.js +40 -5
  190. package/dist/resources/handlers.d.ts +1 -1
  191. package/dist/resources/metadata.d.ts +10 -0
  192. package/dist/resources/metadata.js +7 -3
  193. package/dist/review/actions/create-review-comment.js +1 -1
  194. package/dist/review/actions/reply-review-comment.js +1 -1
  195. package/dist/review/actions/resolve-review-thread.js +1 -1
  196. package/dist/server/action-routes.js +42 -8
  197. package/dist/server/agent-discovery.js +14 -3
  198. package/dist/server/core-routes-plugin.js +1 -1
  199. package/dist/server/identity-sso-migrations.d.ts +7 -0
  200. package/dist/server/identity-sso-migrations.js +36 -0
  201. package/dist/server/identity-sso-store.d.ts +3 -2
  202. package/dist/server/identity-sso-store.js +8 -3
  203. package/dist/server/realtime-token.d.ts +1 -1
  204. package/dist/server/release-migrations.js +4 -0
  205. package/dist/tracking/providers.js +13 -3
  206. package/dist/workspace-files/tool.js +1 -1
  207. package/package.json +3 -2
@@ -6,7 +6,11 @@
6
6
  * of that share and is omitted unless the meeting owner enables it.
7
7
  */
8
8
 
9
- import { getSession, runWithRequestContext } from "@agent-native/core/server";
9
+ import {
10
+ getSession,
11
+ runWithRequestContext,
12
+ verifyScopedAgentAccessToken,
13
+ } from "@agent-native/core/server";
10
14
  import { resolveAccess } from "@agent-native/core/sharing";
11
15
  import { eq } from "drizzle-orm";
12
16
  import {
@@ -16,6 +20,10 @@ import {
16
20
  setResponseStatus,
17
21
  } from "h3";
18
22
 
23
+ import {
24
+ CLIPS_MEETING_AGENT_ACCESS_PARAM,
25
+ CLIPS_MEETING_AGENT_RESOURCE_KIND,
26
+ } from "../../../shared/meeting-agent-access.js";
19
27
  import {
20
28
  normalizeTranscriptSegments,
21
29
  parseTranscriptSegments,
@@ -43,17 +51,31 @@ function parseBullets(raw: string | null | undefined): Bullet[] {
43
51
  }
44
52
  }
45
53
 
54
+ function queryString(value: unknown): string {
55
+ if (typeof value === "string") return value;
56
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
57
+ return "";
58
+ }
59
+
46
60
  export default defineEventHandler(async (event) => {
47
61
  setResponseHeader(event, "Cache-Control", "private, max-age=0, no-store");
48
62
  setResponseHeader(event, "Referrer-Policy", "no-referrer");
49
63
 
50
64
  const query = getQuery(event);
51
- const meetingId = typeof query.id === "string" ? query.id : "";
65
+ const meetingId = queryString(query.id);
52
66
  if (!meetingId) {
53
67
  setResponseStatus(event, 400);
54
68
  return { error: "id is required" };
55
69
  }
56
70
 
71
+ const agentAccessToken = queryString(query[CLIPS_MEETING_AGENT_ACCESS_PARAM]);
72
+ const tokenAccess = agentAccessToken
73
+ ? verifyScopedAgentAccessToken(agentAccessToken, {
74
+ resourceKind: CLIPS_MEETING_AGENT_RESOURCE_KIND,
75
+ resourceId: meetingId,
76
+ })
77
+ : null;
78
+
57
79
  const session = await getSession(event).catch(() => null);
58
80
  const accessContext = {
59
81
  userEmail: session?.email,
@@ -61,14 +83,25 @@ export default defineEventHandler(async (event) => {
61
83
  };
62
84
 
63
85
  return runWithRequestContext(accessContext, async () => {
64
- const access = await resolveAccess("meeting", meetingId, accessContext);
86
+ let db: ReturnType<typeof getDb> | undefined;
87
+ const access = tokenAccess?.ok
88
+ ? await (async () => {
89
+ db = getDb();
90
+ const [resource] = await db
91
+ .select()
92
+ .from(schema.meetings)
93
+ .where(eq(schema.meetings.id, meetingId))
94
+ .limit(1);
95
+ return resource ? { role: "viewer" as const, resource } : null;
96
+ })()
97
+ : await resolveAccess("meeting", meetingId, accessContext);
65
98
  const meeting = access?.resource;
66
99
  if (!meeting || meeting.trashedAt) {
67
100
  setResponseStatus(event, 404);
68
101
  return { error: "Not found" };
69
102
  }
70
103
 
71
- const db = getDb();
104
+ db ??= getDb();
72
105
  const [participants, actionItems, transcriptRows] = await Promise.all([
73
106
  db
74
107
  .select({
@@ -58,6 +58,7 @@ import {
58
58
  parseSpaceIds,
59
59
  type RecordingVisibility,
60
60
  } from "../../lib/recordings.js";
61
+ import { isSeekableRepairPending } from "../../lib/seekable-media-state.js";
61
62
  import { verifySharePassword } from "../../lib/share-password.js";
62
63
 
63
64
  function appPath(path: string): string {
@@ -406,11 +407,19 @@ export default defineEventHandler(async (event) => {
406
407
  // Referer of any outbound link the share page renders.
407
408
  setResponseHeader(event, "Referrer-Policy", "no-referrer");
408
409
  const transcriptPresentation = resolveTranscriptPresentation(transcript);
409
- const verificationPending = await isMediaVerificationPending({
410
- ownerEmail: rec.ownerEmail,
411
- recordingId,
412
- recordingStatus: rec.status,
413
- });
410
+ const [verificationPending, seekableRepairPending] = await Promise.all([
411
+ isMediaVerificationPending({
412
+ ownerEmail: rec.ownerEmail,
413
+ recordingId,
414
+ recordingStatus: rec.status,
415
+ }),
416
+ isSeekableRepairPending({
417
+ ownerEmail: rec.ownerEmail,
418
+ recordingId,
419
+ recordingStatus: rec.status,
420
+ videoUrl: rec.videoUrl,
421
+ }),
422
+ ]);
414
423
 
415
424
  const [viewCount, agentViewCount] = await Promise.all([
416
425
  countRecordingViews(recordingId),
@@ -454,12 +463,14 @@ export default defineEventHandler(async (event) => {
454
463
  videoUrl: playbackVideoUrl,
455
464
  videoFormat: rec.videoFormat,
456
465
  videoSizeBytes: rec.videoSizeBytes ?? null,
466
+ mediaUpdatedAt: rec.mediaUpdatedAt,
457
467
  width: rec.width,
458
468
  height: rec.height,
459
469
  hasAudio: Boolean(rec.hasAudio),
460
470
  hasCamera: Boolean(rec.hasCamera),
461
471
  status: rec.status,
462
472
  verificationPending,
473
+ seekableRepairPending,
463
474
  uploadProgress: rec.uploadProgress,
464
475
  failureReason: rec.failureReason,
465
476
  // Don't leak the password to clients; just indicate whether one was set.
@@ -37,6 +37,9 @@ export type ClipsUserPrefs = ClipsAiPrefs & {
37
37
  /** Visibility applied to new recordings unless the creator picks another. */
38
38
  export type ClipsDefaultVisibility = "private" | "org" | "public";
39
39
 
40
+ export const DEFAULT_CLIPS_RECORDING_VISIBILITY: ClipsDefaultVisibility =
41
+ "public";
42
+
40
43
  export function isIncludeFullVideoInAiEnabled(
41
44
  prefs: ClipsAiPrefs | Record<string, unknown> | null | undefined,
42
45
  ): boolean {
@@ -0,0 +1,5 @@
1
+ import { AGENT_ACCESS_PARAM } from "@agent-native/core/shared";
2
+
3
+ export const CLIPS_MEETING_AGENT_RESOURCE_KIND = "clips:meeting";
4
+ export const CLIPS_MEETING_AGENT_CONTEXT_ENDPOINT = "/api/public-meeting";
5
+ export const CLIPS_MEETING_AGENT_ACCESS_PARAM = AGENT_ACCESS_PARAM;
@@ -121,8 +121,8 @@ For a named intake workflow:
121
121
  when the database has no form contract and all required values have already
122
122
  been confirmed.
123
123
  8. Treat submission as complete only when the successful result includes a
124
- `createdDocumentId` and verification. Return the exact `url` or `urlPath`
125
- from the result. The canonical Content row route is `/page/<createdDocumentId>`;
124
+ stable row IDs and verified read-back. Return the exact `url` or `urlPath`
125
+ from the result. The canonical Content row route is `/page/<documentId>`;
126
126
  never invent a different path, slug, ID, or host.
127
127
 
128
128
  When the user supplies a complete description in one message, do not force a
@@ -117,7 +117,9 @@ checkbox filters as initial property values, resolving option labels back to
117
117
  stable option IDs for select, status, and multi-select filters, so a row
118
118
  created under "Status is Published" remains visible instead of immediately
119
119
  disappearing. Agents can mirror that behavior by passing
120
- `--propertyValues '{"propertyId":"value"}'` to `add-database-item`. Filter
120
+ the discovered property IDs in `propertyValues` to `add-database-item`. The
121
+ action also requires the exact space/database/backing-page target, current
122
+ schema revision, and a caller-stable idempotency key. Filter
121
123
  controls are type-aware: option properties choose from their configured
122
124
  options, option value editors can search existing options or create a new
123
125
  option from the typed query, and property settings can rename option labels
@@ -168,8 +170,10 @@ memberships, but they are references: moving one never reparents, transfers,
168
170
  or changes access to the referenced page. Files sidebar Custom order is
169
171
  different again: persist it per user and per database view with
170
172
  `update-content-database-personal-view`, without changing the shared Files
171
- membership order. Creating a database row returns the created item IDs and
172
- opens the new row page in the side preview. Duplicating a database row
173
+ membership order. Creating a database row returns a receipt with stable item
174
+ and document IDs, row link, revisions, affected fields, idempotency outcome,
175
+ and verified read-back, then opens the new row page in the side preview.
176
+ Duplicating a database row
173
177
  returns the duplicate item IDs and opens the copied row in the side preview
174
178
  so users can continue editing the new page immediately, including from
175
179
  table, list, and gallery row action menus. Board, calendar, and timeline
@@ -227,13 +231,25 @@ property definition.
227
231
 
228
232
  Use `create-content-database`, `create-inline-content-database`,
229
233
  `get-content-database`, `list-trashed-content-databases`,
230
- `restore-content-database`, `add-database-item`, `duplicate-database-item`,
234
+ `restore-content-database`, `add-database-item`, `update-database-item`,
235
+ `upsert-database-item-by-key`, `duplicate-database-item`,
231
236
  `duplicate-database-items`, `remove-database-items`, `move-database-item`,
232
237
  `update-content-database-view`, `list-document-properties`,
233
238
  `configure-document-property`, `set-document-property`,
234
239
  `duplicate-document-property`, and `delete-document-property`; do not edit
235
240
  property rows or view config via raw SQL when an action can do it.
236
241
 
242
+ Read `get-content-database.mutationContract` immediately before a single-row
243
+ mutation. Pass its exact target and schema revision to create, exact item and
244
+ document IDs plus the current row revision to sparse update, and a fresh
245
+ idempotency key for each intended effect. Reusing the same key with the same
246
+ payload replays the durable receipt; reusing it with a different payload fails.
247
+ Configure at most one ordinary text property as the database's natural key,
248
+ then use `upsert-database-item-by-key` with that property. Natural-key upsert
249
+ never accepts an arbitrary property name or silently chooses a field. Blocks,
250
+ computed, system, source-managed, unknown, and relation properties are not
251
+ writable through these actions; use their owning surfaces instead.
252
+
237
253
  For a bounded migration that must rewrite every existing row body while adding
238
254
  new property definitions and values, use `migrate-content-database-rows` rather
239
255
  than looping the single-row actions. Its `validate` phase is read-only; `apply`