@agent-native/core-corpus 0.216.0 → 0.218.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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
- package/corpus/templates/analytics/AGENTS.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +3 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
- package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
- package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
- package/corpus/templates/analytics/actions/view-screen.ts +29 -3
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
- package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
- package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
- package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
- package/corpus/templates/analytics/server/db/schema.ts +67 -0
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
- package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
- package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
- package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
- package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
- package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
- package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
- package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
- package/corpus/templates/analytics/server/plugins/db.ts +46 -0
- package/corpus/templates/analytics/shared/output-use.ts +205 -0
- package/corpus/templates/analytics/shared/session-events.ts +6 -0
- package/corpus/templates/assets/CHANGELOG.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
- package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
- package/corpus/templates/brain/app/routes/sources.tsx +1 -1
- package/corpus/templates/brain/vitest.config.ts +7 -0
- package/corpus/templates/calendar/CHANGELOG.md +12 -0
- package/corpus/templates/calendar/actions/create-event.ts +2 -4
- package/corpus/templates/calendar/actions/update-event.ts +35 -6
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
- package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
- package/corpus/templates/clips/CHANGELOG.md +7 -20
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
- package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
- package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
- package/corpus/templates/content/CHANGELOG.md +10 -0
- package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
- package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
- package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
- package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
- package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
- package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
- package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
- package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
- package/corpus/templates/content/actions/_database-utils.ts +93 -7
- package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
- package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
- package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
- package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
- package/corpus/templates/content/actions/delete-document.ts +41 -3
- package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
- package/corpus/templates/content/actions/get-document.ts +2 -0
- package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
- package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
- package/corpus/templates/content/actions/update-document.ts +139 -20
- package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
- package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
- package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
- package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
- package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
- package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
- package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
- package/corpus/templates/content/app/global.css +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
- package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
- package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
- package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
- package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
- package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
- package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
- package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
- package/corpus/templates/content/parity/matrix.md +42 -42
- package/corpus/templates/content/parity/matrix.ts +3 -1
- package/corpus/templates/content/server/db/schema.ts +20 -0
- package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
- package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
- package/corpus/templates/content/server/live-copy/binding.ts +31 -0
- package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
- package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
- package/corpus/templates/content/server/live-copy/holds.ts +59 -0
- package/corpus/templates/content/server/live-copy/persist.ts +656 -0
- package/corpus/templates/content/server/live-copy/switch.ts +136 -0
- package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
- package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
- package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
- package/corpus/templates/content/server/live-copy/write.ts +85 -0
- package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
- package/corpus/templates/content/server/plugins/collab.ts +3 -0
- package/corpus/templates/content/server/plugins/db.ts +28 -0
- package/corpus/templates/content/shared/api.ts +30 -1
- package/corpus/templates/content/shared/database-item-layout.ts +54 -22
- package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
- package/corpus/templates/content/shared/og-description.ts +258 -36
- package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
- package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
- package/corpus/templates/content/shared/suggestion-text.ts +20 -5
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
- package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
- package/corpus/templates/design/CHANGELOG.md +5 -0
- package/corpus/templates/design/actions/get-getting-started.ts +53 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
- package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
- package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
- package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
- package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
- package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
- package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
- package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
- package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
- package/corpus/templates/design/app/global.css +6 -0
- package/corpus/templates/design/app/i18n/en-US.ts +17 -0
- package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
- package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
- package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
- package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
- package/corpus/templates/design/app/pages/Index.tsx +28 -67
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
- package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
- package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
- package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
- package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
- package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
- package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
- package/corpus/templates/design/server/lib/getting-started.ts +267 -0
- package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
- package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
- package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
- package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
- package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
- package/corpus/templates/design/shared/design-template-presets.ts +8 -0
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
- package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/plan/CHANGELOG.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
- package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +7 -0
- package/corpus/templates/slides/actions/delete-deck.ts +7 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
- package/corpus/templates/slides/app/pages/Index.tsx +7 -2
- package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
- package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
- package/package.json +1 -1
- package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
- package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defineAction } from "@agent-native/core/action";
|
|
1
|
+
import { defineAction, fail } from "@agent-native/core/action";
|
|
2
2
|
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
3
3
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
4
|
-
import { and, eq, inArray, or } from "drizzle-orm";
|
|
4
|
+
import { and, eq, inArray, isNull, or } from "drizzle-orm";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -16,6 +16,13 @@ import type {
|
|
|
16
16
|
PrepareBuilderSourceReviewRequest,
|
|
17
17
|
PrepareBuilderSourceReviewResponse,
|
|
18
18
|
} from "../shared/api.js";
|
|
19
|
+
import {
|
|
20
|
+
builderExecutionPayloadReference,
|
|
21
|
+
cleanupBuilderPrivatePayload,
|
|
22
|
+
enqueueBuilderPrivatePayloadCleanup,
|
|
23
|
+
readBuilderExecutionPayload,
|
|
24
|
+
storeBuilderExecutionPayload,
|
|
25
|
+
} from "./_builder-cms-blob-custody.js";
|
|
19
26
|
import {
|
|
20
27
|
buildBuilderCmsExecutionPlan,
|
|
21
28
|
resolveBuilderCmsWriteEffect,
|
|
@@ -25,7 +32,6 @@ import { claimBuilderSourceExecutionGate } from "./_builder-source-execution-cla
|
|
|
25
32
|
import { shouldPreserveBuilderExecution } from "./_builder-source-execution-preservation.js";
|
|
26
33
|
import { createBuilderSourceTiming } from "./_builder-source-timings.js";
|
|
27
34
|
import {
|
|
28
|
-
canRefreshLocallyBlockedBuilderReview,
|
|
29
35
|
findOpenSourceChangeSet,
|
|
30
36
|
getContentDatabaseSourceSnapshotForReview,
|
|
31
37
|
getContentDatabaseSourceSnapshotForWrite,
|
|
@@ -140,17 +146,6 @@ export function reviewPreparePriority(
|
|
|
140
146
|
return statePriority + effectPriority;
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
function parsePayload(value: string) {
|
|
144
|
-
try {
|
|
145
|
-
const parsed = JSON.parse(value) as unknown;
|
|
146
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
147
|
-
? (parsed as Record<string, unknown>)
|
|
148
|
-
: {};
|
|
149
|
-
} catch {
|
|
150
|
-
return {};
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
149
|
function dryRunStatus(execution: ContentDatabaseSourceExecution | null) {
|
|
155
150
|
const dryRun =
|
|
156
151
|
execution?.payload.dryRun &&
|
|
@@ -188,10 +183,18 @@ export function buildBuilderSourceReviewPayload(args: {
|
|
|
188
183
|
const changedTitle =
|
|
189
184
|
changeSet.fieldChanges.find((field) => field.localFieldKey === "title")
|
|
190
185
|
?.proposedValue ?? null;
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
const plannedEffect = latestExecution?.payload.effect;
|
|
187
|
+
const effect =
|
|
188
|
+
plannedEffect === "autosave" ||
|
|
189
|
+
plannedEffect === "update_in_place" ||
|
|
190
|
+
plannedEffect === "create_draft" ||
|
|
191
|
+
plannedEffect === "publish" ||
|
|
192
|
+
plannedEffect === "unpublish"
|
|
193
|
+
? plannedEffect
|
|
194
|
+
: resolveBuilderCmsWriteEffect({
|
|
195
|
+
source: args.source,
|
|
196
|
+
changeSet,
|
|
197
|
+
});
|
|
195
198
|
|
|
196
199
|
return {
|
|
197
200
|
changeSetId: changeSet.id,
|
|
@@ -221,9 +224,10 @@ export function buildBuilderSourceReviewPayload(args: {
|
|
|
221
224
|
.filter(Boolean);
|
|
222
225
|
const hasExecutionEvidence =
|
|
223
226
|
statuses.length > 0 || executionStates.length > 0;
|
|
224
|
-
const resultStatus =
|
|
225
|
-
|
|
226
|
-
executionStates.
|
|
227
|
+
const resultStatus = rows.some((row) => row.execution?.payloadUnavailable)
|
|
228
|
+
? "blocked"
|
|
229
|
+
: executionStates.length > 0 &&
|
|
230
|
+
executionStates.every((state) => state === "succeeded")
|
|
227
231
|
? "succeeded"
|
|
228
232
|
: executionStates.includes("reconciliation_required") ||
|
|
229
233
|
executionStates.includes("response_received")
|
|
@@ -275,7 +279,9 @@ export function buildBuilderSourceReviewPayload(args: {
|
|
|
275
279
|
: "Ready to send to Builder."
|
|
276
280
|
: "Push checked successfully. Nothing was sent to Builder."
|
|
277
281
|
: resultStatus === "blocked"
|
|
278
|
-
?
|
|
282
|
+
? rows.some((row) => row.execution?.payloadUnavailable)
|
|
283
|
+
? "A prepared Builder payload is unavailable. Retry loading the review before anything can be sent."
|
|
284
|
+
: "Push needs attention before anything can be sent to Builder."
|
|
279
285
|
: resultStatus === "stale"
|
|
280
286
|
? "Push needs a fresh review because the plan changed."
|
|
281
287
|
: "Builder writes are off in this local build. Push will check the update only.",
|
|
@@ -346,23 +352,11 @@ async function approveChangeSetForReview(args: {
|
|
|
346
352
|
: null);
|
|
347
353
|
|
|
348
354
|
if (existing) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
})
|
|
355
|
-
.from(schema.contentDatabaseSourceExecutions)
|
|
356
|
-
.where(
|
|
357
|
-
and(
|
|
358
|
-
eq(schema.contentDatabaseSourceExecutions.sourceId, args.sourceId),
|
|
359
|
-
eq(schema.contentDatabaseSourceExecutions.changeSetId, existing.id),
|
|
360
|
-
),
|
|
361
|
-
);
|
|
362
|
-
const mayRefreshApprovedPayload =
|
|
363
|
-
canRefreshLocallyBlockedBuilderReview(existingExecutions);
|
|
364
|
-
|
|
365
|
-
if (existing.state === "approved" && !mayRefreshApprovedPayload) {
|
|
355
|
+
// An approved payload is immutable execution evidence. Even a locally
|
|
356
|
+
// blocked gate can be claimed after this read, so a materially changed
|
|
357
|
+
// proposal always receives a new revision identity instead of rewriting
|
|
358
|
+
// the proposal beneath an in-flight execution.
|
|
359
|
+
if (existing.state === "approved") {
|
|
366
360
|
if (existingPayloadMatches(existing)) {
|
|
367
361
|
return {
|
|
368
362
|
id: existing.id,
|
|
@@ -382,11 +376,9 @@ async function approveChangeSetForReview(args: {
|
|
|
382
376
|
},
|
|
383
377
|
};
|
|
384
378
|
}
|
|
385
|
-
//
|
|
386
|
-
// approved payload forever. A materially different local edit must fall
|
|
387
|
-
// through to a new revision identity instead of aliasing that evidence.
|
|
379
|
+
// Fall through to a revision-bound change-set ID below.
|
|
388
380
|
} else {
|
|
389
|
-
await db
|
|
381
|
+
const [updated] = await db
|
|
390
382
|
.update(schema.contentDatabaseSourceChangeSets)
|
|
391
383
|
.set({
|
|
392
384
|
direction: "outbound",
|
|
@@ -400,7 +392,29 @@ async function approveChangeSetForReview(args: {
|
|
|
400
392
|
: null,
|
|
401
393
|
updatedAt: args.now,
|
|
402
394
|
})
|
|
403
|
-
.where(
|
|
395
|
+
.where(
|
|
396
|
+
and(
|
|
397
|
+
eq(schema.contentDatabaseSourceChangeSets.id, existing.id),
|
|
398
|
+
eq(schema.contentDatabaseSourceChangeSets.state, existing.state),
|
|
399
|
+
eq(
|
|
400
|
+
schema.contentDatabaseSourceChangeSets.fieldChangesJson,
|
|
401
|
+
existing.fieldChangesJson,
|
|
402
|
+
),
|
|
403
|
+
existing.bodyChangeJson === null
|
|
404
|
+
? isNull(schema.contentDatabaseSourceChangeSets.bodyChangeJson)
|
|
405
|
+
: eq(
|
|
406
|
+
schema.contentDatabaseSourceChangeSets.bodyChangeJson,
|
|
407
|
+
existing.bodyChangeJson,
|
|
408
|
+
),
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
.returning({ id: schema.contentDatabaseSourceChangeSets.id });
|
|
412
|
+
if (!updated) {
|
|
413
|
+
fail("Builder change set changed during approval.", {
|
|
414
|
+
errorCode: "builder_change_set_changed",
|
|
415
|
+
statusCode: 409,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
404
418
|
if (existing.state !== "approved") {
|
|
405
419
|
await db.insert(schema.contentDatabaseSourceChangeReviews).values({
|
|
406
420
|
id: crypto.randomUUID(),
|
|
@@ -504,18 +518,114 @@ async function upsertExecutionGate(args: {
|
|
|
504
518
|
return;
|
|
505
519
|
}
|
|
506
520
|
if (existing) {
|
|
507
|
-
await
|
|
508
|
-
.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
521
|
+
const payloadJson = await storeBuilderExecutionPayload({
|
|
522
|
+
payload: plan.payload as unknown as Record<string, unknown>,
|
|
523
|
+
binding: {
|
|
524
|
+
ownerEmail: args.ownerEmail,
|
|
525
|
+
sourceId: args.source.id,
|
|
526
|
+
changeSetId: args.changeSet.id,
|
|
527
|
+
executionId: existing.id,
|
|
528
|
+
idempotencyKey: plan.idempotencyKey,
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
const nextReference = builderExecutionPayloadReference(payloadJson);
|
|
532
|
+
const previousReference = builderExecutionPayloadReference(
|
|
533
|
+
existing.payloadJson,
|
|
534
|
+
);
|
|
535
|
+
try {
|
|
536
|
+
const updatedRows = await db.transaction(async (tx) => {
|
|
537
|
+
const updated = await tx
|
|
538
|
+
.update(schema.contentDatabaseSourceExecutions)
|
|
539
|
+
.set({
|
|
540
|
+
state: plan.state,
|
|
541
|
+
summary: plan.summary,
|
|
542
|
+
payloadJson,
|
|
543
|
+
lastError: plan.lastError,
|
|
544
|
+
updatedAt: args.now,
|
|
545
|
+
})
|
|
546
|
+
.where(
|
|
547
|
+
and(
|
|
548
|
+
eq(schema.contentDatabaseSourceExecutions.id, existing.id),
|
|
549
|
+
eq(
|
|
550
|
+
schema.contentDatabaseSourceExecutions.payloadJson,
|
|
551
|
+
existing.payloadJson,
|
|
552
|
+
),
|
|
553
|
+
eq(schema.contentDatabaseSourceExecutions.state, existing.state),
|
|
554
|
+
existing.attemptToken
|
|
555
|
+
? eq(
|
|
556
|
+
schema.contentDatabaseSourceExecutions.attemptToken,
|
|
557
|
+
existing.attemptToken,
|
|
558
|
+
)
|
|
559
|
+
: isNull(schema.contentDatabaseSourceExecutions.attemptToken),
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
.returning({ id: schema.contentDatabaseSourceExecutions.id });
|
|
563
|
+
if (
|
|
564
|
+
updated[0] &&
|
|
565
|
+
previousReference &&
|
|
566
|
+
previousReference !== nextReference
|
|
567
|
+
) {
|
|
568
|
+
await enqueueBuilderPrivatePayloadCleanup(
|
|
569
|
+
tx as unknown as ReturnType<typeof getDb>,
|
|
570
|
+
previousReference,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
return updated;
|
|
574
|
+
});
|
|
575
|
+
const [updated] = updatedRows;
|
|
576
|
+
if (!updated)
|
|
577
|
+
fail("Builder execution changed during prepare.", {
|
|
578
|
+
errorCode: "builder_execution_changed",
|
|
579
|
+
statusCode: 409,
|
|
580
|
+
});
|
|
581
|
+
} catch (error) {
|
|
582
|
+
let currentPayloadJson: string | null | undefined;
|
|
583
|
+
try {
|
|
584
|
+
const [current] = await db
|
|
585
|
+
.select({
|
|
586
|
+
payloadJson: schema.contentDatabaseSourceExecutions.payloadJson,
|
|
587
|
+
})
|
|
588
|
+
.from(schema.contentDatabaseSourceExecutions)
|
|
589
|
+
.where(eq(schema.contentDatabaseSourceExecutions.id, existing.id))
|
|
590
|
+
.limit(1);
|
|
591
|
+
currentPayloadJson = current?.payloadJson;
|
|
592
|
+
} catch {
|
|
593
|
+
// Inconclusive readback retains both refs.
|
|
594
|
+
}
|
|
595
|
+
if (currentPayloadJson === payloadJson) {
|
|
596
|
+
// The replacement committed despite an ambiguous acknowledgement.
|
|
597
|
+
} else {
|
|
598
|
+
if (currentPayloadJson !== undefined && nextReference) {
|
|
599
|
+
await cleanupBuilderPrivatePayload(
|
|
600
|
+
nextReference,
|
|
601
|
+
"uncommitted prepared execution",
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (previousReference && previousReference !== nextReference) {
|
|
608
|
+
await cleanupBuilderPrivatePayload(
|
|
609
|
+
previousReference,
|
|
610
|
+
"superseded prepared execution",
|
|
611
|
+
);
|
|
612
|
+
}
|
|
517
613
|
} else {
|
|
614
|
+
let insertedReference: string | null = null;
|
|
615
|
+
let insertedPayloadJson: string | null = null;
|
|
518
616
|
try {
|
|
617
|
+
const payloadJson = await storeBuilderExecutionPayload({
|
|
618
|
+
payload: plan.payload as unknown as Record<string, unknown>,
|
|
619
|
+
binding: {
|
|
620
|
+
ownerEmail: args.ownerEmail,
|
|
621
|
+
sourceId: args.source.id,
|
|
622
|
+
changeSetId: args.changeSet.id,
|
|
623
|
+
executionId,
|
|
624
|
+
idempotencyKey: plan.idempotencyKey,
|
|
625
|
+
},
|
|
626
|
+
});
|
|
627
|
+
insertedReference = builderExecutionPayloadReference(payloadJson);
|
|
628
|
+
insertedPayloadJson = payloadJson;
|
|
519
629
|
await db.insert(schema.contentDatabaseSourceExecutions).values({
|
|
520
630
|
id: executionId,
|
|
521
631
|
ownerEmail: args.ownerEmail,
|
|
@@ -526,18 +636,44 @@ async function upsertExecutionGate(args: {
|
|
|
526
636
|
state: plan.state,
|
|
527
637
|
idempotencyKey: plan.idempotencyKey,
|
|
528
638
|
summary: plan.summary,
|
|
529
|
-
payloadJson
|
|
639
|
+
payloadJson,
|
|
530
640
|
lastError: plan.lastError,
|
|
531
641
|
createdAt: args.now,
|
|
532
642
|
updatedAt: args.now,
|
|
533
643
|
});
|
|
534
644
|
} catch (error) {
|
|
535
|
-
|
|
536
|
-
.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
645
|
+
let winner:
|
|
646
|
+
| typeof schema.contentDatabaseSourceExecutions.$inferSelect
|
|
647
|
+
| null
|
|
648
|
+
| undefined;
|
|
649
|
+
let readbackCompleted = false;
|
|
650
|
+
try {
|
|
651
|
+
[winner] = await db
|
|
652
|
+
.select()
|
|
653
|
+
.from(schema.contentDatabaseSourceExecutions)
|
|
654
|
+
.where(eq(schema.contentDatabaseSourceExecutions.id, executionId))
|
|
655
|
+
.limit(1);
|
|
656
|
+
readbackCompleted = true;
|
|
657
|
+
} catch {
|
|
658
|
+
// Inconclusive readback retains the new ref.
|
|
659
|
+
}
|
|
660
|
+
if (!winner) {
|
|
661
|
+
if (readbackCompleted && insertedReference) {
|
|
662
|
+
await cleanupBuilderPrivatePayload(
|
|
663
|
+
insertedReference,
|
|
664
|
+
"uncommitted prepared execution",
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
if (winner.payloadJson === insertedPayloadJson) {
|
|
670
|
+
// The insert committed despite an ambiguous acknowledgement.
|
|
671
|
+
} else if (insertedReference) {
|
|
672
|
+
await cleanupBuilderPrivatePayload(
|
|
673
|
+
insertedReference,
|
|
674
|
+
"uncommitted prepared execution",
|
|
675
|
+
);
|
|
676
|
+
}
|
|
541
677
|
if (
|
|
542
678
|
shouldPreserveBuilderExecution({
|
|
543
679
|
state: winner.state,
|
|
@@ -558,8 +694,18 @@ async function upsertExecutionGate(args: {
|
|
|
558
694
|
.where(eq(schema.contentDatabaseSourceExecutions.id, executionId));
|
|
559
695
|
if (!execution) return;
|
|
560
696
|
|
|
697
|
+
const storedPayload = await readBuilderExecutionPayload({
|
|
698
|
+
payloadJson: execution.payloadJson,
|
|
699
|
+
binding: {
|
|
700
|
+
ownerEmail: execution.ownerEmail,
|
|
701
|
+
sourceId: execution.sourceId,
|
|
702
|
+
changeSetId: execution.changeSetId,
|
|
703
|
+
executionId: execution.id,
|
|
704
|
+
idempotencyKey: execution.idempotencyKey,
|
|
705
|
+
},
|
|
706
|
+
});
|
|
561
707
|
const payload = validateBuilderCmsExecutionDryRun({
|
|
562
|
-
storedPayload
|
|
708
|
+
storedPayload,
|
|
563
709
|
plan,
|
|
564
710
|
now: args.now,
|
|
565
711
|
});
|
|
@@ -571,17 +717,97 @@ async function upsertExecutionGate(args: {
|
|
|
571
717
|
? `${plan.summary} Dry run validated blockers locally.`
|
|
572
718
|
: `${plan.summary} Dry run found a stale execution gate.`;
|
|
573
719
|
|
|
574
|
-
await
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
720
|
+
const payloadJson = await storeBuilderExecutionPayload({
|
|
721
|
+
payload,
|
|
722
|
+
binding: {
|
|
723
|
+
ownerEmail: execution.ownerEmail,
|
|
724
|
+
sourceId: execution.sourceId,
|
|
725
|
+
changeSetId: execution.changeSetId,
|
|
726
|
+
executionId: execution.id,
|
|
727
|
+
idempotencyKey: execution.idempotencyKey,
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
const nextReference = builderExecutionPayloadReference(payloadJson);
|
|
731
|
+
const previousReference = builderExecutionPayloadReference(
|
|
732
|
+
execution.payloadJson,
|
|
733
|
+
);
|
|
734
|
+
try {
|
|
735
|
+
const updatedRows = await db.transaction(async (tx) => {
|
|
736
|
+
const updated = await tx
|
|
737
|
+
.update(schema.contentDatabaseSourceExecutions)
|
|
738
|
+
.set({
|
|
739
|
+
state: dryRun?.status === "stale" ? "blocked" : plan.state,
|
|
740
|
+
summary,
|
|
741
|
+
payloadJson,
|
|
742
|
+
lastError:
|
|
743
|
+
dryRun?.status === "stale" ? dryRun.mismatches[0] : plan.lastError,
|
|
744
|
+
updatedAt: args.now,
|
|
745
|
+
})
|
|
746
|
+
.where(
|
|
747
|
+
and(
|
|
748
|
+
eq(schema.contentDatabaseSourceExecutions.id, executionId),
|
|
749
|
+
eq(
|
|
750
|
+
schema.contentDatabaseSourceExecutions.payloadJson,
|
|
751
|
+
execution.payloadJson,
|
|
752
|
+
),
|
|
753
|
+
eq(schema.contentDatabaseSourceExecutions.state, execution.state),
|
|
754
|
+
execution.attemptToken
|
|
755
|
+
? eq(
|
|
756
|
+
schema.contentDatabaseSourceExecutions.attemptToken,
|
|
757
|
+
execution.attemptToken,
|
|
758
|
+
)
|
|
759
|
+
: isNull(schema.contentDatabaseSourceExecutions.attemptToken),
|
|
760
|
+
),
|
|
761
|
+
)
|
|
762
|
+
.returning({ id: schema.contentDatabaseSourceExecutions.id });
|
|
763
|
+
if (
|
|
764
|
+
updated[0] &&
|
|
765
|
+
previousReference &&
|
|
766
|
+
previousReference !== nextReference
|
|
767
|
+
) {
|
|
768
|
+
await enqueueBuilderPrivatePayloadCleanup(
|
|
769
|
+
tx as unknown as ReturnType<typeof getDb>,
|
|
770
|
+
previousReference,
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
return updated;
|
|
774
|
+
});
|
|
775
|
+
const [updated] = updatedRows;
|
|
776
|
+
if (!updated)
|
|
777
|
+
fail("Builder execution changed during validation.", {
|
|
778
|
+
errorCode: "builder_execution_changed",
|
|
779
|
+
statusCode: 409,
|
|
780
|
+
});
|
|
781
|
+
} catch (error) {
|
|
782
|
+
let currentPayloadJson: string | null | undefined;
|
|
783
|
+
try {
|
|
784
|
+
const [current] = await db
|
|
785
|
+
.select({
|
|
786
|
+
payloadJson: schema.contentDatabaseSourceExecutions.payloadJson,
|
|
787
|
+
})
|
|
788
|
+
.from(schema.contentDatabaseSourceExecutions)
|
|
789
|
+
.where(eq(schema.contentDatabaseSourceExecutions.id, executionId))
|
|
790
|
+
.limit(1);
|
|
791
|
+
currentPayloadJson = current?.payloadJson;
|
|
792
|
+
} catch {
|
|
793
|
+
// Inconclusive readback retains both refs.
|
|
794
|
+
}
|
|
795
|
+
if (currentPayloadJson !== payloadJson) {
|
|
796
|
+
if (currentPayloadJson !== undefined && nextReference) {
|
|
797
|
+
await cleanupBuilderPrivatePayload(
|
|
798
|
+
nextReference,
|
|
799
|
+
"uncommitted validated execution",
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
throw error;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (previousReference && previousReference !== nextReference) {
|
|
806
|
+
await cleanupBuilderPrivatePayload(
|
|
807
|
+
previousReference,
|
|
808
|
+
"superseded validated execution",
|
|
809
|
+
);
|
|
810
|
+
}
|
|
585
811
|
}
|
|
586
812
|
|
|
587
813
|
export default defineAction({
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ActionContractError } from "@agent-native/core";
|
|
2
|
+
import { defineAction } from "@agent-native/core/action";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { LiveBodyConversionError } from "../server/live-copy/persist.js";
|
|
6
|
+
import {
|
|
7
|
+
switchPageToLegacy,
|
|
8
|
+
switchPageToLiveCopy,
|
|
9
|
+
} from "../server/live-copy/switch.js";
|
|
10
|
+
import { assertDocumentMutationAccess } from "./_document-mutation-access.js";
|
|
11
|
+
|
|
12
|
+
export default defineAction({
|
|
13
|
+
description:
|
|
14
|
+
"Switch one page's body writes to its live copy, or back to the legacy writer, for the live-copy rollout.",
|
|
15
|
+
uiOnly: true,
|
|
16
|
+
agentTool: false,
|
|
17
|
+
toolCallable: false,
|
|
18
|
+
mcpTool: false,
|
|
19
|
+
schema: z.object({
|
|
20
|
+
id: z.string().min(1).describe("Document ID"),
|
|
21
|
+
switched: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.describe("true writes through the live copy; false goes back"),
|
|
24
|
+
}),
|
|
25
|
+
run: async (args, ctx) => {
|
|
26
|
+
await assertDocumentMutationAccess(args.id, "editor", "id");
|
|
27
|
+
try {
|
|
28
|
+
const gate = args.switched
|
|
29
|
+
? await switchPageToLiveCopy(args.id, { ctx })
|
|
30
|
+
: await switchPageToLegacy(args.id);
|
|
31
|
+
return { id: args.id, ...gate };
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error instanceof LiveBodyConversionError) {
|
|
34
|
+
throw new ActionContractError(error.message, {
|
|
35
|
+
errorCode:
|
|
36
|
+
error.code === "flag_off"
|
|
37
|
+
? "LIVE_BODY_WRITER_OFF"
|
|
38
|
+
: "LIVE_BODY_SWITCH_CONFLICT",
|
|
39
|
+
statusCode: 409,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
});
|