@anthusai/papyrus 1.11.0 → 1.12.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.
Files changed (34) hide show
  1. package/amplify/data/resource.js +67 -0
  2. package/amplify/data/resource.ts +72 -0
  3. package/amplify/data/schema.js +67 -0
  4. package/amplify/data/schema.ts +72 -0
  5. package/amplify/functions/category-action/handler.ts +172 -2
  6. package/amplify/functions/content-actions/requirements.txt +1 -1
  7. package/amplify/functions/content-actions/resource.js +1 -1
  8. package/amplify/functions/content-actions/resource.ts +1 -1
  9. package/amplify/functions/renditions/requirements.txt +1 -1
  10. package/amplify/functions/shared/curation-delegation.ts +67 -0
  11. package/amplify/functions/shared/reference-curation-metadata.ts +20 -0
  12. package/amplify/publication/renditions-function.js +1 -1
  13. package/amplify/publication/renditions-function.ts +1 -1
  14. package/components/administration-delegations-panel.tsx +204 -0
  15. package/components/curation-delegation-client.ts +81 -0
  16. package/components/news-desk-taxonomy-client.ts +7 -0
  17. package/components/newsroom-organizer.css +22 -0
  18. package/components/newsroom-references-view.tsx +56 -21
  19. package/components/prospectus-account-chooser.tsx +14 -19
  20. package/components/reference-cyclotron-status.tsx +5 -5
  21. package/components/reference-cyclotron-suggestion.tsx +143 -0
  22. package/components/reference-live-agreement.tsx +62 -0
  23. package/components/topic-steering-workspace.tsx +49 -3
  24. package/lib/content-source-context.ts +9 -1
  25. package/lib/curation-delegation-helpers.ts +59 -0
  26. package/lib/cyclotron-suggestions.ts +263 -0
  27. package/lib/graphql-content-repository.ts +42 -7
  28. package/lib/local-preview-seed.ts +141 -3
  29. package/lib/local-preview-tenant.ts +27 -0
  30. package/lib/prospectus-tenant.ts +22 -0
  31. package/lib/relevance-decisions.ts +29 -0
  32. package/lib/tenant-keys.ts +32 -0
  33. package/package.json +10 -1
  34. package/routes.manifest.json +1 -1
@@ -0,0 +1,22 @@
1
+ import {
2
+ PROSPECTUS_SESSION_COOKIE,
3
+ loadProspectusSessionSealingSecret,
4
+ openProspectusCookie,
5
+ type ProspectusSignedInSession,
6
+ } from "./prospectus-session";
7
+ import { tenantIdForProspectusAccount } from "./tenant-keys";
8
+
9
+ type CookieReader = { get(name: string): { value: string } | undefined };
10
+
11
+ /**
12
+ * The tenant of the publishing account the signed-in person selected, read from the sealed Prospectus session
13
+ * cookie. Returns undefined on a site without Prospectus, with no session, or with no account selected, so the
14
+ * caller falls back to the site's own tenant (PAPYRUS_TENANT_ID).
15
+ */
16
+ export async function selectedTenantIdFromCookies(cookies: CookieReader, loadSecret: () => Promise<string | null> = loadProspectusSessionSealingSecret): Promise<string | undefined> {
17
+ const sealingSecret = await loadSecret();
18
+ if (!sealingSecret) return undefined;
19
+ const session = openProspectusCookie<ProspectusSignedInSession>(sealingSecret, PROSPECTUS_SESSION_COOKIE, cookies.get(PROSPECTUS_SESSION_COOKIE)?.value);
20
+ if (!session || session.accessTokenExpiresAt * 1000 <= Date.now()) return undefined;
21
+ return tenantIdForProspectusAccount(session.selectedAccountId);
22
+ }
@@ -2,6 +2,15 @@ import type { SemanticRelationRecord } from "./category-repository";
2
2
 
3
3
  export const RELEVANCE_RELATION = "relevance_decision_is";
4
4
 
5
+ /** What produced a suggestion beyond the model version, as the sweep recorded it. */
6
+ export type RelevanceProvenance = {
7
+ labelSet: string[];
8
+ doctrineFingerprint: string | null;
9
+ teachingFingerprint: string | null;
10
+ teachingLabels: number;
11
+ primingExamples: number;
12
+ };
13
+
5
14
  /** The relevance cyclotron's current decision about one reference, as the References tab shows it. */
6
15
  export type RelevanceDecisionView = {
7
16
  decisionRelationId: string;
@@ -19,6 +28,10 @@ export type RelevanceDecisionView = {
19
28
  /** Thumbs for a two-class decision with a positive label; per-class buttons otherwise. */
20
29
  reviewControl: "thumbs" | "labels";
21
30
  cyclotronId: string | null;
31
+ /** When the cyclotron made the suggestion, if the decision recorded it. */
32
+ decidedAt: string | null;
33
+ /** Null for decisions made before provenance was recorded. */
34
+ provenance: RelevanceProvenance | null;
22
35
  };
23
36
 
24
37
  /** What an editor submits from the review control. */
@@ -49,6 +62,20 @@ function parseMetadata(value: unknown): Record<string, unknown> {
49
62
  }
50
63
  }
51
64
 
65
+ function parseProvenance(value: unknown): RelevanceProvenance | null {
66
+ const record = parseMetadata(value);
67
+ if (!Object.keys(record).length) return null;
68
+ const count = (entry: unknown) => (Number.isFinite(Number(entry)) ? Number(entry) : 0);
69
+ const text = (entry: unknown) => (typeof entry === "string" && entry ? entry : null);
70
+ return {
71
+ labelSet: Array.isArray(record.labelSet) ? record.labelSet.map(String) : [],
72
+ doctrineFingerprint: text(record.doctrineFingerprint),
73
+ teachingFingerprint: text(record.teachingFingerprint),
74
+ teachingLabels: count(record.teachingLabels),
75
+ primingExamples: count(record.primingExamples),
76
+ };
77
+ }
78
+
52
79
  /**
53
80
  * Current decisions keyed by reference lineage, from relations the Newsroom
54
81
  * already loads: no extra request per row.
@@ -77,6 +104,8 @@ export function currentRelevanceDecisions(relations: SemanticRelationRecord[]):
77
104
  question: typeof metadata.question === "string" ? metadata.question : null,
78
105
  reviewControl: metadata.reviewControl === "thumbs" ? "thumbs" : "labels",
79
106
  cyclotronId: typeof metadata.cyclotronId === "string" ? metadata.cyclotronId : null,
107
+ decidedAt: typeof metadata.decidedAt === "string" ? metadata.decidedAt : null,
108
+ provenance: parseProvenance(metadata.provenance),
80
109
  });
81
110
  }
82
111
  return decisions;
@@ -0,0 +1,32 @@
1
+ export const TENANT_ID_ENVIRONMENT_VARIABLE = "PAPYRUS_TENANT_ID";
2
+ const TENANT_KEY_SEPARATOR = "#";
3
+ const TENANT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
4
+
5
+ export function validateTenantId(tenantId: string): string {
6
+ if (!TENANT_ID_PATTERN.test(tenantId)) {
7
+ throw new Error(`Tenant id "${tenantId}" must be lowercase letters, digits and hyphens, starting with a letter or digit.`);
8
+ }
9
+ return tenantId;
10
+ }
11
+
12
+ export function tenantIdFromEnvironment(environment: NodeJS.ProcessEnv = process.env): string | undefined {
13
+ const configured = environment[TENANT_ID_ENVIRONMENT_VARIABLE]?.trim();
14
+ return configured ? validateTenantId(configured) : undefined;
15
+ }
16
+
17
+ export function tenantScopedValue(tenantId: string | undefined, value: string): string {
18
+ if (!tenantId) return value;
19
+ const prefix = `${tenantId}${TENANT_KEY_SEPARATOR}`;
20
+ return value.startsWith(prefix) ? value : `${prefix}${value}`;
21
+ }
22
+
23
+ export function rowBelongsToTenant(tenantId: string | undefined, row: { tenantId?: string | null } | null | undefined): boolean {
24
+ if (!tenantId || !row) return true;
25
+ return row.tenantId === tenantId;
26
+ }
27
+
28
+ /** The tenant a Prospectus publishing account maps to: its id as lowercase letters, digits and hyphens. */
29
+ export function tenantIdForProspectusAccount(accountId: string | null | undefined): string | undefined {
30
+ const normalized = (accountId ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
31
+ return normalized ? validateTenantId(normalized) : undefined;
32
+ }
package/package.json CHANGED
@@ -64,6 +64,7 @@
64
64
  "types": "./infra/amplify-app-shell.ts",
65
65
  "default": "./infra/index.js"
66
66
  },
67
+ "./components/administration-delegations-panel": "./components/administration-delegations-panel.tsx",
67
68
  "./components/ai-elements/conversation": "./components/ai-elements/conversation.tsx",
68
69
  "./components/ai-elements/model-selector": "./components/ai-elements/model-selector.tsx",
69
70
  "./components/ai-elements/prompt-input": "./components/ai-elements/prompt-input.tsx",
@@ -76,6 +77,7 @@
76
77
  "./components/article-video": "./components/article-video.tsx",
77
78
  "./components/blog-page-background": "./components/blog-page-background.tsx",
78
79
  "./components/board-card-excerpts": "./components/board-card-excerpts.tsx",
80
+ "./components/curation-delegation-client": "./components/curation-delegation-client.ts",
79
81
  "./components/dev-sandbox-editor-sign-in": "./components/dev-sandbox-editor-sign-in.tsx",
80
82
  "./components/markus-ir-preview": "./components/markus-ir-preview.tsx",
81
83
  "./components/news-desk-client-provider": "./components/news-desk-client-provider.tsx",
@@ -110,6 +112,8 @@
110
112
  "./components/reference-curation-controls": "./components/reference-curation-controls.tsx",
111
113
  "./components/reference-cyclotron-priming": "./components/reference-cyclotron-priming.tsx",
112
114
  "./components/reference-cyclotron-status": "./components/reference-cyclotron-status.tsx",
115
+ "./components/reference-cyclotron-suggestion": "./components/reference-cyclotron-suggestion.tsx",
116
+ "./components/reference-live-agreement": "./components/reference-live-agreement.tsx",
113
117
  "./components/reference-priming": "./components/reference-priming.tsx",
114
118
  "./components/reference-relevance-review": "./components/reference-relevance-review.tsx",
115
119
  "./components/reference-source-preview": "./components/reference-source-preview.tsx",
@@ -172,6 +176,8 @@
172
176
  "./lib/content-source-context": "./lib/content-source-context.ts",
173
177
  "./lib/content-types": "./lib/content-types.ts",
174
178
  "./lib/curation-client": "./lib/curation-client.ts",
179
+ "./lib/curation-delegation-helpers": "./lib/curation-delegation-helpers.ts",
180
+ "./lib/cyclotron-suggestions": "./lib/cyclotron-suggestions.ts",
175
181
  "./lib/define-site": "./lib/define-site.ts",
176
182
  "./lib/demo-amplify-outputs": "./lib/demo-amplify-outputs.ts",
177
183
  "./lib/dev-sandbox-editor-auth": "./lib/dev-sandbox-editor-auth.ts",
@@ -192,6 +198,7 @@
192
198
  "./lib/layout-types": "./lib/layout-types.ts",
193
199
  "./lib/layout-warnings": "./lib/layout-warnings.ts",
194
200
  "./lib/local-preview-seed": "./lib/local-preview-seed.ts",
201
+ "./lib/local-preview-tenant": "./lib/local-preview-tenant.ts",
195
202
  "./lib/local-preview": "./lib/local-preview.ts",
196
203
  "./lib/markus-body": "./lib/markus-body.ts",
197
204
  "./lib/markus-ir": "./lib/markus-ir.ts",
@@ -234,6 +241,7 @@
234
241
  "./lib/prospectus-session": "./lib/prospectus-session.ts",
235
242
  "./lib/prospectus-sign-in-routes": "./lib/prospectus-sign-in-routes.ts",
236
243
  "./lib/prospectus-site-identity": "./lib/prospectus-site-identity.ts",
244
+ "./lib/prospectus-tenant": "./lib/prospectus-tenant.ts",
237
245
  "./lib/public-placeholder-config": "./lib/public-placeholder-config.ts",
238
246
  "./lib/publication-cyclotron": "./lib/publication-cyclotron.ts",
239
247
  "./lib/publication-items": "./lib/publication-items.ts",
@@ -267,6 +275,7 @@
267
275
  "./lib/staging-gated-path": "./lib/staging-gated-path.ts",
268
276
  "./lib/staging-preview-object": "./lib/staging-preview-object.ts",
269
277
  "./lib/story-reporting-trail": "./lib/story-reporting-trail.ts",
278
+ "./lib/tenant-keys": "./lib/tenant-keys.ts",
270
279
  "./lib/test-editor-override": "./lib/test-editor-override.ts",
271
280
  "./lib/text-columns": "./lib/text-columns.ts",
272
281
  "./lib/themed-image": "./lib/themed-image.ts",
@@ -365,7 +374,7 @@
365
374
  "optional": true
366
375
  }
367
376
  },
368
- "version": "1.11.0",
377
+ "version": "1.12.0",
369
378
  "dependencies": {
370
379
  "@aws-amplify/adapter-nextjs": "^1.7.3",
371
380
  "@aws-crypto/sha256-js": "5.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.11.0",
2
+ "version": "1.12.0",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",