@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
@@ -1,5 +1,4 @@
1
1
  import {
2
- BUILDER_CMS_SAFE_WRITE_MODEL,
3
2
  type ContentDatabaseSourceCapabilities,
4
3
  type ContentDatabaseSourcePushMode,
5
4
  type ContentDatabaseSourceWriteMode,
@@ -126,6 +125,32 @@ function pushModeForTier(
126
125
  return "none";
127
126
  }
128
127
 
128
+ function writeModeCopy(writeMode: ContentDatabaseSourceWriteMode) {
129
+ if (writeMode === "publish_updates") {
130
+ return {
131
+ pushModeLabel: "Publish updates",
132
+ pushModeDescription:
133
+ "Review, update, and publish existing Builder entries. New entries are never created by this mode.",
134
+ notes:
135
+ "Builder CMS source with reviewed update-in-place publication enabled for existing entries.",
136
+ };
137
+ }
138
+ if (writeMode === "stage_only") {
139
+ return {
140
+ pushModeLabel: "Stage only",
141
+ pushModeDescription:
142
+ "Prepare and review local revisions without writing to Builder.",
143
+ notes:
144
+ "Builder CMS source with local review staging enabled and remote writes disabled.",
145
+ };
146
+ }
147
+ return {
148
+ pushModeLabel: "Read only",
149
+ pushModeDescription: "Refresh and review Builder content without writing.",
150
+ notes: "Builder CMS source with remote writes disabled.",
151
+ };
152
+ }
153
+
129
154
  export function builderCmsWriteSettingsFromJson(args: {
130
155
  capabilitiesJson: string | null | undefined;
131
156
  metadataJson: string | null | undefined;
@@ -178,11 +203,6 @@ export function buildBuilderCmsWriteModeJson(
178
203
  "Live writes can only be enabled for Builder CMS sources.",
179
204
  );
180
205
  }
181
- if (args.sourceTable !== BUILDER_CMS_SAFE_WRITE_MODEL) {
182
- throw new Error(
183
- `Live Builder writes are only allowed for ${BUILDER_CMS_SAFE_WRITE_MODEL}.`,
184
- );
185
- }
186
206
  }
187
207
 
188
208
  if (
@@ -199,6 +219,7 @@ export function buildBuilderCmsWriteModeJson(
199
219
  };
200
220
  const nextMetadata: Record<string, unknown> = {
201
221
  ...metadata,
222
+ ...writeModeCopy(writeMode),
202
223
  writeMode,
203
224
  allowPublicationTransitions,
204
225
  allowedWriteModes,
@@ -226,7 +247,6 @@ export function mergeBuilderCmsWriteSettingsIntoJson(args: {
226
247
  });
227
248
  if (
228
249
  currentSettings.liveWritesEnabled !== true ||
229
- args.sourceTable !== BUILDER_CMS_SAFE_WRITE_MODEL ||
230
250
  currentSettings.writeMode === "read_only"
231
251
  ) {
232
252
  return {
@@ -0,0 +1,124 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { and, eq } from "drizzle-orm";
4
+
5
+ import { getDb, schema } from "../server/db/index.js";
6
+ import { builderExecutionHasResponseEvidence } from "./_builder-source-execution-preservation.js";
7
+
8
+ function claimId(sourceId: string, idempotencyKey: string) {
9
+ return createHash("sha256")
10
+ .update(`${sourceId}\u0000${idempotencyKey}`)
11
+ .digest("hex");
12
+ }
13
+
14
+ function evidenceRank(row: {
15
+ state: string;
16
+ payloadJson: string;
17
+ updatedAt: string;
18
+ id: string;
19
+ }) {
20
+ const stateRank =
21
+ row.state === "succeeded"
22
+ ? 0
23
+ : row.state === "response_received"
24
+ ? 1
25
+ : row.state === "reconciliation_required"
26
+ ? 2
27
+ : builderExecutionHasResponseEvidence(row.payloadJson)
28
+ ? 3
29
+ : row.state === "running"
30
+ ? 4
31
+ : 5;
32
+ return { stateRank, updatedAt: row.updatedAt, id: row.id };
33
+ }
34
+
35
+ function canonicalExecution<
36
+ T extends {
37
+ state: string;
38
+ payloadJson: string;
39
+ updatedAt: string;
40
+ id: string;
41
+ },
42
+ >(rows: T[]) {
43
+ return [...rows].sort((left, right) => {
44
+ const a = evidenceRank(left);
45
+ const b = evidenceRank(right);
46
+ return (
47
+ a.stateRank - b.stateRank ||
48
+ b.updatedAt.localeCompare(a.updatedAt) ||
49
+ a.id.localeCompare(b.id)
50
+ );
51
+ })[0];
52
+ }
53
+
54
+ export async function claimBuilderSourceExecutionGate(args: {
55
+ ownerEmail: string;
56
+ sourceId: string;
57
+ idempotencyKey: string;
58
+ now: string;
59
+ }) {
60
+ const db = getDb();
61
+ const whereKey = and(
62
+ eq(schema.contentDatabaseSourceExecutionClaims.ownerEmail, args.ownerEmail),
63
+ eq(schema.contentDatabaseSourceExecutionClaims.sourceId, args.sourceId),
64
+ eq(
65
+ schema.contentDatabaseSourceExecutionClaims.idempotencyKey,
66
+ args.idempotencyKey,
67
+ ),
68
+ );
69
+ const [existingClaim] = await db
70
+ .select()
71
+ .from(schema.contentDatabaseSourceExecutionClaims)
72
+ .where(whereKey)
73
+ .limit(1);
74
+ if (existingClaim) return existingClaim.executionId;
75
+
76
+ const candidates = await db
77
+ .select()
78
+ .from(schema.contentDatabaseSourceExecutions)
79
+ .where(
80
+ and(
81
+ eq(schema.contentDatabaseSourceExecutions.ownerEmail, args.ownerEmail),
82
+ eq(schema.contentDatabaseSourceExecutions.sourceId, args.sourceId),
83
+ eq(
84
+ schema.contentDatabaseSourceExecutions.idempotencyKey,
85
+ args.idempotencyKey,
86
+ ),
87
+ ),
88
+ );
89
+ const canonical = canonicalExecution(candidates);
90
+ const executionId = canonical?.id ?? crypto.randomUUID();
91
+
92
+ try {
93
+ await db.insert(schema.contentDatabaseSourceExecutionClaims).values({
94
+ id: claimId(args.sourceId, args.idempotencyKey),
95
+ ownerEmail: args.ownerEmail,
96
+ sourceId: args.sourceId,
97
+ idempotencyKey: args.idempotencyKey,
98
+ executionId,
99
+ createdAt: args.now,
100
+ });
101
+ } catch (error) {
102
+ const [winner] = await db
103
+ .select()
104
+ .from(schema.contentDatabaseSourceExecutionClaims)
105
+ .where(whereKey)
106
+ .limit(1);
107
+ if (!winner) throw error;
108
+ return winner.executionId;
109
+ }
110
+
111
+ if (candidates.length > 1 && canonical) {
112
+ await db
113
+ .update(schema.contentDatabaseSourceExecutions)
114
+ .set({
115
+ state: "reconciliation_required",
116
+ summary: "Duplicate execution gates require reconciliation.",
117
+ lastError:
118
+ "Multiple execution rows exist for this idempotency key. Evidence was preserved; do not retry.",
119
+ updatedAt: args.now,
120
+ })
121
+ .where(eq(schema.contentDatabaseSourceExecutions.id, canonical.id));
122
+ }
123
+ return executionId;
124
+ }
@@ -0,0 +1,21 @@
1
+ export function builderExecutionHasResponseEvidence(payloadJson: string) {
2
+ try {
3
+ const payload = JSON.parse(payloadJson) as Record<string, unknown>;
4
+ return Boolean(payload.response && typeof payload.response === "object");
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
9
+
10
+ export function shouldPreserveBuilderExecution(args: {
11
+ state: string;
12
+ payloadJson: string;
13
+ }) {
14
+ return (
15
+ args.state === "running" ||
16
+ args.state === "response_received" ||
17
+ args.state === "reconciliation_required" ||
18
+ args.state === "succeeded" ||
19
+ builderExecutionHasResponseEvidence(args.payloadJson)
20
+ );
21
+ }
@@ -0,0 +1,57 @@
1
+ import type { BuilderActionTiming } from "../shared/api.js";
2
+
3
+ type Clock = () => number;
4
+
5
+ function defaultClock() {
6
+ return performance.now();
7
+ }
8
+
9
+ export function createBuilderSourceTiming(
10
+ action: string,
11
+ clock: Clock = defaultClock,
12
+ ) {
13
+ const actionStartedAt = clock();
14
+ const timings: BuilderActionTiming[] = [];
15
+
16
+ function record(name: string, startedAt: number) {
17
+ const durationMs = Math.max(0, clock() - startedAt);
18
+ timings.push({ name, durationMs });
19
+ }
20
+
21
+ async function measure<T>(name: string, operation: () => Promise<T>) {
22
+ const startedAt = clock();
23
+ try {
24
+ return await operation();
25
+ } finally {
26
+ record(name, startedAt);
27
+ }
28
+ }
29
+
30
+ function ensure(name: string) {
31
+ if (!timings.some((timing) => timing.name === name)) {
32
+ timings.push({ name, durationMs: 0 });
33
+ }
34
+ }
35
+
36
+ function add(name: string, durationMs: number) {
37
+ const safeDurationMs = Math.max(0, durationMs);
38
+ timings.push({ name, durationMs: safeDurationMs });
39
+ }
40
+
41
+ function finish() {
42
+ if (!timings.some((timing) => timing.name === "total")) {
43
+ record("total", actionStartedAt);
44
+ }
45
+ return timings.map((timing) => ({ ...timing }));
46
+ }
47
+
48
+ function log(outcome: "succeeded" | "failed") {
49
+ console.info("builder_source_action_timing", {
50
+ action,
51
+ outcome,
52
+ timings: finish(),
53
+ });
54
+ }
55
+
56
+ return { start: clock, record, measure, add, ensure, finish, log };
57
+ }