@agent-native/core 0.101.6 → 0.101.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/deployment.mdx +9 -7
  4. package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +9 -7
  5. package/corpus/core/docs/content/locales/de-DE/deployment.mdx +9 -7
  6. package/corpus/core/docs/content/locales/es-ES/deployment.mdx +9 -7
  7. package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +9 -7
  8. package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +9 -7
  9. package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +9 -7
  10. package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +9 -7
  11. package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +9 -7
  12. package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +9 -7
  13. package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +9 -7
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/a2a/artifact-response.ts +255 -12
  16. package/corpus/core/src/agent/thread-data-builder.ts +84 -11
  17. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +9 -1
  18. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +17 -2
  19. package/corpus/core/src/client/settings/SettingsPanel.tsx +41 -21
  20. package/corpus/core/src/client/settings/useBuilderStatus.ts +28 -9
  21. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +2 -7
  22. package/corpus/core/src/integrations/a2a-continuation-processor.ts +99 -7
  23. package/corpus/core/src/integrations/adapters/discord.ts +3 -1
  24. package/corpus/core/src/integrations/adapters/email.ts +4 -1
  25. package/corpus/core/src/integrations/adapters/google-docs.ts +4 -1
  26. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +3 -1
  27. package/corpus/core/src/integrations/adapters/slack.ts +38 -9
  28. package/corpus/core/src/integrations/adapters/telegram.ts +32 -10
  29. package/corpus/core/src/integrations/adapters/whatsapp.ts +5 -1
  30. package/corpus/core/src/integrations/index.ts +1 -0
  31. package/corpus/core/src/integrations/types.ts +9 -2
  32. package/corpus/core/src/integrations/webhook-handler.ts +69 -45
  33. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +1 -0
  34. package/corpus/core/src/server/auth.ts +25 -1
  35. package/corpus/core/src/server/builder-browser.ts +353 -1
  36. package/corpus/core/src/server/core-routes-plugin.ts +601 -223
  37. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +80 -1
  38. package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +28 -3
  39. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +5 -1
  40. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +30 -3
  41. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +196 -0
  42. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +115 -1
  43. package/corpus/templates/calendar/app/lib/out-of-office.ts +45 -0
  44. package/corpus/templates/calendar/changelog/2026-07-14-out-of-office-blocks-now-sit-behind-meetings-and-event-stack.md +6 -0
  45. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  46. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +35 -21
  47. package/corpus/templates/content/AGENTS.md +32 -9
  48. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +87 -0
  49. package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -25
  50. package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
  51. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +427 -18
  52. package/corpus/templates/content/actions/_builder-cms-write-client.ts +17 -73
  53. package/corpus/templates/content/actions/_builder-cms-write-settings.ts +27 -7
  54. package/corpus/templates/content/actions/_builder-source-execution-claim.ts +124 -0
  55. package/corpus/templates/content/actions/_builder-source-execution-preservation.ts +21 -0
  56. package/corpus/templates/content/actions/_builder-source-timings.ts +57 -0
  57. package/corpus/templates/content/actions/_database-source-utils.ts +1071 -170
  58. package/corpus/templates/content/actions/_database-utils.ts +104 -13
  59. package/corpus/templates/content/actions/_local-file-documents.ts +53 -1
  60. package/corpus/templates/content/actions/_preview-document-draft.ts +31 -0
  61. package/corpus/templates/content/actions/_property-utils.ts +7 -1
  62. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +309 -0
  63. package/corpus/templates/content/actions/configure-document-property.ts +11 -0
  64. package/corpus/templates/content/actions/create-content-database.ts +11 -0
  65. package/corpus/templates/content/actions/create-document.ts +9 -0
  66. package/corpus/templates/content/actions/create-inline-content-database.ts +3 -0
  67. package/corpus/templates/content/actions/execute-builder-source-batch.ts +120 -49
  68. package/corpus/templates/content/actions/execute-builder-source-execution.ts +608 -264
  69. package/corpus/templates/content/actions/export-content-source.ts +1 -0
  70. package/corpus/templates/content/actions/get-content-database.ts +13 -1
  71. package/corpus/templates/content/actions/get-document.ts +12 -2
  72. package/corpus/templates/content/actions/get-preview-document-draft.ts +26 -0
  73. package/corpus/templates/content/actions/import-content-source.ts +6 -0
  74. package/corpus/templates/content/actions/list-documents.ts +3 -0
  75. package/corpus/templates/content/actions/materialize-builder-required-fields.ts +550 -0
  76. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +112 -65
  77. package/corpus/templates/content/actions/prepare-builder-source-review.ts +463 -170
  78. package/corpus/templates/content/actions/preview-builder-source-review.ts +99 -0
  79. package/corpus/templates/content/actions/pull-document.ts +2 -0
  80. package/corpus/templates/content/actions/search-documents.ts +5 -1
  81. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +2 -2
  82. package/corpus/templates/content/actions/update-document.ts +16 -1
  83. package/corpus/templates/content/actions/update-preview-document-draft.ts +153 -0
  84. package/corpus/templates/content/actions/view-screen.ts +15 -2
  85. package/corpus/templates/content/app/components/editor/DescriptionField.tsx +121 -0
  86. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +14 -0
  87. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +250 -49
  88. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +57 -5
  89. package/corpus/templates/content/app/components/editor/body-hydration.ts +27 -0
  90. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2414 -589
  91. package/corpus/templates/content/app/components/editor/database/settings.tsx +13 -10
  92. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +358 -75
  93. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +124 -29
  94. package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +2 -0
  95. package/corpus/templates/content/app/global.css +18 -1
  96. package/corpus/templates/content/app/hooks/use-content-database.ts +68 -0
  97. package/corpus/templates/content/app/hooks/use-documents.ts +53 -4
  98. package/corpus/templates/content/app/i18n/zh-TW.ts +30 -0
  99. package/corpus/templates/content/app/i18n-data.ts +339 -0
  100. package/corpus/templates/content/changelog/2026-07-10-builder-backed-preview-edits-are-preserved-when-article-bodi.md +6 -0
  101. package/corpus/templates/content/changelog/2026-07-10-builder-cms-sources-can-now-be-enabled-for-guarded-staged-or.md +6 -0
  102. package/corpus/templates/content/changelog/2026-07-13-builder-connection-setup-is-now-available-directly-from-conn.md +6 -0
  103. package/corpus/templates/content/changelog/2026-07-13-builder-reviews-now-hide-already-applied-writes-detect-nativ.md +6 -0
  104. package/corpus/templates/content/changelog/2026-07-13-required-builder-publishing-fields-can-now-be-added-directly.md +6 -0
  105. package/corpus/templates/content/changelog/2026-07-13-you-can-now-cancel-a-prepared-builder-update-safely-before-i.md +6 -0
  106. package/corpus/templates/content/changelog/2026-07-14-builder-account-connection-now-remains-available-when-branch.md +6 -0
  107. package/corpus/templates/content/changelog/2026-07-14-builder-backed-articles-now-preserve-fifth-and-sixth-level-h.md +6 -0
  108. package/corpus/templates/content/changelog/2026-07-14-builder-sources-now-review-and-publish-rich-article-updates-.md +6 -0
  109. package/corpus/templates/content/changelog/2026-07-14-builder-writes-now-stop-retries-when-reconciliation-is-needed.md +6 -0
  110. package/corpus/templates/content/changelog/2026-07-14-file-and-media-links-now-validate-before-saving.md +6 -0
  111. package/corpus/templates/content/changelog/2026-07-14-pages-and-databases-can-describe-themselves-with-guidance-fo.md +6 -0
  112. package/corpus/templates/content/changelog/2026-07-14-slack-follow-ups-now-retain-created-content-identity-and-per.md +6 -0
  113. package/corpus/templates/content/parity/matrix.md +27 -25
  114. package/corpus/templates/content/parity/matrix.ts +50 -2
  115. package/corpus/templates/content/server/db/schema.ts +51 -0
  116. package/corpus/templates/content/server/lib/document-context.ts +76 -0
  117. package/corpus/templates/content/server/lib/public-documents.ts +3 -0
  118. package/corpus/templates/content/server/plugins/auth.ts +1 -0
  119. package/corpus/templates/content/server/plugins/db.ts +59 -0
  120. package/corpus/templates/content/server/routes/api/document-agent-context.json.get.ts +5 -0
  121. package/corpus/templates/content/shared/api.ts +87 -1
  122. package/corpus/templates/content/shared/builder-mdx.ts +705 -15
  123. package/corpus/templates/content/shared/content-source.ts +7 -0
  124. package/corpus/templates/content/shared/nfm.ts +3 -3
  125. package/corpus/templates/content/shared/properties.ts +2 -0
  126. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +52 -7
  127. package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +36 -4
  128. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
  129. package/corpus/templates/design/app/i18n/ar-SA.ts +1 -0
  130. package/corpus/templates/design/app/i18n/de-DE.ts +1 -0
  131. package/corpus/templates/design/app/i18n/en-US.ts +1 -0
  132. package/corpus/templates/design/app/i18n/es-ES.ts +1 -0
  133. package/corpus/templates/design/app/i18n/fr-FR.ts +1 -0
  134. package/corpus/templates/design/app/i18n/hi-IN.ts +1 -0
  135. package/corpus/templates/design/app/i18n/ja-JP.ts +1 -0
  136. package/corpus/templates/design/app/i18n/ko-KR.ts +1 -0
  137. package/corpus/templates/design/app/i18n/pt-BR.ts +1 -0
  138. package/corpus/templates/design/app/i18n/zh-CN.ts +1 -0
  139. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  140. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +8 -0
  141. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +11 -0
  142. package/corpus/templates/design/changelog/2026-07-14-reviewers-can-now-pin-comments-from-the-canvas-toolbar-targe.md +6 -0
  143. package/corpus/templates/design/changelog/2026-07-15-viewer-comment-controls-now-sit-inside-the-read-only-notice-.md +6 -0
  144. package/corpus/templates/design/shared/review-anchor.ts +48 -0
  145. package/corpus/templates/plan/server/plan-mdx.ts +53 -7
  146. package/dist/a2a/artifact-response.d.ts +15 -0
  147. package/dist/a2a/artifact-response.d.ts.map +1 -1
  148. package/dist/a2a/artifact-response.js +204 -5
  149. package/dist/a2a/artifact-response.js.map +1 -1
  150. package/dist/agent/thread-data-builder.d.ts +6 -0
  151. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  152. package/dist/agent/thread-data-builder.js +71 -8
  153. package/dist/agent/thread-data-builder.js.map +1 -1
  154. package/dist/client/review/ReviewCommentComposer.d.ts +2 -1
  155. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
  156. package/dist/client/review/ReviewCommentComposer.js +3 -3
  157. package/dist/client/review/ReviewCommentComposer.js.map +1 -1
  158. package/dist/client/review/ReviewThreadPanel.d.ts +9 -1
  159. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
  160. package/dist/client/review/ReviewThreadPanel.js +6 -4
  161. package/dist/client/review/ReviewThreadPanel.js.map +1 -1
  162. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  163. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  164. package/dist/client/settings/SettingsPanel.js +11 -4
  165. package/dist/client/settings/SettingsPanel.js.map +1 -1
  166. package/dist/client/settings/useBuilderStatus.d.ts +8 -4
  167. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  168. package/dist/client/settings/useBuilderStatus.js +19 -6
  169. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  170. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
  171. package/dist/client/setup-connections/BuilderConnectCard.js +2 -5
  172. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
  173. package/dist/collab/awareness.d.ts +2 -2
  174. package/dist/collab/awareness.d.ts.map +1 -1
  175. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  176. package/dist/integrations/a2a-continuation-processor.js +72 -6
  177. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  178. package/dist/integrations/adapters/discord.d.ts.map +1 -1
  179. package/dist/integrations/adapters/discord.js +1 -0
  180. package/dist/integrations/adapters/discord.js.map +1 -1
  181. package/dist/integrations/adapters/email.d.ts.map +1 -1
  182. package/dist/integrations/adapters/email.js +2 -0
  183. package/dist/integrations/adapters/email.js.map +1 -1
  184. package/dist/integrations/adapters/google-docs.d.ts.map +1 -1
  185. package/dist/integrations/adapters/google-docs.js +2 -0
  186. package/dist/integrations/adapters/google-docs.js.map +1 -1
  187. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -1
  188. package/dist/integrations/adapters/microsoft-teams.js +1 -0
  189. package/dist/integrations/adapters/microsoft-teams.js.map +1 -1
  190. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  191. package/dist/integrations/adapters/slack.js +21 -6
  192. package/dist/integrations/adapters/slack.js.map +1 -1
  193. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  194. package/dist/integrations/adapters/telegram.js +11 -2
  195. package/dist/integrations/adapters/telegram.js.map +1 -1
  196. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  197. package/dist/integrations/adapters/whatsapp.js +3 -0
  198. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  199. package/dist/integrations/index.d.ts +1 -1
  200. package/dist/integrations/index.d.ts.map +1 -1
  201. package/dist/integrations/index.js.map +1 -1
  202. package/dist/integrations/types.d.ts +8 -2
  203. package/dist/integrations/types.d.ts.map +1 -1
  204. package/dist/integrations/types.js.map +1 -1
  205. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  206. package/dist/integrations/webhook-handler.js +40 -43
  207. package/dist/integrations/webhook-handler.js.map +1 -1
  208. package/dist/observability/routes.d.ts +5 -5
  209. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  210. package/dist/secrets/routes.d.ts +9 -9
  211. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  212. package/dist/server/agent-chat/action-filters-a2a.js +1 -0
  213. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  214. package/dist/server/auth.d.ts +1 -0
  215. package/dist/server/auth.d.ts.map +1 -1
  216. package/dist/server/auth.js +19 -2
  217. package/dist/server/auth.js.map +1 -1
  218. package/dist/server/builder-browser.d.ts +78 -0
  219. package/dist/server/builder-browser.d.ts.map +1 -1
  220. package/dist/server/builder-browser.js +246 -1
  221. package/dist/server/builder-browser.js.map +1 -1
  222. package/dist/server/core-routes-plugin.d.ts +41 -0
  223. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  224. package/dist/server/core-routes-plugin.js +260 -14
  225. package/dist/server/core-routes-plugin.js.map +1 -1
  226. package/docs/content/deployment.mdx +9 -7
  227. package/docs/content/locales/ar-SA/deployment.mdx +9 -7
  228. package/docs/content/locales/de-DE/deployment.mdx +9 -7
  229. package/docs/content/locales/es-ES/deployment.mdx +9 -7
  230. package/docs/content/locales/fr-FR/deployment.mdx +9 -7
  231. package/docs/content/locales/hi-IN/deployment.mdx +9 -7
  232. package/docs/content/locales/ja-JP/deployment.mdx +9 -7
  233. package/docs/content/locales/ko-KR/deployment.mdx +9 -7
  234. package/docs/content/locales/pt-BR/deployment.mdx +9 -7
  235. package/docs/content/locales/zh-CN/deployment.mdx +9 -7
  236. package/docs/content/locales/zh-TW/deployment.mdx +9 -7
  237. package/package.json +2 -2
@@ -36,6 +36,12 @@ export default defineAction({
36
36
  .string()
37
37
  .describe("Document ID used to scope the property workspace"),
38
38
  name: z.string().min(1).describe("Property name"),
39
+ description: z
40
+ .string()
41
+ .optional()
42
+ .describe(
43
+ "Stable guidance describing what this property means and which value belongs here",
44
+ ),
39
45
  type: z.enum(CREATABLE_DOCUMENT_PROPERTY_TYPES).describe("Property type"),
40
46
  visibility: z
41
47
  .enum(DOCUMENT_PROPERTY_VISIBILITIES)
@@ -49,6 +55,7 @@ export default defineAction({
49
55
  id: z.string(),
50
56
  name: z.string(),
51
57
  color: z.string(),
58
+ description: z.string().optional(),
52
59
  }),
53
60
  )
54
61
  .optional(),
@@ -162,6 +169,9 @@ export default defineAction({
162
169
  .update(schema.documentPropertyDefinitions)
163
170
  .set({
164
171
  name,
172
+ ...(args.description === undefined
173
+ ? {}
174
+ : { description: args.description.trim() }),
165
175
  type,
166
176
  visibility:
167
177
  args.visibility === undefined
@@ -196,6 +206,7 @@ export default defineAction({
196
206
  orgId: document.orgId ?? null,
197
207
  databaseId: database.id,
198
208
  name,
209
+ description: args.description?.trim() ?? "",
199
210
  type,
200
211
  visibility: normalizePropertyVisibility(args.visibility),
201
212
  optionsJson,
@@ -28,6 +28,10 @@ const createContentDatabaseSchema = z.object({
28
28
  .nullish()
29
29
  .describe("Parent document for a new database page"),
30
30
  title: z.string().optional().describe("Database title"),
31
+ description: z
32
+ .string()
33
+ .optional()
34
+ .describe("Stable guidance describing what belongs in this database"),
31
35
  });
32
36
 
33
37
  export default defineAction({
@@ -110,6 +114,12 @@ export async function createContentDatabaseRecord(
110
114
  .set({ title, updatedAt: now })
111
115
  .where(eq(schema.documents.id, documentId));
112
116
  }
117
+ if (args.description !== undefined) {
118
+ await db
119
+ .update(schema.documents)
120
+ .set({ description: args.description.trim(), updatedAt: now })
121
+ .where(eq(schema.documents.id, documentId));
122
+ }
113
123
  } else {
114
124
  title = databaseTitleForPage(title);
115
125
  const parentId = args.parentId || null;
@@ -163,6 +173,7 @@ export async function createContentDatabaseRecord(
163
173
  parentId,
164
174
  title,
165
175
  content: "",
176
+ description: args.description?.trim() ?? "",
166
177
  icon: null,
167
178
  position: (maxPos?.max ?? -1) + 1,
168
179
  isFavorite: 0,
@@ -45,6 +45,12 @@ export default defineAction({
45
45
  .describe("Pre-generated document ID (for optimistic UI)"),
46
46
  title: z.string().describe("Document title"),
47
47
  content: z.string().optional().describe("Markdown content"),
48
+ description: z
49
+ .string()
50
+ .optional()
51
+ .describe(
52
+ "Stable guidance describing why this page exists and what belongs in it",
53
+ ),
48
54
  parentId: z.string().nullish().describe("Parent document ID for nesting"),
49
55
  icon: z.string().optional().describe("Emoji icon"),
50
56
  }),
@@ -78,6 +84,7 @@ export default defineAction({
78
84
  const title = args.title;
79
85
 
80
86
  let content = args.content || "";
87
+ const description = args.description?.trim() ?? "";
81
88
  // Strip leading H1 that duplicates the title
82
89
  if (title && content) {
83
90
  const h1Match = content.match(/^#\s+(.+?)(\r?\n|$)/);
@@ -154,6 +161,7 @@ export default defineAction({
154
161
  parentId,
155
162
  title,
156
163
  content,
164
+ description,
157
165
  icon,
158
166
  position,
159
167
  isFavorite: 0,
@@ -202,6 +210,7 @@ export default defineAction({
202
210
  parentId: doc.parentId,
203
211
  title: doc.title,
204
212
  content: doc.content,
213
+ description: doc.description,
205
214
  icon: doc.icon,
206
215
  position: doc.position,
207
216
  isFavorite: parseDocumentFavorite(doc.isFavorite),
@@ -22,10 +22,12 @@ export default defineAction({
22
22
  schema: z.object({
23
23
  hostDocumentId: z.string().describe("Host page document ID"),
24
24
  title: z.string().optional().describe("Database title"),
25
+ description: z.string().optional().describe("Stable database guidance"),
25
26
  }),
26
27
  run: async ({
27
28
  hostDocumentId,
28
29
  title,
30
+ description,
29
31
  }): Promise<CreateInlineDatabaseResponse> => {
30
32
  const db = getDb();
31
33
  const ownerBlockId = createInlineDatabaseBlockId();
@@ -37,6 +39,7 @@ export default defineAction({
37
39
  {
38
40
  parentId: hostDocumentId,
39
41
  title: databaseTitleForPage(title),
42
+ description,
40
43
  },
41
44
  { db: tx },
42
45
  );
@@ -10,6 +10,7 @@ import {
10
10
  type ExecuteBuilderSourceBatchResponse,
11
11
  type ExecuteBuilderSourceBatchTransition,
12
12
  } from "../shared/api.js";
13
+ import { createBuilderSourceTiming } from "./_builder-source-timings.js";
13
14
  import {
14
15
  getContentDatabaseSourceSnapshotForWrite,
15
16
  resolveDatabaseForSourceMutation,
@@ -63,25 +64,41 @@ function realBatchDeps(
63
64
  publicationTransition: transition?.publicationTransition,
64
65
  confirmUnpublish: transition?.confirmUnpublish,
65
66
  };
67
+ let preparationTimings;
66
68
  if (transition?.publicationTransition) {
67
- await prepareBuilderSourceExecution.run(executionArgs);
69
+ preparationTimings = (
70
+ await prepareBuilderSourceExecution.run(executionArgs)
71
+ ).timings;
68
72
  }
69
73
  try {
70
- await executeBuilderSourceExecutionWithDeps(
74
+ const response = await executeBuilderSourceExecutionWithDeps(
71
75
  executionArgs,
72
76
  realExecutionDeps(args.sourceId),
73
77
  );
78
+ return {
79
+ changeSetId,
80
+ status: "succeeded",
81
+ timings: [...(preparationTimings ?? []), ...(response.timings ?? [])],
82
+ };
74
83
  } catch (error) {
75
84
  if (!isMissingGateMessage(errorMessage(error))) {
76
85
  throw error;
77
86
  }
78
- await prepareBuilderSourceExecution.run(executionArgs);
79
- await executeBuilderSourceExecutionWithDeps(
87
+ const preparation =
88
+ await prepareBuilderSourceExecution.run(executionArgs);
89
+ const response = await executeBuilderSourceExecutionWithDeps(
80
90
  executionArgs,
81
91
  realExecutionDeps(args.sourceId),
82
92
  );
93
+ return {
94
+ changeSetId,
95
+ status: "succeeded",
96
+ timings: [
97
+ ...(preparation.timings ?? []),
98
+ ...(response.timings ?? []),
99
+ ],
100
+ };
83
101
  }
84
- return { changeSetId, status: "succeeded" };
85
102
  },
86
103
  };
87
104
  }
@@ -112,6 +129,12 @@ function isBlockedMessage(message: string) {
112
129
  ].some((pattern) => pattern.test(message));
113
130
  }
114
131
 
132
+ function isReconciliationRequiredMessage(message: string) {
133
+ return /reconciliation required|requires reconciliation|remote outcome is unknown/i.test(
134
+ message,
135
+ );
136
+ }
137
+
115
138
  function clampConcurrency(value: number | undefined) {
116
139
  if (!value || !Number.isFinite(value)) {
117
140
  return DEFAULT_BUILDER_SOURCE_BATCH_CONCURRENCY;
@@ -180,6 +203,9 @@ function summarize(results: BuilderSourceBatchItemResult[]) {
180
203
  total: results.length,
181
204
  succeeded: results.filter((result) => result.status === "succeeded").length,
182
205
  blocked: results.filter((result) => result.status === "blocked").length,
206
+ reconciliationRequired: results.filter(
207
+ (result) => result.status === "reconciliation_required",
208
+ ).length,
183
209
  failed: results.filter((result) => result.status === "failed").length,
184
210
  };
185
211
  }
@@ -188,55 +214,100 @@ export async function executeBuilderSourceBatchWithDeps(
188
214
  args: ExecuteBuilderSourceBatchRequest,
189
215
  deps: ExecuteBuilderSourceBatchDeps,
190
216
  ): Promise<ExecuteBuilderSourceBatchResponse> {
191
- const database = await deps.resolveDatabase(args);
192
- if (!database) throw new Error("Database not found.");
193
- await deps.assertEditor(database);
217
+ const timing = createBuilderSourceTiming("execute_builder_source_batch");
218
+ try {
219
+ const { source } = await timing.measure(
220
+ "snapshot_read_and_diff_load",
221
+ async () => {
222
+ const database = await deps.resolveDatabase(args);
223
+ if (!database) throw new Error("Database not found.");
224
+ await deps.assertEditor(database);
225
+ return { source: await deps.getSourceSnapshot(database) };
226
+ },
227
+ );
228
+ if (!source || source.sourceType !== "builder-cms") {
229
+ throw new Error("Attach a Builder CMS source before executing writes.");
230
+ }
194
231
 
195
- const source = await deps.getSourceSnapshot(database);
196
- if (!source || source.sourceType !== "builder-cms") {
197
- throw new Error("Attach a Builder CMS source before executing writes.");
198
- }
232
+ const ids = uniqueIds(
233
+ args.changeSetIds ?? defaultBatchChangeSetIds(source),
234
+ );
235
+ const changeSetsById = new Map(
236
+ source.changeSets.map((changeSet) => [changeSet.id, changeSet]),
237
+ );
238
+ const maxConcurrency = clampConcurrency(args.maxConcurrency);
239
+ const results = await timing.measure("batch_dispatch", () =>
240
+ runBoundedPool(ids, maxConcurrency, async (changeSetId) => {
241
+ const changeSet = changeSetsById.get(changeSetId);
242
+ if (changeSet && hasSucceededExecution(changeSet)) {
243
+ return {
244
+ changeSetId,
245
+ status: "succeeded",
246
+ message: "Builder execution already succeeded; skipped.",
247
+ };
248
+ }
199
249
 
200
- const ids = uniqueIds(args.changeSetIds ?? defaultBatchChangeSetIds(source));
201
- const changeSetsById = new Map(
202
- source.changeSets.map((changeSet) => [changeSet.id, changeSet]),
203
- );
204
- const maxConcurrency = clampConcurrency(args.maxConcurrency);
205
- const results = await runBoundedPool(
206
- ids,
207
- maxConcurrency,
208
- async (changeSetId) => {
209
- const changeSet = changeSetsById.get(changeSetId);
210
- if (changeSet && hasSucceededExecution(changeSet)) {
211
- return {
212
- changeSetId,
213
- status: "succeeded",
214
- message: "Builder execution already succeeded; skipped.",
215
- };
216
- }
250
+ const blocker = explicitBatchBlocker(changeSet);
251
+ if (blocker) {
252
+ return { changeSetId, status: "blocked", message: blocker };
253
+ }
217
254
 
218
- const blocker = explicitBatchBlocker(changeSet);
219
- if (blocker) {
220
- return { changeSetId, status: "blocked", message: blocker };
221
- }
255
+ try {
256
+ return await deps.runOne(
257
+ changeSetId,
258
+ args.transitions?.[changeSetId],
259
+ );
260
+ } catch (error) {
261
+ const message = errorMessage(error);
262
+ return {
263
+ changeSetId,
264
+ status: isReconciliationRequiredMessage(message)
265
+ ? "reconciliation_required"
266
+ : isBlockedMessage(message)
267
+ ? "blocked"
268
+ : "failed",
269
+ message,
270
+ };
271
+ }
272
+ }),
273
+ );
222
274
 
223
- try {
224
- return await deps.runOne(changeSetId, args.transitions?.[changeSetId]);
225
- } catch (error) {
226
- const message = errorMessage(error);
227
- return {
228
- changeSetId,
229
- status: isBlockedMessage(message) ? "blocked" : "failed",
230
- message,
231
- };
232
- }
233
- },
234
- );
275
+ const phaseDuration = (names: string[]) =>
276
+ results.reduce(
277
+ (total, result) =>
278
+ total +
279
+ (result.timings ?? [])
280
+ .filter((item) => names.includes(item.name))
281
+ .reduce((sum, item) => sum + item.durationMs, 0),
282
+ 0,
283
+ );
284
+ timing.add(
285
+ "approval_gate_preparation_and_dry_run_validation",
286
+ phaseDuration([
287
+ "gate_preparation_and_dry_run_validation",
288
+ "approval_gate_and_dry_run_validation",
289
+ ]),
290
+ );
291
+ timing.add("write_dispatch", phaseDuration(["write_dispatch"]));
292
+ timing.add(
293
+ "reconciliation_and_persistence",
294
+ phaseDuration(["reconciliation_and_persistence"]),
295
+ );
235
296
 
236
- return {
237
- summary: summarize(results),
238
- results,
239
- };
297
+ const response = {
298
+ summary: summarize(results),
299
+ results,
300
+ timings: timing.finish(),
301
+ };
302
+ timing.log("succeeded");
303
+ return response;
304
+ } catch (error) {
305
+ timing.ensure("approval_gate_preparation_and_dry_run_validation");
306
+ timing.ensure("write_dispatch");
307
+ timing.ensure("reconciliation_and_persistence");
308
+ timing.log("failed");
309
+ throw error;
310
+ }
240
311
  }
241
312
 
242
313
  const transitionSchema = z.object({