@anthusai/papyrus 2.0.0-next.11 → 2.0.0-next.110

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 (227) hide show
  1. package/amplify/auth/resource.js +16 -1
  2. package/amplify/auth/resource.ts +19 -1
  3. package/amplify/data/function-handler.js +20 -0
  4. package/amplify/data/function-handler.ts +40 -0
  5. package/amplify/data/resource.js +152 -62
  6. package/amplify/data/resource.ts +158 -62
  7. package/amplify/data/schema.js +2101 -1987
  8. package/amplify/data/schema.ts +197 -70
  9. package/amplify/functions/assignment-action/data-access.js +2 -0
  10. package/amplify/functions/assignment-action/data-access.ts +2 -0
  11. package/amplify/functions/assignment-action/handler.ts +1 -1
  12. package/amplify/functions/automation-view/handler.py +17 -0
  13. package/amplify/functions/automation-view/resource.js +24 -0
  14. package/amplify/functions/automation-view/resource.ts +34 -0
  15. package/amplify/functions/automation-view/schema.js +26 -0
  16. package/amplify/functions/automation-view/schema.ts +28 -0
  17. package/amplify/functions/automation-view/wire.js +56 -0
  18. package/amplify/functions/automation-view/wire.ts +76 -0
  19. package/amplify/functions/content-actions/handler.py +64 -3
  20. package/amplify/functions/content-actions/requirements.txt +1 -0
  21. package/amplify/functions/content-actions/resource.js +1 -1
  22. package/amplify/functions/content-actions/resource.ts +1 -1
  23. package/amplify/functions/editorial-checks/handler.py +2 -2
  24. package/amplify/functions/editorial-checks/requirements.txt +1 -1
  25. package/amplify/functions/editorial-checks/resource.js +3 -3
  26. package/amplify/functions/editorial-checks/resource.ts +3 -3
  27. package/amplify/functions/editorial-checks/schema.js +38 -8
  28. package/amplify/functions/editorial-checks/schema.ts +38 -8
  29. package/amplify/functions/editorial-checks/wire.js +4 -0
  30. package/amplify/functions/editorial-checks/wire.ts +4 -0
  31. package/amplify/functions/monitoring-view/handler.py +11 -0
  32. package/amplify/functions/monitoring-view/resource.js +24 -0
  33. package/amplify/functions/monitoring-view/resource.ts +34 -0
  34. package/amplify/functions/monitoring-view/schema.js +29 -0
  35. package/amplify/functions/monitoring-view/schema.ts +31 -0
  36. package/amplify/functions/monitoring-view/wire.js +64 -0
  37. package/amplify/functions/monitoring-view/wire.ts +81 -0
  38. package/amplify/functions/public-forms/core.js +247 -0
  39. package/amplify/functions/public-forms/core.ts +310 -0
  40. package/amplify/functions/public-forms/handler.ts +41 -0
  41. package/amplify/functions/public-forms/origins.js +35 -0
  42. package/amplify/functions/public-forms/origins.ts +43 -0
  43. package/amplify/functions/public-forms/resource.js +9 -0
  44. package/amplify/functions/public-forms/resource.ts +10 -0
  45. package/amplify/functions/public-forms/wire.js +25 -0
  46. package/amplify/functions/public-forms/wire.ts +32 -0
  47. package/amplify/functions/shared/editorial-context.ts +63 -4
  48. package/amplify/functions/shared/python-bundle.js +20 -14
  49. package/amplify/functions/shared/python-bundle.ts +25 -18
  50. package/amplify/plugins/newsletter/config.js +245 -0
  51. package/amplify/plugins/newsletter/config.ts +305 -0
  52. package/amplify/plugins/newsletter/function/handler.py +6 -0
  53. package/amplify/plugins/newsletter/function/requirements.txt +5 -0
  54. package/amplify/plugins/newsletter/ops/handler.py +12 -0
  55. package/amplify/plugins/newsletter/ops/resource.js +27 -0
  56. package/amplify/plugins/newsletter/ops/resource.ts +37 -0
  57. package/amplify/plugins/newsletter/papyrus.plugin.js +263 -0
  58. package/amplify/plugins/newsletter/papyrus.plugin.ts +311 -0
  59. package/amplify/plugins/newsletter/schema.js +57 -0
  60. package/amplify/plugins/newsletter/schema.ts +62 -0
  61. package/amplify/plugins/newsletter/secure-string-parameter.js +107 -0
  62. package/amplify/plugins/newsletter/secure-string-parameter.ts +120 -0
  63. package/amplify/plugins/newsletter/sending.js +140 -0
  64. package/amplify/plugins/newsletter/sending.ts +186 -0
  65. package/amplify/plugins/reader-steering/config.js +182 -0
  66. package/amplify/plugins/reader-steering/config.ts +290 -0
  67. package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
  68. package/amplify/plugins/reader-steering/function/handler.py +1589 -0
  69. package/amplify/plugins/reader-steering/function/moderation-prompt.md +27 -0
  70. package/amplify/plugins/reader-steering/function/moderation.py +316 -0
  71. package/amplify/plugins/reader-steering/function/proposals.py +487 -0
  72. package/amplify/plugins/reader-steering/function/resource.js +27 -0
  73. package/amplify/plugins/reader-steering/function/resource.ts +35 -0
  74. package/amplify/plugins/reader-steering/papyrus.plugin.js +187 -0
  75. package/amplify/plugins/reader-steering/papyrus.plugin.ts +213 -0
  76. package/amplify/plugins/reader-steering/schema.js +252 -0
  77. package/amplify/plugins/reader-steering/schema.ts +254 -0
  78. package/amplify/plugins/registry.js +4 -0
  79. package/amplify/plugins/registry.ts +4 -0
  80. package/amplify/plugins/signup/schema.js +2 -1
  81. package/amplify/plugins/signup/schema.ts +2 -1
  82. package/amplify/plugins/types.ts +5 -0
  83. package/amplify/plugins/wire.js +1 -0
  84. package/amplify/plugins/wire.ts +1 -0
  85. package/amplify/publication/projection-lease.js +30 -0
  86. package/amplify/publication/projection-lease.ts +39 -0
  87. package/amplify/publication/redirect-and-index.js +105 -0
  88. package/amplify/publication/redirect-rules.js +73 -0
  89. package/amplify/publication/redirect-rules.ts +93 -0
  90. package/amplify/publication/s3-projection.js +7 -1
  91. package/amplify/publication/s3-projection.ts +9 -1
  92. package/amplify/site-backend-config.js +19 -0
  93. package/amplify/site-backend-config.ts +45 -1
  94. package/amplify/site-backend-names.js +4 -2
  95. package/amplify/site-backend-names.ts +4 -2
  96. package/amplify/site-backend.js +42 -4
  97. package/amplify/site-backend.ts +45 -4
  98. package/app/.well-known/papyrus-cli.json/route.ts +9 -0
  99. package/app/api/auth/prospectus/sign-in/route.ts +4 -0
  100. package/app/api/newsletter/confirm/route.ts +13 -0
  101. package/app/api/newsletter/preferences/route.ts +13 -0
  102. package/app/api/newsletter/state/route.ts +9 -0
  103. package/app/api/newsletter/subscribe/route.ts +9 -0
  104. package/app/api/newsletter/unsubscribe/route.ts +13 -0
  105. package/app/api/newsroom/chat/[...path]/route.ts +17 -0
  106. package/app/api/newsroom/curation/context/route.ts +9 -0
  107. package/app/api/newsroom/curation/priming/route.ts +9 -0
  108. package/app/api/newsroom/curation/publication/route.ts +9 -0
  109. package/app/api/reader/comment/delete/route.ts +9 -0
  110. package/app/api/reader/comment/edit/route.ts +9 -0
  111. package/app/api/reader/comment/route.ts +9 -0
  112. package/app/api/reader/flag/route.ts +9 -0
  113. package/app/api/reader/handle/route.ts +9 -0
  114. package/app/api/reader/me/route.ts +9 -0
  115. package/app/api/reader/rating/route.ts +9 -0
  116. package/app/api/reader/report/route.ts +9 -0
  117. package/app/api/reader/retired/route.ts +9 -0
  118. package/app/api/reader/submit/route.ts +9 -0
  119. package/app/api/reader/vote/route.ts +9 -0
  120. package/app/layout.tsx +5 -1
  121. package/bin/papyrus-infra.mjs +20 -1
  122. package/components/automation-panel.tsx +49 -0
  123. package/components/automation-run-now.tsx +97 -0
  124. package/components/automation-view.tsx +195 -0
  125. package/components/editorial-checks-admin-panel.tsx +32 -17
  126. package/components/moderation-panel.tsx +248 -0
  127. package/components/monitoring-overview-view.tsx +422 -0
  128. package/components/monitoring-panel.tsx +60 -0
  129. package/components/news-desk-client-provider.tsx +18 -18
  130. package/components/news-desk-taxonomy-client.ts +28 -8
  131. package/components/newsletter-panel.tsx +474 -0
  132. package/components/newsletter-sections.tsx +163 -0
  133. package/components/newsroom-access-gate.tsx +16 -2
  134. package/components/newsroom-article-editor.tsx +9 -7
  135. package/components/newsroom-articles-view.tsx +6 -6
  136. package/components/newsroom-assignment-detail.tsx +6 -6
  137. package/components/newsroom-board-view.tsx +8 -3
  138. package/components/newsroom-curation-bootstrap.tsx +45 -0
  139. package/components/newsroom-ops-overview.tsx +26 -96
  140. package/components/newsroom-ops-shell.tsx +2 -2
  141. package/components/newsroom-organizer.css +33 -0
  142. package/components/newsroom-overview-skeleton.tsx +25 -0
  143. package/components/newsroom-references-view.tsx +192 -49
  144. package/components/notification-center.tsx +281 -0
  145. package/components/papyrus-console-conversation.css +14 -0
  146. package/components/papyrus-console-conversation.tsx +55 -0
  147. package/components/papyrus-console-shell.tsx +62 -420
  148. package/components/papyrus-private-chat-panel.tsx +134 -0
  149. package/components/publication-cyclotron-panel.tsx +15 -10
  150. package/components/reader-auth-state.ts +1 -1
  151. package/components/reference-add-entry.tsx +75 -0
  152. package/components/reference-curation-controls.tsx +31 -22
  153. package/components/reference-cyclotron-priming.tsx +58 -19
  154. package/components/reference-cyclotron-suggestion.tsx +10 -6
  155. package/components/reference-priming.tsx +14 -10
  156. package/components/reference-relevance-review.tsx +3 -3
  157. package/components/reference-section-suggestion.tsx +1 -1
  158. package/components/topic-steering-workspace.tsx +230 -144
  159. package/infra/amplify-app-shell.js +96 -16
  160. package/infra/amplify-app-shell.ts +108 -18
  161. package/infra/cloud-email-config.js +32 -0
  162. package/infra/cloud-email-config.ts +39 -0
  163. package/infra/cloud-email.js +57 -0
  164. package/infra/cloud-email.test.js +69 -0
  165. package/infra/cloud-email.test.ts +66 -0
  166. package/infra/cloud-email.ts +60 -0
  167. package/infra/index.js +1 -0
  168. package/infra/secure-string-parameter-handler.js +41 -0
  169. package/infra/secure-string-parameter-handler.ts +41 -0
  170. package/infra/secure-string-parameter.js +79 -0
  171. package/infra/secure-string-parameter.ts +91 -0
  172. package/infra/site-config.js +92 -6
  173. package/infra/site-config.ts +138 -6
  174. package/lib/automation-client.ts +54 -0
  175. package/lib/automation-fixture.ts +93 -0
  176. package/lib/automation-overview.ts +252 -0
  177. package/lib/category-repository.ts +14 -12
  178. package/lib/cli-login-discovery.ts +78 -0
  179. package/lib/console-web-context.ts +1 -1
  180. package/lib/curation-routes.ts +345 -0
  181. package/lib/cyclotron-suggestions.ts +4 -4
  182. package/lib/editorial-checks-client.ts +18 -0
  183. package/lib/editorial-checks-demo.ts +1 -1
  184. package/lib/editorial-checks.ts +8 -4
  185. package/lib/editorial-preview.ts +5 -5
  186. package/lib/monitoring-client.ts +83 -0
  187. package/lib/monitoring-overview-fixture.ts +51 -0
  188. package/lib/monitoring-overview.ts +191 -0
  189. package/lib/news-desk-session.ts +12 -21
  190. package/lib/newsletter-api.ts +418 -0
  191. package/lib/newsletter-client.ts +46 -0
  192. package/lib/newsletter-fixture.ts +184 -0
  193. package/lib/newsletter-model.ts +202 -0
  194. package/lib/newsroom-demo-dashboard.ts +1 -1
  195. package/lib/newsroom-nav.ts +1 -1
  196. package/lib/newsroom-organizer-nav.ts +2 -1
  197. package/lib/papyrus-chat-admission.ts +65 -0
  198. package/lib/papyrus-hosted-chat.ts +217 -0
  199. package/lib/papyrus-private-chat-client.ts +81 -0
  200. package/lib/prospectus-session.ts +30 -14
  201. package/lib/prospectus-sign-in-routes.ts +106 -31
  202. package/lib/prospectus-site-identity.ts +46 -6
  203. package/lib/reader-api.ts +145 -0
  204. package/lib/reader-moderation.ts +220 -0
  205. package/lib/reader-retirement-route.ts +89 -0
  206. package/lib/reader-subject.md +29 -0
  207. package/lib/reader-subject.ts +53 -0
  208. package/lib/reference-review-details.ts +71 -0
  209. package/lib/reference-review-request.ts +14 -0
  210. package/lib/relevance-decisions.ts +42 -10
  211. package/lib/relevance-status.ts +2 -2
  212. package/lib/review-client.ts +120 -0
  213. package/lib/review-fixture.ts +45 -0
  214. package/lib/review-requests.ts +246 -0
  215. package/lib/semantic-graph.ts +2 -0
  216. package/lib/shared-chat-admission.ts +182 -0
  217. package/lib/site-brand-tenant.ts +11 -6
  218. package/lib/site-brand.ts +6 -3
  219. package/lib/site-origin-value.ts +50 -0
  220. package/lib/site-origin.ts +8 -0
  221. package/lib/use-reference-review-data.ts +44 -0
  222. package/middleware.ts +14 -3
  223. package/package.json +67 -3
  224. package/routes.manifest.json +238 -2
  225. package/src/sync.mjs +11 -2
  226. package/src/with-papyrus.d.mts +1 -0
  227. package/src/with-papyrus.mjs +17 -1
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Code-local trust contract for a site-hosted shared chat adapter.
3
+ *
4
+ * This module does not verify JWT signatures and is not an HTTP endpoint. The
5
+ * hosting server must inject a real verifier and authoritative membership
6
+ * resolver. Never construct VerifiedChatCredential from request JSON.
7
+ */
8
+
9
+ export type TokenUse = string;
10
+
11
+ /** Claims returned only after the injected host verifier has validated the JWT. */
12
+ export interface VerifiedChatCredential {
13
+ subject: string;
14
+ issuer: string;
15
+ audiences: readonly string[];
16
+ clientId?: string;
17
+ tokenUse?: TokenUse;
18
+ expiresAtEpochSeconds: number;
19
+ notBeforeEpochSeconds?: number;
20
+ issuedAtEpochSeconds?: number;
21
+ }
22
+
23
+ export interface AudienceRule {
24
+ /** Explicitly choose which JWT claim is authoritative for this host. */
25
+ claim: "aud" | "client_id";
26
+ expectedValue: string;
27
+ }
28
+
29
+ export interface ChatHostTrustProfile {
30
+ issuer: string;
31
+ audienceRule: AudienceRule;
32
+ /** Must be configured by the host after verifying its actual token contract. */
33
+ acceptedTokenUses: readonly TokenUse[];
34
+ configuredSiteId: string;
35
+ clockSkewSeconds?: number;
36
+ }
37
+
38
+ export interface ChatRequest {
39
+ authorization?: string;
40
+ channelId: string;
41
+ idempotencyKey: string;
42
+ message: { text: string; authorSub?: string; accountId?: string; siteId?: string };
43
+ }
44
+
45
+ export interface ChatMembership {
46
+ accountId: string;
47
+ siteId: string;
48
+ editorSub: string;
49
+ active: boolean;
50
+ /** Host-derived authorization for the editorial chat surface (editor/admin policy). */
51
+ canUseEditorialChat: boolean;
52
+ }
53
+
54
+ export interface TranscriptOwner {
55
+ accountId: string;
56
+ siteId: string;
57
+ editorSub: string;
58
+ channelId: string;
59
+ }
60
+
61
+ export interface SharedChatAdmissionDependencies {
62
+ trust: ChatHostTrustProfile;
63
+ nowEpochSeconds?: () => number;
64
+ verifyCredential: (rawCredential: string) => Promise<VerifiedChatCredential | null>;
65
+ resolveMembership: (subject: string, configuredSiteId: string) => Promise<ChatMembership | null>;
66
+ resolveTranscriptOwner: (channelId: string) => Promise<TranscriptOwner | null>;
67
+ }
68
+
69
+ export interface AdmittedChatMessage {
70
+ owner: TranscriptOwner;
71
+ idempotencyKey: string;
72
+ /** Stable canonical representation used for payload-reuse detection. */
73
+ payloadFingerprint: string;
74
+ }
75
+
76
+ export class ChatAdmissionError extends Error {
77
+ readonly code: "unauthorized" | "forbidden" | "invalid_request" | "idempotency_conflict";
78
+
79
+ constructor(code: ChatAdmissionError["code"]) {
80
+ super(code);
81
+ this.name = "ChatAdmissionError";
82
+ this.code = code;
83
+ }
84
+ }
85
+
86
+ const reject = (code: ChatAdmissionError["code"]): never => { throw new ChatAdmissionError(code); };
87
+
88
+ function bearerToken(value: string | undefined): string {
89
+ const match = value?.match(/^Bearer ([^\s]+)$/i);
90
+ if (!match) return reject("unauthorized");
91
+ return match[1];
92
+ }
93
+
94
+ function assertCredential(credential: VerifiedChatCredential, trust: ChatHostTrustProfile, now: number): void {
95
+ const skew = Math.max(0, trust.clockSkewSeconds ?? 0);
96
+ if (!trust.issuer || credential.issuer !== trust.issuer) reject("unauthorized");
97
+ if (!trust.acceptedTokenUses.length || !credential.tokenUse || !trust.acceptedTokenUses.includes(credential.tokenUse)) reject("unauthorized");
98
+ if (!Number.isFinite(credential.expiresAtEpochSeconds) || now - skew >= credential.expiresAtEpochSeconds) reject("unauthorized");
99
+ if (credential.notBeforeEpochSeconds !== undefined && now + skew < credential.notBeforeEpochSeconds) reject("unauthorized");
100
+ if (credential.issuedAtEpochSeconds !== undefined && now + skew < credential.issuedAtEpochSeconds) reject("unauthorized");
101
+
102
+ const audienceMatches = trust.audienceRule.claim === "aud"
103
+ ? credential.audiences.includes(trust.audienceRule.expectedValue)
104
+ : credential.clientId === trust.audienceRule.expectedValue;
105
+ if (!audienceMatches) reject("unauthorized");
106
+ }
107
+
108
+ /** Authenticate, derive the current host membership, and enforce private channel ownership. */
109
+ export async function admitSharedChatMessage(
110
+ request: ChatRequest,
111
+ dependencies: SharedChatAdmissionDependencies,
112
+ ): Promise<AdmittedChatMessage> {
113
+ if (!request.channelId || !request.message.text.trim() || !/^[A-Za-z0-9._~-]{16,128}$/.test(request.idempotencyKey)) {
114
+ return reject("invalid_request");
115
+ }
116
+
117
+ const rawCredential = bearerToken(request.authorization);
118
+ const credential = await dependencies.verifyCredential(rawCredential);
119
+ if (!credential) return reject("unauthorized");
120
+ const now = (dependencies.nowEpochSeconds ?? (() => Date.now() / 1000))();
121
+ assertCredential(credential, dependencies.trust, now);
122
+
123
+ const membership = await dependencies.resolveMembership(credential.subject, dependencies.trust.configuredSiteId);
124
+ if (!membership || !membership.active || !membership.canUseEditorialChat
125
+ || membership.editorSub !== credential.subject || membership.siteId !== dependencies.trust.configuredSiteId) {
126
+ return reject("forbidden");
127
+ }
128
+ if ((request.message.authorSub && request.message.authorSub !== membership.editorSub)
129
+ || (request.message.accountId && request.message.accountId !== membership.accountId)
130
+ || (request.message.siteId && request.message.siteId !== membership.siteId)) {
131
+ return reject("forbidden");
132
+ }
133
+
134
+ const expectedOwner: TranscriptOwner = {
135
+ accountId: membership.accountId,
136
+ siteId: membership.siteId,
137
+ editorSub: membership.editorSub,
138
+ channelId: request.channelId,
139
+ };
140
+ const actualOwner = await dependencies.resolveTranscriptOwner(request.channelId);
141
+ if (!actualOwner || actualOwner.accountId !== expectedOwner.accountId || actualOwner.siteId !== expectedOwner.siteId
142
+ || actualOwner.editorSub !== expectedOwner.editorSub || actualOwner.channelId !== expectedOwner.channelId) {
143
+ return reject("forbidden");
144
+ }
145
+
146
+ return {
147
+ owner: expectedOwner,
148
+ idempotencyKey: request.idempotencyKey,
149
+ payloadFingerprint: JSON.stringify([request.channelId, request.message.text]),
150
+ };
151
+ }
152
+
153
+ export interface IdempotencyRequest<T> {
154
+ /** Storage must atomically scope key by the derived owner and operation. */
155
+ scope: string;
156
+ key: string;
157
+ payloadFingerprint: string;
158
+ run: () => Promise<T>;
159
+ }
160
+
161
+ /**
162
+ * Store contract for replay-safe writes. Production implementations must be
163
+ * atomic/durable (for example conditional put + canonical result); this module
164
+ * deliberately supplies no in-memory production store.
165
+ */
166
+ export interface ChatIdempotencyStore {
167
+ executeOnce<T>(request: IdempotencyRequest<T>): Promise<T>;
168
+ }
169
+
170
+ export function runAdmittedChatMessage<T>(
171
+ admission: AdmittedChatMessage,
172
+ store: ChatIdempotencyStore,
173
+ operation: () => Promise<T>,
174
+ ): Promise<T> {
175
+ const { accountId, siteId, editorSub, channelId } = admission.owner;
176
+ return store.executeOnce({
177
+ scope: JSON.stringify(["chat-message", accountId, siteId, editorSub, channelId]),
178
+ key: admission.idempotencyKey,
179
+ payloadFingerprint: admission.payloadFingerprint,
180
+ run: operation,
181
+ });
182
+ }
@@ -9,14 +9,19 @@ export function resolveBrandConfigPath(relativePath: string): string {
9
9
  return path.isAbsolute(relativePath) ? relativePath : path.join(process.cwd(), relativePath);
10
10
  }
11
11
 
12
- export function getBrandSteeringConfigPath(brand: SiteBrand = resolveActiveSiteBrand()): string {
13
- return resolveBrandConfigPath(brand.steeringConfigPath);
12
+ /** Optional repo files a brand may still name; undefined means the site keeps that configuration as backend data and nothing is looked up. */
13
+ function optionalBrandConfigPath(relativePath: string | undefined): string | undefined {
14
+ return relativePath ? resolveBrandConfigPath(relativePath) : undefined;
14
15
  }
15
16
 
16
- export function getBrandNewsroomSectionsConfigPath(brand: SiteBrand = resolveActiveSiteBrand()): string {
17
- return resolveBrandConfigPath(brand.newsroomSectionsConfigPath);
17
+ export function getBrandSteeringConfigPath(brand: SiteBrand = resolveActiveSiteBrand()): string | undefined {
18
+ return optionalBrandConfigPath(brand.steeringConfigPath);
18
19
  }
19
20
 
20
- export function getBrandAnalysisProfilesPath(brand: SiteBrand = resolveActiveSiteBrand()): string {
21
- return resolveBrandConfigPath(brand.analysisProfilesPath);
21
+ export function getBrandNewsroomSectionsConfigPath(brand: SiteBrand = resolveActiveSiteBrand()): string | undefined {
22
+ return optionalBrandConfigPath(brand.newsroomSectionsConfigPath);
23
+ }
24
+
25
+ export function getBrandAnalysisProfilesPath(brand: SiteBrand = resolveActiveSiteBrand()): string | undefined {
26
+ return optionalBrandConfigPath(brand.analysisProfilesPath);
22
27
  }
package/lib/site-brand.ts CHANGED
@@ -142,9 +142,12 @@ export type SiteBrand = {
142
142
  readerBasePath?: string;
143
143
  opsChrome: OpsChrome;
144
144
  corpusKey: string;
145
- steeringConfigPath: string;
146
- newsroomSectionsConfigPath: string;
147
- analysisProfilesPath: string;
145
+ /** Optional repo file for topic steering. Editorial and steering configuration is backend data; a data-driven site sets none and nothing is looked up. */
146
+ steeringConfigPath?: string;
147
+ /** Optional repo file for the local-preview demo sections only. Sections live in the backend. */
148
+ newsroomSectionsConfigPath?: string;
149
+ /** Optional repo file listing analysis profiles. */
150
+ analysisProfilesPath?: string;
148
151
  publicationName: string;
149
152
  components?: SiteBrandComponents;
150
153
  /**
@@ -0,0 +1,50 @@
1
+ import type { NextRequest } from "next/server";
2
+ import { getSiteEnv } from "./site-env";
3
+
4
+ type EnvironmentVariables = Record<string, string | undefined>;
5
+
6
+ export const SITE_ORIGIN_ENV = "PAPYRUS_SITE_ORIGIN";
7
+
8
+ export class SiteOriginNotConfiguredError extends Error {
9
+ readonly status = 503;
10
+ readonly code = "site_origin_not_configured";
11
+ }
12
+
13
+ function isLoopbackHostname(hostname: string): boolean {
14
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
15
+ }
16
+
17
+ function configuredOrigin(value: string): string {
18
+ const invalid = () => new SiteOriginNotConfiguredError(`${SITE_ORIGIN_ENV} must be an https origin such as https://example.com with no path.`);
19
+ let parsed: URL;
20
+ try {
21
+ parsed = new URL(value);
22
+ } catch {
23
+ throw invalid();
24
+ }
25
+ if (parsed.protocol !== "https:" || parsed.origin !== value) throw invalid();
26
+ return parsed.origin;
27
+ }
28
+
29
+ function siteEnvOrNull(environment: EnvironmentVariables): string | null {
30
+ try {
31
+ return getSiteEnv(environment);
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ /** The site's public origin from trusted configuration, never Host or forwarded headers. */
38
+ export function publicSiteOrigin(request: Pick<NextRequest, "nextUrl">, environment: EnvironmentVariables = process.env): string {
39
+ const configured = (environment[SITE_ORIGIN_ENV] ?? "").trim();
40
+ if (configured) return configuredOrigin(configured);
41
+ const { hostname, origin } = request.nextUrl;
42
+ const developing = siteEnvOrNull(environment) === "development" && environment.NODE_ENV !== "production";
43
+ if (isLoopbackHostname(hostname) && developing) return origin;
44
+ throw new SiteOriginNotConfiguredError(`${SITE_ORIGIN_ENV} is not set; this site cannot tell its public origin.`);
45
+ }
46
+
47
+ /** An absolute URL on the site's trusted public origin. */
48
+ export function publicSiteUrl(path: string, request: Pick<NextRequest, "nextUrl">, environment: EnvironmentVariables = process.env): URL {
49
+ return new URL(path, publicSiteOrigin(request, environment));
50
+ }
@@ -0,0 +1,8 @@
1
+ import { NextResponse } from "next/server";
2
+ import { SiteOriginNotConfiguredError } from "./site-origin-value";
3
+
4
+ export { SITE_ORIGIN_ENV, SiteOriginNotConfiguredError, publicSiteOrigin, publicSiteUrl } from "./site-origin-value";
5
+
6
+ export function siteOriginNotConfiguredResponse(error: SiteOriginNotConfiguredError): NextResponse {
7
+ return NextResponse.json({ error: error.code, message: error.message }, { status: error.status, headers: { "cache-control": "no-store" } });
8
+ }
@@ -0,0 +1,44 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useState } from "react";
4
+
5
+ /** Load persisted predictions and attributed review history without cancelling on a loading rerender. */
6
+ export function useReferenceReviewData<Relation, Message>({
7
+ enabled,
8
+ loadRelations,
9
+ loadMessages,
10
+ onRelations,
11
+ onMessages,
12
+ }: {
13
+ enabled: boolean;
14
+ loadRelations: () => Promise<Relation[]>;
15
+ loadMessages: () => Promise<Message[]>;
16
+ onRelations: (relations: Relation[]) => void;
17
+ onMessages: (messages: Message[]) => void;
18
+ }) {
19
+ const [state, setState] = useState<"loading" | "ready" | "error">(enabled ? "loading" : "ready");
20
+ const [error, setError] = useState<string | null>(null);
21
+ const [attempt, setAttempt] = useState(0);
22
+ const retry = useCallback(() => setAttempt((current) => current + 1), []);
23
+ useEffect(() => {
24
+ if (!enabled) return;
25
+ let active = true;
26
+ setState("loading");
27
+ setError(null);
28
+ void Promise.allSettled([loadRelations(), loadMessages()]).then(([relations, history]) => {
29
+ if (!active) return;
30
+ if (relations.status === "fulfilled") onRelations(relations.value);
31
+ if (history.status === "fulfilled") onMessages(history.value);
32
+ const failure = relations.status === "rejected" ? relations.reason : history.status === "rejected" ? history.reason : null;
33
+ if (failure) {
34
+ const subject = relations.status === "rejected" ? "Predictions" : "Review history";
35
+ setError(`${subject} could not be loaded. ${failure instanceof Error ? failure.message : "Try again."}`);
36
+ setState("error");
37
+ } else {
38
+ setState("ready");
39
+ }
40
+ });
41
+ return () => { active = false; };
42
+ }, [enabled, loadRelations, loadMessages, onRelations, onMessages, attempt]);
43
+ return { state, error, retry };
44
+ }
package/middleware.ts CHANGED
@@ -5,6 +5,7 @@ import { newsroomHref } from "./lib/newsroom-base-path";
5
5
  import { getReaderBasePath } from "./lib/reader-base-path";
6
6
  import { isStagingGatedPath, isStaticOrApiPath } from "./lib/staging-gated-path";
7
7
  import { getSiteEnv, isIndexable } from "./lib/site-env";
8
+ import { SiteOriginNotConfiguredError, publicSiteUrl, siteOriginNotConfiguredResponse } from "./lib/site-origin";
8
9
  import { getStagingAccess } from "./lib/staging-gate";
9
10
  import {
10
11
  isPreviewGatedPath,
@@ -38,11 +39,11 @@ function routeRequest(request: NextRequest): NextResponse {
38
39
  }
39
40
 
40
41
  if (pathname === "/newsroom" || pathname === "/newsroom/") {
41
- return NextResponse.redirect(new URL(`/${request.nextUrl.search}`, request.url));
42
+ return NextResponse.redirect(publicSiteUrl(`/${request.nextUrl.search}`, request));
42
43
  }
43
44
  if (pathname.startsWith("/newsroom/")) {
44
45
  const target = pathname.slice("/newsroom".length) || "/";
45
- return NextResponse.redirect(new URL(`${target}${request.nextUrl.search}`, request.url));
46
+ return NextResponse.redirect(publicSiteUrl(`${target}${request.nextUrl.search}`, request));
46
47
  }
47
48
 
48
49
  if (pathname === "/" || pathname === "") {
@@ -59,6 +60,16 @@ function routeRequest(request: NextRequest): NextResponse {
59
60
  }
60
61
 
61
62
  export async function middleware(request: NextRequest): Promise<NextResponse> {
63
+ try {
64
+ return await handleRequest(request);
65
+ } catch (error) {
66
+ // Redirects name the site's public origin (request.url is localhost on Amplify); without it, fail closed.
67
+ if (error instanceof SiteOriginNotConfiguredError) return withRobotsHeader(siteOriginNotConfiguredResponse(error));
68
+ throw error;
69
+ }
70
+ }
71
+
72
+ async function handleRequest(request: NextRequest): Promise<NextResponse> {
62
73
  let sessionResponse: NextResponse | null = null;
63
74
  const { pathname } = request.nextUrl;
64
75
  const gated = isStaticPreviewEnabled()
@@ -68,7 +79,7 @@ export async function middleware(request: NextRequest): Promise<NextResponse> {
68
79
  sessionResponse = NextResponse.next();
69
80
  const access = await getStagingAccess(request, sessionResponse);
70
81
  if (access === "anonymous") {
71
- return withRobotsHeader(NextResponse.redirect(new URL(newsroomHref(), request.url)));
82
+ return withRobotsHeader(NextResponse.redirect(publicSiteUrl(newsroomHref(), request)));
72
83
  }
73
84
  if (access === "forbidden") {
74
85
  return withRobotsHeader(new NextResponse("Staging is limited to editors and admins.", { status: 403 }));
package/package.json CHANGED
@@ -75,6 +75,9 @@
75
75
  "./components/archive-grid": "./components/archive-grid.tsx",
76
76
  "./components/archive-shell": "./components/archive-shell.tsx",
77
77
  "./components/article-video": "./components/article-video.tsx",
78
+ "./components/automation-panel": "./components/automation-panel.tsx",
79
+ "./components/automation-run-now": "./components/automation-run-now.tsx",
80
+ "./components/automation-view": "./components/automation-view.tsx",
78
81
  "./components/blog-page-background": "./components/blog-page-background.tsx",
79
82
  "./components/curation-delegation-client": "./components/curation-delegation-client.ts",
80
83
  "./components/dev-sandbox-editor-sign-in": "./components/dev-sandbox-editor-sign-in.tsx",
@@ -85,9 +88,14 @@
85
88
  "./components/item-stream-view": "./components/item-stream-view.tsx",
86
89
  "./components/markus-body-editor": "./components/markus-body-editor.tsx",
87
90
  "./components/markus-ir-preview": "./components/markus-ir-preview.tsx",
91
+ "./components/moderation-panel": "./components/moderation-panel.tsx",
92
+ "./components/monitoring-overview-view": "./components/monitoring-overview-view.tsx",
93
+ "./components/monitoring-panel": "./components/monitoring-panel.tsx",
88
94
  "./components/news-desk-client-provider": "./components/news-desk-client-provider.tsx",
89
95
  "./components/news-desk-page": "./components/news-desk-page.tsx",
90
96
  "./components/news-desk-taxonomy-client": "./components/news-desk-taxonomy-client.ts",
97
+ "./components/newsletter-panel": "./components/newsletter-panel.tsx",
98
+ "./components/newsletter-sections": "./components/newsletter-sections.tsx",
91
99
  "./components/newspaper": "./components/newspaper.tsx",
92
100
  "./components/newsroom-access-gate": "./components/newsroom-access-gate.tsx",
93
101
  "./components/newsroom-article-editor": "./components/newsroom-article-editor.tsx",
@@ -96,9 +104,11 @@
96
104
  "./components/newsroom-assignments-view": "./components/newsroom-assignments-view.tsx",
97
105
  "./components/newsroom-board-view": "./components/newsroom-board-view.tsx",
98
106
  "./components/newsroom-client-shell": "./components/newsroom-client-shell.tsx",
107
+ "./components/newsroom-curation-bootstrap": "./components/newsroom-curation-bootstrap.tsx",
99
108
  "./components/newsroom-curation-provider": "./components/newsroom-curation-provider.tsx",
100
109
  "./components/newsroom-ops-overview": "./components/newsroom-ops-overview.tsx",
101
110
  "./components/newsroom-ops-shell": "./components/newsroom-ops-shell.tsx",
111
+ "./components/newsroom-overview-skeleton": "./components/newsroom-overview-skeleton.tsx",
102
112
  "./components/newsroom-references-view": "./components/newsroom-references-view.tsx",
103
113
  "./components/newsroom-theme-fonts/broadsheet-green": "./components/newsroom-theme-fonts/broadsheet-green.tsx",
104
114
  "./components/newsroom-theme-fonts/newspaper-gothic": "./components/newsroom-theme-fonts/newspaper-gothic.tsx",
@@ -106,7 +116,10 @@
106
116
  "./components/newsroom-theme-fonts/refined-newsprint": "./components/newsroom-theme-fonts/refined-newsprint.tsx",
107
117
  "./components/newsroom-theme": "./components/newsroom-theme.tsx",
108
118
  "./components/newsroom-topics-view": "./components/newsroom-topics-view.tsx",
119
+ "./components/notification-center": "./components/notification-center.tsx",
120
+ "./components/papyrus-console-conversation": "./components/papyrus-console-conversation.tsx",
109
121
  "./components/papyrus-console-shell": "./components/papyrus-console-shell.tsx",
122
+ "./components/papyrus-private-chat-panel": "./components/papyrus-private-chat-panel.tsx",
110
123
  "./components/pictogram-figure": "./components/pictogram-figure.tsx",
111
124
  "./components/presentation-footer": "./components/presentation-footer.tsx",
112
125
  "./components/presentation-header": "./components/presentation-header.tsx",
@@ -118,6 +131,7 @@
118
131
  "./components/reader-auth-state": "./components/reader-auth-state.ts",
119
132
  "./components/reader-settings": "./components/reader-settings.ts",
120
133
  "./components/reader-theme-control": "./components/reader-theme-control.tsx",
134
+ "./components/reference-add-entry": "./components/reference-add-entry.tsx",
121
135
  "./components/reference-curation-controls": "./components/reference-curation-controls.tsx",
122
136
  "./components/reference-cyclotron-priming": "./components/reference-cyclotron-priming.tsx",
123
137
  "./components/reference-cyclotron-status": "./components/reference-cyclotron-status.tsx",
@@ -169,6 +183,9 @@
169
183
  "./lib/article-flow": "./lib/article-flow.ts",
170
184
  "./lib/article-frame-solver": "./lib/article-frame-solver.ts",
171
185
  "./lib/articles": "./lib/articles.ts",
186
+ "./lib/automation-client": "./lib/automation-client.ts",
187
+ "./lib/automation-fixture": "./lib/automation-fixture.ts",
188
+ "./lib/automation-overview": "./lib/automation-overview.ts",
172
189
  "./lib/blog-feature-solver": "./lib/blog-feature-solver.ts",
173
190
  "./lib/blog-rhythm": "./lib/blog-rhythm.ts",
174
191
  "./lib/brand-fonts-watch": "./lib/brand-fonts-watch.ts",
@@ -177,6 +194,7 @@
177
194
  "./lib/category-dashboard": "./lib/category-dashboard.ts",
178
195
  "./lib/category-repository": "./lib/category-repository.ts",
179
196
  "./lib/chrome-text": "./lib/chrome-text.ts",
197
+ "./lib/cli-login-discovery": "./lib/cli-login-discovery.ts",
180
198
  "./lib/console-chat-client": "./lib/console-chat-client.ts",
181
199
  "./lib/console-web-context": "./lib/console-web-context.ts",
182
200
  "./lib/content-actions-client": "./lib/content-actions-client.ts",
@@ -188,6 +206,7 @@
188
206
  "./lib/content-types": "./lib/content-types.ts",
189
207
  "./lib/curation-client": "./lib/curation-client.ts",
190
208
  "./lib/curation-delegation-helpers": "./lib/curation-delegation-helpers.ts",
209
+ "./lib/curation-routes": "./lib/curation-routes.ts",
191
210
  "./lib/cyclotron-suggestions": "./lib/cyclotron-suggestions.ts",
192
211
  "./lib/define-site": "./lib/define-site.ts",
193
212
  "./lib/demo-amplify-outputs": "./lib/demo-amplify-outputs.ts",
@@ -224,7 +243,14 @@
224
243
  "./lib/markus-projection": "./lib/markus-projection.ts",
225
244
  "./lib/markus-to-article": "./lib/markus-to-article.ts",
226
245
  "./lib/media-route-response": "./lib/media-route-response.ts",
246
+ "./lib/monitoring-client": "./lib/monitoring-client.ts",
247
+ "./lib/monitoring-overview-fixture": "./lib/monitoring-overview-fixture.ts",
248
+ "./lib/monitoring-overview": "./lib/monitoring-overview.ts",
227
249
  "./lib/news-desk-session": "./lib/news-desk-session.ts",
250
+ "./lib/newsletter-api": "./lib/newsletter-api.ts",
251
+ "./lib/newsletter-client": "./lib/newsletter-client.ts",
252
+ "./lib/newsletter-fixture": "./lib/newsletter-fixture.ts",
253
+ "./lib/newsletter-model": "./lib/newsletter-model.ts",
228
254
  "./lib/newspaper-layout": "./lib/newspaper-layout.ts",
229
255
  "./lib/newsroom-articles": "./lib/newsroom-articles.ts",
230
256
  "./lib/newsroom-assignments": "./lib/newsroom-assignments.ts",
@@ -248,7 +274,10 @@
248
274
  "./lib/newsroom-sections": "./lib/newsroom-sections.ts",
249
275
  "./lib/newsroom-topics": "./lib/newsroom-topics.ts",
250
276
  "./lib/oauth-redirect-priority": "./lib/oauth-redirect-priority.ts",
277
+ "./lib/papyrus-chat-admission": "./lib/papyrus-chat-admission.ts",
278
+ "./lib/papyrus-hosted-chat": "./lib/papyrus-hosted-chat.ts",
251
279
  "./lib/papyrus-object-kinds": "./lib/papyrus-object-kinds.ts",
280
+ "./lib/papyrus-private-chat-client": "./lib/papyrus-private-chat-client.ts",
252
281
  "./lib/papyrus-web-locations": "./lib/papyrus-web-locations.ts",
253
282
  "./lib/pilobol-sample": "./lib/pilobol-sample.ts",
254
283
  "./lib/plugin-settings": "./lib/plugin-settings.ts",
@@ -265,13 +294,19 @@
265
294
  "./lib/publication-cyclotron": "./lib/publication-cyclotron.ts",
266
295
  "./lib/publication-items": "./lib/publication-items.ts",
267
296
  "./lib/rate-limit-retry": "./lib/rate-limit-retry.ts",
297
+ "./lib/reader-api": "./lib/reader-api.ts",
268
298
  "./lib/reader-base-path": "./lib/reader-base-path.ts",
269
299
  "./lib/reader-cache-lifetime": "./lib/reader-cache-lifetime.ts",
300
+ "./lib/reader-moderation": "./lib/reader-moderation.ts",
301
+ "./lib/reader-retirement-route": "./lib/reader-retirement-route.ts",
270
302
  "./lib/reader-route-config": "./lib/reader-route-config.ts",
271
303
  "./lib/reader-static-params": "./lib/reader-static-params.ts",
272
304
  "./lib/reader-storage-url": "./lib/reader-storage-url.ts",
305
+ "./lib/reader-subject": "./lib/reader-subject.ts",
273
306
  "./lib/reference-display": "./lib/reference-display.ts",
274
307
  "./lib/reference-policy": "./lib/reference-policy.ts",
308
+ "./lib/reference-review-details": "./lib/reference-review-details.ts",
309
+ "./lib/reference-review-request": "./lib/reference-review-request.ts",
275
310
  "./lib/reference-source-preview": "./lib/reference-source-preview.ts",
276
311
  "./lib/relevance-decisions": "./lib/relevance-decisions.ts",
277
312
  "./lib/relevance-status": "./lib/relevance-status.ts",
@@ -280,14 +315,20 @@
280
315
  "./lib/reporting-story-budget": "./lib/reporting-story-budget.ts",
281
316
  "./lib/revalidate-prewarm": "./lib/revalidate-prewarm.ts",
282
317
  "./lib/revalidate-secret": "./lib/revalidate-secret.ts",
318
+ "./lib/review-client": "./lib/review-client.ts",
319
+ "./lib/review-fixture": "./lib/review-fixture.ts",
320
+ "./lib/review-requests": "./lib/review-requests.ts",
283
321
  "./lib/robots-policy": "./lib/robots-policy.ts",
284
322
  "./lib/root-route-redirect": "./lib/root-route-redirect.ts",
285
323
  "./lib/semantic-graph": "./lib/semantic-graph.ts",
324
+ "./lib/shared-chat-admission": "./lib/shared-chat-admission.ts",
286
325
  "./lib/shared-credentials-provider": "./lib/shared-credentials-provider.ts",
287
326
  "./lib/signup-csv": "./lib/signup-csv.ts",
288
327
  "./lib/site-brand-tenant": "./lib/site-brand-tenant.ts",
289
328
  "./lib/site-brand": "./lib/site-brand.ts",
290
329
  "./lib/site-env": "./lib/site-env.ts",
330
+ "./lib/site-origin-value": "./lib/site-origin-value.ts",
331
+ "./lib/site-origin": "./lib/site-origin.ts",
291
332
  "./lib/site-renderer": "./lib/site-renderer.ts",
292
333
  "./lib/site-section-suggestions": "./lib/site-section-suggestions.ts",
293
334
  "./lib/site-stack": "./lib/site-stack.ts",
@@ -299,6 +340,7 @@
299
340
  "./lib/test-editor-override": "./lib/test-editor-override.ts",
300
341
  "./lib/text-columns": "./lib/text-columns.ts",
301
342
  "./lib/themed-image": "./lib/themed-image.ts",
343
+ "./lib/use-reference-review-data": "./lib/use-reference-review-data.ts",
302
344
  "./lib/utils": "./lib/utils.ts",
303
345
  "./lib/video-loading": "./lib/video-loading.ts",
304
346
  "./lib/video-mode": "./lib/video-mode.ts",
@@ -315,6 +357,7 @@
315
357
  "./renderers/pretext/rhythm-blog-presentation": "./renderers/pretext/rhythm-blog-presentation.tsx",
316
358
  "./renderers/pretext/rhythm-shell-style": "./renderers/pretext/rhythm-shell-style.ts",
317
359
  "./app/%5F_preview/[[...path]]/route": "./app/%5F_preview/[[...path]]/route.ts",
360
+ "./app/.well-known/papyrus-cli.json/route": "./app/.well-known/papyrus-cli.json/route.ts",
318
361
  "./app/[year]/[month]/[day]/[articleSlug]/page": "./app/[year]/[month]/[day]/[articleSlug]/page.tsx",
319
362
  "./app/[year]/[month]/[day]/page": "./app/[year]/[month]/[day]/page.tsx",
320
363
  "./app/[year]/[month]/[day]/page/[pageNumber]/page": "./app/[year]/[month]/[day]/page/[pageNumber]/page.tsx",
@@ -330,6 +373,26 @@
330
373
  "./app/api/local-preview/derive/route": "./app/api/local-preview/derive/route.ts",
331
374
  "./app/api/local-preview/media/route": "./app/api/local-preview/media/route.ts",
332
375
  "./app/api/media/[...path]/route": "./app/api/media/[...path]/route.ts",
376
+ "./app/api/newsletter/confirm/route": "./app/api/newsletter/confirm/route.ts",
377
+ "./app/api/newsletter/preferences/route": "./app/api/newsletter/preferences/route.ts",
378
+ "./app/api/newsletter/state/route": "./app/api/newsletter/state/route.ts",
379
+ "./app/api/newsletter/subscribe/route": "./app/api/newsletter/subscribe/route.ts",
380
+ "./app/api/newsletter/unsubscribe/route": "./app/api/newsletter/unsubscribe/route.ts",
381
+ "./app/api/newsroom/chat/[...path]/route": "./app/api/newsroom/chat/[...path]/route.ts",
382
+ "./app/api/newsroom/curation/context/route": "./app/api/newsroom/curation/context/route.ts",
383
+ "./app/api/newsroom/curation/priming/route": "./app/api/newsroom/curation/priming/route.ts",
384
+ "./app/api/newsroom/curation/publication/route": "./app/api/newsroom/curation/publication/route.ts",
385
+ "./app/api/reader/comment/delete/route": "./app/api/reader/comment/delete/route.ts",
386
+ "./app/api/reader/comment/edit/route": "./app/api/reader/comment/edit/route.ts",
387
+ "./app/api/reader/comment/route": "./app/api/reader/comment/route.ts",
388
+ "./app/api/reader/flag/route": "./app/api/reader/flag/route.ts",
389
+ "./app/api/reader/handle/route": "./app/api/reader/handle/route.ts",
390
+ "./app/api/reader/me/route": "./app/api/reader/me/route.ts",
391
+ "./app/api/reader/rating/route": "./app/api/reader/rating/route.ts",
392
+ "./app/api/reader/report/route": "./app/api/reader/report/route.ts",
393
+ "./app/api/reader/retired/route": "./app/api/reader/retired/route.ts",
394
+ "./app/api/reader/submit/route": "./app/api/reader/submit/route.ts",
395
+ "./app/api/reader/vote/route": "./app/api/reader/vote/route.ts",
333
396
  "./app/api/revalidate/route": "./app/api/revalidate/route.ts",
334
397
  "./app/archive/page": "./app/archive/page.tsx",
335
398
  "./app/articles/[slug]/page": "./app/articles/[slug]/page.tsx",
@@ -395,14 +458,14 @@
395
458
  "optional": true
396
459
  }
397
460
  },
398
- "version": "2.0.0-next.11",
461
+ "version": "2.0.0-next.110",
399
462
  "dependencies": {
400
463
  "@aws-amplify/adapter-nextjs": "^1.7.3",
401
464
  "@aws-crypto/sha256-js": "5.2.0",
402
465
  "@aws-sdk/client-cognito-identity-provider": "3.1045.0",
466
+ "@aws-sdk/client-dynamodb": "^3.1045.0",
403
467
  "@aws-sdk/client-lambda": "^3.1045.0",
404
468
  "@aws-sdk/client-s3": "^3.1045.0",
405
- "@aws-sdk/client-secrets-manager": "3.1045.0",
406
469
  "@aws-sdk/client-ssm": "^3.1045.0",
407
470
  "@aws-sdk/credential-provider-node": "3.972.84",
408
471
  "@aws-sdk/s3-request-presigner": "^3.1045.0",
@@ -432,10 +495,11 @@
432
495
  "anthus-vultus": "github:AnthusAI/Vultus#af98d57",
433
496
  "aws-amplify": "^6.17.0",
434
497
  "aws-jwt-verify": "4.0.1",
498
+ "chatticus": "https://github.com/AnthusAI/Chatticus/archive/db862e386119f97922cf303fd00ef0505d86a08a.tar.gz",
435
499
  "class-variance-authority": "^0.7.1",
436
500
  "clsx": "^2.1.1",
437
501
  "cmdk": "^1.1.1",
438
- "cyclotron": "https://github.com/AnthusAI/Cyclotron/releases/download/ui-v0.2.2/cyclotron-0.2.2.tgz",
502
+ "cyclotron": "https://github.com/AnthusAI/Cyclotron/releases/download/ui-v0.2.3/cyclotron-0.2.3.tgz",
439
503
  "d3": "^7.9.0",
440
504
  "esbuild": "^0.28.2",
441
505
  "framer-motion": "^14.3.0",