@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.
- package/dist/actions/creative-context-a2a.js +15 -5
- package/dist/actions/creative-context-a2a.js.map +1 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js.map +1 -1
- package/dist/server/generation-artifact-access.d.ts +10 -0
- package/dist/server/generation-artifact-access.d.ts.map +1 -1
- package/dist/server/generation-artifact-access.js +75 -0
- package/dist/server/generation-artifact-access.js.map +1 -1
- package/dist/server/generation-context.d.ts +5 -0
- package/dist/server/generation-context.d.ts.map +1 -1
- package/dist/server/generation-context.js +36 -3
- package/dist/server/generation-context.js.map +1 -1
- package/dist/server/isolated-a2a.d.ts +4 -0
- package/dist/server/isolated-a2a.d.ts.map +1 -1
- package/dist/server/isolated-a2a.js +2 -0
- package/dist/server/isolated-a2a.js.map +1 -1
- package/dist/store/generation.d.ts +15 -0
- package/dist/store/generation.d.ts.map +1 -1
- package/dist/store/generation.js +65 -17
- package/dist/store/generation.js.map +1 -1
- package/package.json +5 -5
- package/src/actions/creative-context-a2a.spec.ts +99 -0
- package/src/actions/creative-context-a2a.ts +22 -5
- package/src/server/context.ts +2 -0
- package/src/server/generation-artifact-access.spec.ts +45 -0
- package/src/server/generation-artifact-access.ts +117 -0
- package/src/server/generation-context-a2a.spec.ts +76 -8
- package/src/server/generation-context.ts +58 -1
- package/src/server/isolated-a2a.spec.ts +23 -0
- package/src/server/isolated-a2a.ts +4 -0
- package/src/store/access.integration.spec.ts +145 -0
- package/src/store/generation.ts +109 -16
package/dist/store/generation.js
CHANGED
|
@@ -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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
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.
|
|
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.
|
|
62
|
-
"@agent-native/core": "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": "
|
|
66
|
-
"@agent-native/toolkit": "
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
72
|
-
|
|
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
|
}
|
package/src/server/context.ts
CHANGED
|
@@ -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;
|