@agent-native/creative-context 0.8.35 → 0.8.37-nightly-20261009093831

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 (32) hide show
  1. package/dist/actions/creative-context-a2a.js +15 -5
  2. package/dist/actions/creative-context-a2a.js.map +1 -1
  3. package/dist/server/context.d.ts.map +1 -1
  4. package/dist/server/context.js.map +1 -1
  5. package/dist/server/generation-artifact-access.d.ts +10 -0
  6. package/dist/server/generation-artifact-access.d.ts.map +1 -1
  7. package/dist/server/generation-artifact-access.js +75 -0
  8. package/dist/server/generation-artifact-access.js.map +1 -1
  9. package/dist/server/generation-context.d.ts +5 -0
  10. package/dist/server/generation-context.d.ts.map +1 -1
  11. package/dist/server/generation-context.js +36 -3
  12. package/dist/server/generation-context.js.map +1 -1
  13. package/dist/server/isolated-a2a.d.ts +4 -0
  14. package/dist/server/isolated-a2a.d.ts.map +1 -1
  15. package/dist/server/isolated-a2a.js +2 -0
  16. package/dist/server/isolated-a2a.js.map +1 -1
  17. package/dist/store/generation.d.ts +15 -0
  18. package/dist/store/generation.d.ts.map +1 -1
  19. package/dist/store/generation.js +65 -17
  20. package/dist/store/generation.js.map +1 -1
  21. package/package.json +5 -5
  22. package/src/actions/creative-context-a2a.spec.ts +99 -0
  23. package/src/actions/creative-context-a2a.ts +22 -5
  24. package/src/server/context.ts +2 -0
  25. package/src/server/generation-artifact-access.spec.ts +45 -0
  26. package/src/server/generation-artifact-access.ts +117 -0
  27. package/src/server/generation-context-a2a.spec.ts +76 -8
  28. package/src/server/generation-context.ts +58 -1
  29. package/src/server/isolated-a2a.spec.ts +23 -0
  30. package/src/server/isolated-a2a.ts +4 -0
  31. package/src/store/access.integration.spec.ts +145 -0
  32. package/src/store/generation.ts +109 -16
@@ -1,5 +1,5 @@
1
1
  import { assertAccess } from "@agent-native/core/sharing";
2
- import { and, desc, eq } from "drizzle-orm";
2
+ import { and, desc, eq, isNull, sql } from "drizzle-orm";
3
3
  import { getCreativeContext } from "../server/context.js";
4
4
  import { assertGenerationArtifactAccessProof, } from "../server/generation-artifact-access.js";
5
5
  import { newId, nowIso, parseJson, requireActor, stringifyJson, } from "./helpers.js";
@@ -37,6 +37,15 @@ export function assertGenerationCreativeContextInvariants(input) {
37
37
  }
38
38
  }
39
39
  export async function recordGenerationCreativeContext(input, options = {}) {
40
+ return recordGenerationCreativeContextInternal(input, options, false);
41
+ }
42
+ export async function recordGenerationCreativeContextFromSnapshot(input, options = {}) {
43
+ if (!input.onlyIfMissing) {
44
+ throw new Error("Creative Context snapshots must be recorded idempotently");
45
+ }
46
+ return recordGenerationCreativeContextInternal(input, options, true);
47
+ }
48
+ async function recordGenerationCreativeContextInternal(input, options, fromValidatedSnapshot) {
40
49
  const elementProvenance = input.elementProvenance ??
41
50
  input.reuseLabels.map((label, index) => ({
42
51
  elementId: label.elementId ?? `reference:${index + 1}`,
@@ -51,10 +60,10 @@ export async function recordGenerationCreativeContext(input, options = {}) {
51
60
  reuseLabels: input.reuseLabels,
52
61
  elementProvenance,
53
62
  });
54
- if (input.contextPackId) {
63
+ if (input.contextPackId && !fromValidatedSnapshot) {
55
64
  await assertAccess("creative-context-pack", input.contextPackId, "viewer", undefined, { skipResourceBody: true });
56
65
  }
57
- if (input.contextPackId) {
66
+ if (input.contextPackId && !fromValidatedSnapshot) {
58
67
  const { getDb, schema } = getCreativeContext();
59
68
  const db = options.db ?? getDb();
60
69
  const members = await db
@@ -92,18 +101,57 @@ export async function recordGenerationCreativeContext(input, options = {}) {
92
101
  ownerEmail: actor.ownerEmail,
93
102
  orgId: options.artifactAccess ? actor.orgId : null,
94
103
  };
95
- await db.insert(schema.generationRecords).values(row);
96
- await getCreativeContext().projections?.generation?.record({
97
- appId: input.appId,
98
- artifactType: input.artifactType,
99
- artifactId: input.artifactId,
100
- contextPackId: input.contextPackId,
101
- elementProvenance,
102
- });
103
- return {
104
- ...row,
105
- elementProvenance: parseJson(row.elementProvenance, []),
106
- };
104
+ let recorded;
105
+ if (input.onlyIfMissing) {
106
+ // Matching retries can reach separate server processes, so coordinate in Postgres.
107
+ const scopeKey = options.artifactAccess
108
+ ? ["org", actor.orgId, input.appId, input.artifactType, input.artifactId]
109
+ : [
110
+ "owner",
111
+ actor.ownerEmail,
112
+ input.appId,
113
+ input.artifactType,
114
+ input.artifactId,
115
+ ];
116
+ recorded = await db.transaction(async (tx) => {
117
+ await tx.execute(sql `SELECT pg_advisory_xact_lock(hashtextextended(${`creative-context-generation:${JSON.stringify(scopeKey)}`}, 0::bigint))`);
118
+ const existing = await getGenerationCreativeContext({
119
+ appId: input.appId,
120
+ artifactType: input.artifactType,
121
+ artifactId: input.artifactId,
122
+ }, { artifactAccess: options.artifactAccess, db: tx });
123
+ if (existing)
124
+ return { record: existing, inserted: false };
125
+ await tx.insert(schema.generationRecords).values(row);
126
+ return {
127
+ record: {
128
+ ...row,
129
+ elementProvenance: parseJson(row.elementProvenance, []),
130
+ },
131
+ inserted: true,
132
+ };
133
+ });
134
+ }
135
+ else {
136
+ await db.insert(schema.generationRecords).values(row);
137
+ recorded = {
138
+ record: {
139
+ ...row,
140
+ elementProvenance: parseJson(row.elementProvenance, []),
141
+ },
142
+ inserted: true,
143
+ };
144
+ }
145
+ if (recorded.inserted || input.onlyIfMissing) {
146
+ await getCreativeContext().projections?.generation?.record({
147
+ appId: recorded.record.appId,
148
+ artifactType: recorded.record.artifactType,
149
+ artifactId: recorded.record.artifactId,
150
+ contextPackId: recorded.record.contextPackId,
151
+ elementProvenance: recorded.record.elementProvenance,
152
+ });
153
+ }
154
+ return recorded.record;
107
155
  }
108
156
  export async function getGenerationCreativeContext(input, options = {}) {
109
157
  const { getDb, schema } = getCreativeContext();
@@ -116,8 +164,8 @@ export async function getGenerationCreativeContext(input, options = {}) {
116
164
  }
117
165
  const actorScope = options.artifactAccess
118
166
  ? eq(schema.generationRecords.orgId, actor.orgId)
119
- : eq(schema.generationRecords.ownerEmail, actor.ownerEmail);
120
- const rows = await getDb()
167
+ : and(eq(schema.generationRecords.ownerEmail, actor.ownerEmail), isNull(schema.generationRecords.orgId));
168
+ const rows = await (options.db ?? getDb())
121
169
  .select()
122
170
  .from(schema.generationRecords)
123
171
  .where(and(eq(schema.generationRecords.appId, input.appId), eq(schema.generationRecords.artifactType, input.artifactType), eq(schema.generationRecords.artifactId, input.artifactId), actorScope))
@@ -1 +1 @@
1
- {"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/store/generation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/store/generation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/creative-context",
3
- "version": "0.8.35",
3
+ "version": "0.8.37-nightly-20261009093831",
4
4
  "description": "Shared creative context ingestion, versioning, search, brand DNA, and context-pack primitives for agent-native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -58,12 +58,12 @@
58
58
  "react-dom": "^19.2.7",
59
59
  "typescript-7": "npm:typescript@^7.0.2",
60
60
  "vitest": "^4.1.5",
61
- "@agent-native/toolkit": "^0.204.0",
62
- "@agent-native/core": "0.204.0"
61
+ "@agent-native/toolkit": "^0.206.0-nightly-20261009093831",
62
+ "@agent-native/core": "0.206.0-nightly-20261009093831"
63
63
  },
64
64
  "peerDependencies": {
65
- "@agent-native/core": ">=0.190.0",
66
- "@agent-native/toolkit": ">=0.1.0",
65
+ "@agent-native/core": "0.206.0-nightly-20261009093831",
66
+ "@agent-native/toolkit": "0.206.0-nightly-20261009093831",
67
67
  "@tabler/icons-react": ">=3.41.1",
68
68
  "@tanstack/react-query": ">=5.101.4",
69
69
  "drizzle-orm": ">=0.45",
@@ -6,8 +6,10 @@ const mocks = vi.hoisted(() => ({
6
6
  createResponseToken: vi.fn(),
7
7
  resolveLocal: vi.fn(),
8
8
  verifyArtifactAccess: vi.fn(),
9
+ verifySnapshotCapability: vi.fn(),
9
10
  getGeneration: vi.fn(),
10
11
  recordGeneration: vi.fn(),
12
+ recordGenerationFromSnapshot: vi.fn(),
11
13
  }));
12
14
 
13
15
  vi.mock("@agent-native/core/server", () => ({
@@ -25,11 +27,15 @@ vi.mock("../server/generation-context.js", () => ({
25
27
 
26
28
  vi.mock("../server/generation-artifact-access.js", () => ({
27
29
  verifyGenerationArtifactAccessCapability: mocks.verifyArtifactAccess,
30
+ verifyGenerationCreativeContextSnapshotCapability:
31
+ mocks.verifySnapshotCapability,
28
32
  }));
29
33
 
30
34
  vi.mock("../store/generation.js", () => ({
31
35
  getGenerationCreativeContext: mocks.getGeneration,
32
36
  recordGenerationCreativeContext: mocks.recordGeneration,
37
+ recordGenerationCreativeContextFromSnapshot:
38
+ mocks.recordGenerationFromSnapshot,
33
39
  }));
34
40
 
35
41
  import action from "./creative-context-a2a.js";
@@ -52,6 +58,7 @@ describe("creative-context-a2a receiver action", () => {
52
58
  });
53
59
  mocks.createResponseToken.mockReturnValue("response-token");
54
60
  mocks.verifyArtifactAccess.mockResolvedValue({ verified: true });
61
+ mocks.verifySnapshotCapability.mockResolvedValue(undefined);
55
62
  mocks.getGeneration.mockResolvedValue(null);
56
63
  mocks.recordGeneration.mockResolvedValue({
57
64
  id: "record-1",
@@ -63,6 +70,16 @@ describe("creative-context-a2a receiver action", () => {
63
70
  elementProvenance: [],
64
71
  createdAt: "2026-07-16T00:00:00.000Z",
65
72
  });
73
+ mocks.recordGenerationFromSnapshot.mockResolvedValue({
74
+ id: "record-1",
75
+ appId: "slides",
76
+ artifactType: "deck",
77
+ artifactId: "deck-1",
78
+ contextMode: "auto",
79
+ contextPackId: null,
80
+ elementProvenance: [],
81
+ createdAt: "2026-07-16T00:00:00.000Z",
82
+ });
66
83
  });
67
84
 
68
85
  it("executes the local operation and returns the opaque response token", async () => {
@@ -159,4 +176,86 @@ describe("creative-context-a2a receiver action", () => {
159
176
  );
160
177
  expect(mocks.recordGeneration).not.toHaveBeenCalled();
161
178
  });
179
+
180
+ it("records replayed provenance from a validated snapshot", async () => {
181
+ const record = {
182
+ appId: "slides",
183
+ artifactType: "deck",
184
+ artifactId: "deck-1",
185
+ contextMode: "auto",
186
+ contextPackId: null,
187
+ reuseLabels: [],
188
+ onlyIfMissing: true,
189
+ };
190
+ mocks.decodeRequest.mockReturnValue({
191
+ protocol: "creative-context-a2a-v1",
192
+ requestId: "87f466ae-32f4-4d0f-9de7-96f955e69f7b",
193
+ operation: "record",
194
+ payload: { ...record, snapshotCapability: "signed-snapshot-capability" },
195
+ });
196
+
197
+ await action.run({ requestToken: "request-token" });
198
+
199
+ expect(mocks.verifySnapshotCapability).toHaveBeenCalledWith(
200
+ "signed-snapshot-capability",
201
+ record,
202
+ );
203
+ expect(mocks.recordGenerationFromSnapshot).toHaveBeenCalledWith(record, {
204
+ artifactAccess: undefined,
205
+ });
206
+ expect(mocks.recordGeneration).not.toHaveBeenCalled();
207
+ });
208
+
209
+ it("does not bypass pack checks without a snapshot capability", async () => {
210
+ const record = {
211
+ appId: "slides",
212
+ artifactType: "deck",
213
+ artifactId: "deck-1",
214
+ contextMode: "pinned",
215
+ contextPackId: "pack-1",
216
+ reuseLabels: [],
217
+ onlyIfMissing: true,
218
+ };
219
+ mocks.decodeRequest.mockReturnValue({
220
+ protocol: "creative-context-a2a-v1",
221
+ requestId: "87f466ae-32f4-4d0f-9de7-96f955e69f7b",
222
+ operation: "record",
223
+ payload: record,
224
+ });
225
+
226
+ await action.run({ requestToken: "request-token" });
227
+
228
+ expect(mocks.verifySnapshotCapability).not.toHaveBeenCalled();
229
+ expect(mocks.recordGeneration).toHaveBeenCalledWith(record, {
230
+ artifactAccess: undefined,
231
+ });
232
+ expect(mocks.recordGenerationFromSnapshot).not.toHaveBeenCalled();
233
+ });
234
+
235
+ it("rejects an invalid snapshot capability before storing provenance", async () => {
236
+ const record = {
237
+ appId: "slides",
238
+ artifactType: "deck",
239
+ artifactId: "deck-1",
240
+ contextMode: "pinned",
241
+ contextPackId: "pack-1",
242
+ reuseLabels: [],
243
+ onlyIfMissing: true,
244
+ };
245
+ mocks.decodeRequest.mockReturnValue({
246
+ protocol: "creative-context-a2a-v1",
247
+ requestId: "87f466ae-32f4-4d0f-9de7-96f955e69f7b",
248
+ operation: "record",
249
+ payload: { ...record, snapshotCapability: "forged" },
250
+ });
251
+ mocks.verifySnapshotCapability.mockRejectedValue(
252
+ new Error("Invalid Creative Context snapshot capability"),
253
+ );
254
+
255
+ await expect(action.run({ requestToken: "request-token" })).rejects.toThrow(
256
+ /invalid creative context snapshot capability/i,
257
+ );
258
+ expect(mocks.recordGeneration).not.toHaveBeenCalled();
259
+ expect(mocks.recordGenerationFromSnapshot).not.toHaveBeenCalled();
260
+ });
162
261
  });
@@ -2,7 +2,10 @@ import { defineAction } from "@agent-native/core/action";
2
2
  import { getRequestUserEmail } from "@agent-native/core/server";
3
3
  import { z } from "zod";
4
4
 
5
- import { verifyGenerationArtifactAccessCapability } from "../server/generation-artifact-access.js";
5
+ import {
6
+ verifyGenerationArtifactAccessCapability,
7
+ verifyGenerationCreativeContextSnapshotCapability,
8
+ } from "../server/generation-artifact-access.js";
6
9
  import {
7
10
  createCreativeContextA2AResponseToken,
8
11
  decodeCreativeContextA2ARequest,
@@ -60,7 +63,10 @@ export default defineAction({
60
63
  case "record": {
61
64
  const { recordGenerationCreativeContext } =
62
65
  await import("../store/generation.js");
63
- const { artifactAccessCapability, ...record } = request.payload;
66
+ const { recordGenerationCreativeContextFromSnapshot } =
67
+ await import("../store/generation.js");
68
+ const { artifactAccessCapability, snapshotCapability, ...record } =
69
+ request.payload;
64
70
  const artifactAccess = artifactAccessCapability
65
71
  ? await verifyGenerationArtifactAccessCapability(
66
72
  artifactAccessCapability,
@@ -68,9 +74,20 @@ export default defineAction({
68
74
  "record",
69
75
  )
70
76
  : undefined;
71
- result = await recordGenerationCreativeContext(record, {
72
- artifactAccess,
73
- });
77
+ if (snapshotCapability) {
78
+ const snapshot = { ...record, onlyIfMissing: true as const };
79
+ await verifyGenerationCreativeContextSnapshotCapability(
80
+ snapshotCapability,
81
+ snapshot,
82
+ );
83
+ result = await recordGenerationCreativeContextFromSnapshot(snapshot, {
84
+ artifactAccess,
85
+ });
86
+ } else {
87
+ result = await recordGenerationCreativeContext(record, {
88
+ artifactAccess,
89
+ });
90
+ }
74
91
  break;
75
92
  }
76
93
  }
@@ -56,6 +56,8 @@ export interface CreativeContextProjectionAdapters {
56
56
  }): Promise<void>;
57
57
  };
58
58
  generation?: {
59
+ // onlyIfMissing retries can replay this after the SQL row commits.
60
+ // Implementations must upsert by the artifact identity.
59
61
  record(input: {
60
62
  appId: string;
61
63
  artifactType: string;
@@ -18,7 +18,9 @@ vi.mock("@agent-native/core/server/request-context", () => ({
18
18
  import {
19
19
  assertGenerationArtifactAccessProof,
20
20
  createGenerationArtifactAccessCapability,
21
+ createGenerationCreativeContextSnapshotCapability,
21
22
  verifyGenerationArtifactAccessCapability,
23
+ verifyGenerationCreativeContextSnapshotCapability,
22
24
  } from "./generation-artifact-access.js";
23
25
 
24
26
  const originalKey = process.env.CREATIVE_CONTEXT_A2A_KEY;
@@ -27,6 +29,30 @@ const identity = {
27
29
  artifactType: "deck",
28
30
  artifactId: "deck-1",
29
31
  };
32
+ const snapshot = {
33
+ ...identity,
34
+ contextMode: "pinned" as const,
35
+ contextPackId: "pack-1",
36
+ reuseLabels: [
37
+ {
38
+ itemId: "item-1",
39
+ itemVersionId: "version-1",
40
+ kind: "brand-voice",
41
+ label: "Voice reference",
42
+ dataRole: "untrusted-reference" as const,
43
+ },
44
+ ],
45
+ elementProvenance: [
46
+ {
47
+ elementId: "slide-1",
48
+ influence: "reference-conditioned" as const,
49
+ itemId: "item-1",
50
+ itemVersionId: "version-1",
51
+ label: "Voice reference",
52
+ },
53
+ ],
54
+ onlyIfMissing: true as const,
55
+ };
30
56
 
31
57
  describe("generation artifact access capabilities", () => {
32
58
  beforeEach(() => {
@@ -146,4 +172,23 @@ describe("generation artifact access capabilities", () => {
146
172
  verifyGenerationArtifactAccessCapability(forged, identity, "read"),
147
173
  ).rejects.toThrow(/invalid generation artifact access capability/i);
148
174
  });
175
+
176
+ it("binds snapshot receipts to the exact provenance and caller", async () => {
177
+ const token =
178
+ await createGenerationCreativeContextSnapshotCapability(snapshot);
179
+
180
+ await expect(
181
+ verifyGenerationCreativeContextSnapshotCapability(token, snapshot),
182
+ ).resolves.toBeUndefined();
183
+ await expect(
184
+ verifyGenerationCreativeContextSnapshotCapability(token, {
185
+ ...snapshot,
186
+ contextPackId: "other-pack",
187
+ }),
188
+ ).rejects.toThrow(/invalid creative context snapshot capability/i);
189
+ mocks.getRequestUserEmail.mockReturnValue("mallory@example.test");
190
+ await expect(
191
+ verifyGenerationCreativeContextSnapshotCapability(token, snapshot),
192
+ ).rejects.toThrow(/invalid creative context snapshot capability/i);
193
+ });
149
194
  });
@@ -4,6 +4,11 @@ import {
4
4
  } from "@agent-native/core/server/request-context";
5
5
  import { assertAccess } from "@agent-native/core/sharing";
6
6
 
7
+ import type {
8
+ CreativeContextElementProvenance,
9
+ CreativeContextReuseLabel,
10
+ } from "../types.js";
11
+
7
12
  export interface GenerationArtifactIdentity {
8
13
  appId: string;
9
14
  artifactType: string;
@@ -40,6 +45,25 @@ interface CapabilityClaims {
40
45
  }
41
46
 
42
47
  const CAPABILITY_LIFETIME_MS = 60_000;
48
+ const SNAPSHOT_CAPABILITY_LIFETIME_MS = 5 * 60_000;
49
+
50
+ export type GenerationCreativeContextSnapshot = GenerationArtifactIdentity & {
51
+ contextMode: "off" | "auto" | "pinned";
52
+ contextPackId: string | null;
53
+ reuseLabels: CreativeContextReuseLabel[];
54
+ elementProvenance?: CreativeContextElementProvenance[];
55
+ onlyIfMissing: true;
56
+ };
57
+
58
+ interface SnapshotCapabilityClaims {
59
+ version: 1;
60
+ operation: "record-validated-snapshot";
61
+ identityKey: string;
62
+ snapshotDigest: string;
63
+ userEmail: string;
64
+ orgId: string | null;
65
+ expiresAt: number;
66
+ }
43
67
 
44
68
  export function generationArtifactAccessRole(
45
69
  target: GenerationArtifactAccessTarget,
@@ -153,6 +177,65 @@ export async function verifyGenerationArtifactAccessCapability(
153
177
  return createProof(identity, operation, claims.minRole);
154
178
  }
155
179
 
180
+ export async function createGenerationCreativeContextSnapshotCapability(
181
+ snapshot: GenerationCreativeContextSnapshot,
182
+ ): Promise<string> {
183
+ if (snapshot.onlyIfMissing !== true) {
184
+ throw new Error("Creative Context snapshot receipts require onlyIfMissing");
185
+ }
186
+ const actor = requireCapabilityActor();
187
+ const claims: SnapshotCapabilityClaims = {
188
+ version: 1,
189
+ operation: "record-validated-snapshot",
190
+ identityKey: generationIdentityKey(snapshot),
191
+ snapshotDigest: await generationSnapshotDigest(snapshot),
192
+ userEmail: actor.userEmail,
193
+ orgId: actor.orgId,
194
+ expiresAt: Date.now() + SNAPSHOT_CAPABILITY_LIFETIME_MS,
195
+ };
196
+ const encoded = Buffer.from(JSON.stringify(claims), "utf8").toString(
197
+ "base64url",
198
+ );
199
+ return `${encoded}.${await signCapability(encoded)}`;
200
+ }
201
+
202
+ export async function verifyGenerationCreativeContextSnapshotCapability(
203
+ token: string,
204
+ snapshot: GenerationCreativeContextSnapshot,
205
+ ): Promise<void> {
206
+ const [encoded, signature, extra] = token.split(".");
207
+ if (!encoded || !signature || extra) {
208
+ throw new Error("Invalid Creative Context snapshot capability");
209
+ }
210
+ const valid = await verifyCapabilitySignature(encoded, signature);
211
+ if (!valid) {
212
+ throw new Error("Invalid Creative Context snapshot capability");
213
+ }
214
+
215
+ let claims: SnapshotCapabilityClaims;
216
+ try {
217
+ claims = JSON.parse(
218
+ Buffer.from(encoded, "base64url").toString("utf8"),
219
+ ) as SnapshotCapabilityClaims;
220
+ } catch {
221
+ throw new Error("Invalid Creative Context snapshot capability");
222
+ }
223
+ const actor = requireCapabilityActor();
224
+ if (
225
+ claims.version !== 1 ||
226
+ claims.operation !== "record-validated-snapshot" ||
227
+ claims.identityKey !== generationIdentityKey(snapshot) ||
228
+ claims.snapshotDigest !== (await generationSnapshotDigest(snapshot)) ||
229
+ claims.userEmail !== actor.userEmail ||
230
+ claims.orgId !== actor.orgId ||
231
+ !Number.isSafeInteger(claims.expiresAt) ||
232
+ claims.expiresAt < Date.now() ||
233
+ claims.expiresAt > Date.now() + SNAPSHOT_CAPABILITY_LIFETIME_MS
234
+ ) {
235
+ throw new Error("Invalid Creative Context snapshot capability");
236
+ }
237
+ }
238
+
156
239
  function createProof(
157
240
  identity: GenerationArtifactIdentity,
158
241
  operation: GenerationArtifactAccessOperation,
@@ -174,6 +257,40 @@ function generationIdentityKey(identity: GenerationArtifactIdentity): string {
174
257
  ]);
175
258
  }
176
259
 
260
+ async function generationSnapshotDigest(
261
+ snapshot: GenerationCreativeContextSnapshot,
262
+ ): Promise<string> {
263
+ const canonicalSnapshot = JSON.stringify({
264
+ appId: snapshot.appId,
265
+ artifactType: snapshot.artifactType,
266
+ artifactId: snapshot.artifactId,
267
+ contextMode: snapshot.contextMode,
268
+ contextPackId: snapshot.contextPackId,
269
+ reuseLabels: snapshot.reuseLabels.map((label) => ({
270
+ itemId: label.itemId,
271
+ itemVersionId: label.itemVersionId,
272
+ kind: label.kind,
273
+ label: label.label,
274
+ dataRole: label.dataRole,
275
+ elementId: label.elementId,
276
+ influence: label.influence,
277
+ })),
278
+ elementProvenance: snapshot.elementProvenance?.map((entry) => ({
279
+ elementId: entry.elementId,
280
+ influence: entry.influence,
281
+ itemId: entry.itemId,
282
+ itemVersionId: entry.itemVersionId,
283
+ label: entry.label,
284
+ })),
285
+ onlyIfMissing: snapshot.onlyIfMissing,
286
+ });
287
+ const digest = await crypto.subtle.digest(
288
+ "SHA-256",
289
+ new TextEncoder().encode(canonicalSnapshot),
290
+ );
291
+ return Buffer.from(digest).toString("base64url");
292
+ }
293
+
177
294
  function requireCapabilityActor(): {
178
295
  userEmail: string;
179
296
  orgId: string | null;