@prenta/core 6.0.0 → 7.0.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 (430) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/ai/brand-voice-analyze.d.ts +1 -4
  3. package/dist/ai/brand-voice-analyze.d.ts.map +1 -1
  4. package/dist/ai/brand-voice-analyze.js +1 -2
  5. package/dist/ai/brand-voice-analyze.js.map +1 -1
  6. package/dist/ai/brand-voice-sources.d.ts +0 -2
  7. package/dist/ai/brand-voice-sources.d.ts.map +1 -1
  8. package/dist/ai/brand-voice-sources.js +2 -2
  9. package/dist/ai/brand-voice-sources.js.map +1 -1
  10. package/dist/ai/brand-voice-structured.d.ts +6 -6
  11. package/dist/ai/brand-voice-structured.d.ts.map +1 -1
  12. package/dist/ai/catalog.d.ts +8 -0
  13. package/dist/ai/catalog.d.ts.map +1 -1
  14. package/dist/ai/catalog.js +22 -0
  15. package/dist/ai/catalog.js.map +1 -1
  16. package/dist/ai/config-store.d.ts +18 -4
  17. package/dist/ai/config-store.d.ts.map +1 -1
  18. package/dist/ai/config-store.js +115 -20
  19. package/dist/ai/config-store.js.map +1 -1
  20. package/dist/ai/content-writing-generate.d.ts +10 -8
  21. package/dist/ai/content-writing-generate.d.ts.map +1 -1
  22. package/dist/ai/content-writing-generate.js +24 -56
  23. package/dist/ai/content-writing-generate.js.map +1 -1
  24. package/dist/ai/embeddings.d.ts +28 -0
  25. package/dist/ai/embeddings.d.ts.map +1 -0
  26. package/dist/ai/embeddings.js +36 -0
  27. package/dist/ai/embeddings.js.map +1 -0
  28. package/dist/ai/feature-defaults.d.ts +3 -3
  29. package/dist/ai/feature-defaults.d.ts.map +1 -1
  30. package/dist/ai/feature-defaults.js +24 -14
  31. package/dist/ai/feature-defaults.js.map +1 -1
  32. package/dist/ai/index.d.ts +15 -16
  33. package/dist/ai/index.d.ts.map +1 -1
  34. package/dist/ai/index.js +14 -15
  35. package/dist/ai/index.js.map +1 -1
  36. package/dist/ai/models.d.ts +7 -3
  37. package/dist/ai/models.d.ts.map +1 -1
  38. package/dist/ai/models.js +10 -5
  39. package/dist/ai/models.js.map +1 -1
  40. package/dist/ai/plugin-errors.d.ts +9 -3
  41. package/dist/ai/plugin-errors.d.ts.map +1 -1
  42. package/dist/ai/plugin-errors.js +9 -3
  43. package/dist/ai/plugin-errors.js.map +1 -1
  44. package/dist/ai/plugin-import.d.ts +9 -1
  45. package/dist/ai/plugin-import.d.ts.map +1 -1
  46. package/dist/ai/plugin-import.js +9 -1
  47. package/dist/ai/plugin-import.js.map +1 -1
  48. package/dist/ai/prompt-safety.d.ts +23 -0
  49. package/dist/ai/prompt-safety.d.ts.map +1 -0
  50. package/dist/ai/prompt-safety.js +34 -0
  51. package/dist/ai/prompt-safety.js.map +1 -0
  52. package/dist/ai/providers/anthropic.d.ts.map +1 -1
  53. package/dist/ai/providers/anthropic.js +16 -1
  54. package/dist/ai/providers/anthropic.js.map +1 -1
  55. package/dist/ai/providers/http.d.ts +7 -2
  56. package/dist/ai/providers/http.d.ts.map +1 -1
  57. package/dist/ai/providers/http.js +13 -2
  58. package/dist/ai/providers/http.js.map +1 -1
  59. package/dist/ai/providers/index.d.ts +0 -7
  60. package/dist/ai/providers/index.d.ts.map +1 -1
  61. package/dist/ai/providers/index.js +1 -1
  62. package/dist/ai/providers/index.js.map +1 -1
  63. package/dist/ai/resolve.d.ts +7 -1
  64. package/dist/ai/resolve.d.ts.map +1 -1
  65. package/dist/ai/resolve.js +36 -3
  66. package/dist/ai/resolve.js.map +1 -1
  67. package/dist/ai/runtime-response.d.ts +27 -0
  68. package/dist/ai/runtime-response.d.ts.map +1 -0
  69. package/dist/ai/runtime-response.js +75 -0
  70. package/dist/ai/runtime-response.js.map +1 -0
  71. package/dist/ai/runtime.d.ts +9 -7
  72. package/dist/ai/runtime.d.ts.map +1 -1
  73. package/dist/ai/runtime.js +14 -19
  74. package/dist/ai/runtime.js.map +1 -1
  75. package/dist/ai/tasks/alt-text.d.ts +82 -0
  76. package/dist/ai/tasks/alt-text.d.ts.map +1 -0
  77. package/dist/ai/tasks/alt-text.js +171 -0
  78. package/dist/ai/tasks/alt-text.js.map +1 -0
  79. package/dist/ai/tasks/document-generation.d.ts +102 -0
  80. package/dist/ai/tasks/document-generation.d.ts.map +1 -0
  81. package/dist/ai/tasks/document-generation.js +321 -0
  82. package/dist/ai/tasks/document-generation.js.map +1 -0
  83. package/dist/ai/tasks/link-embeddings.d.ts +55 -0
  84. package/dist/ai/tasks/link-embeddings.d.ts.map +1 -0
  85. package/dist/ai/tasks/link-embeddings.js +160 -0
  86. package/dist/ai/tasks/link-embeddings.js.map +1 -0
  87. package/dist/ai/types.d.ts +15 -19
  88. package/dist/ai/types.d.ts.map +1 -1
  89. package/dist/ai/types.js +6 -7
  90. package/dist/ai/types.js.map +1 -1
  91. package/dist/ai/usage.d.ts +6 -4
  92. package/dist/ai/usage.d.ts.map +1 -1
  93. package/dist/ai/usage.js +6 -4
  94. package/dist/ai/usage.js.map +1 -1
  95. package/dist/ai/vector.d.ts +8 -0
  96. package/dist/ai/vector.d.ts.map +1 -0
  97. package/dist/ai/vector.js +24 -0
  98. package/dist/ai/vector.js.map +1 -0
  99. package/dist/api/responses.d.ts +1 -1
  100. package/dist/api/responses.d.ts.map +1 -1
  101. package/dist/api/responses.js +1 -1
  102. package/dist/api/responses.js.map +1 -1
  103. package/dist/api/route-helpers.d.ts +2 -19
  104. package/dist/api/route-helpers.d.ts.map +1 -1
  105. package/dist/api/route-helpers.js +11 -22
  106. package/dist/api/route-helpers.js.map +1 -1
  107. package/dist/api/routes/ai-authoring.d.ts.map +1 -1
  108. package/dist/api/routes/ai-authoring.js +132 -144
  109. package/dist/api/routes/ai-authoring.js.map +1 -1
  110. package/dist/api/routes/ai-seo.d.ts.map +1 -1
  111. package/dist/api/routes/ai-seo.js +44 -120
  112. package/dist/api/routes/ai-seo.js.map +1 -1
  113. package/dist/api/routes/ai-settings.d.ts.map +1 -1
  114. package/dist/api/routes/ai-settings.js +76 -28
  115. package/dist/api/routes/ai-settings.js.map +1 -1
  116. package/dist/api/routes/security-center.d.ts.map +1 -1
  117. package/dist/api/routes/security-center.js +5 -39
  118. package/dist/api/routes/security-center.js.map +1 -1
  119. package/dist/api/routes/seo-public.d.ts +0 -5
  120. package/dist/api/routes/seo-public.d.ts.map +1 -1
  121. package/dist/api/routes/seo-public.js +1 -1
  122. package/dist/api/routes/seo-public.js.map +1 -1
  123. package/dist/api/routes/seo.d.ts.map +1 -1
  124. package/dist/api/routes/seo.js +11 -10
  125. package/dist/api/routes/seo.js.map +1 -1
  126. package/dist/api/security-headers.d.ts +8 -0
  127. package/dist/api/security-headers.d.ts.map +1 -0
  128. package/dist/api/security-headers.js +13 -0
  129. package/dist/api/security-headers.js.map +1 -0
  130. package/dist/appearance/favicon-icon.d.ts +1 -1
  131. package/dist/appearance/favicon-icon.d.ts.map +1 -1
  132. package/dist/appearance/favicon-icon.js +1 -1
  133. package/dist/appearance/favicon-icon.js.map +1 -1
  134. package/dist/auth/index.d.ts +0 -6
  135. package/dist/auth/index.d.ts.map +1 -1
  136. package/dist/auth/index.js +0 -3
  137. package/dist/auth/index.js.map +1 -1
  138. package/dist/auth/invites.d.ts +0 -5
  139. package/dist/auth/invites.d.ts.map +1 -1
  140. package/dist/auth/invites.js +3 -3
  141. package/dist/auth/invites.js.map +1 -1
  142. package/dist/auth/team.d.ts +4 -3
  143. package/dist/auth/team.d.ts.map +1 -1
  144. package/dist/auth/user-admin.d.ts +2 -1
  145. package/dist/auth/user-admin.d.ts.map +1 -1
  146. package/dist/billing/stripe-meter.d.ts +2 -1
  147. package/dist/billing/stripe-meter.d.ts.map +1 -1
  148. package/dist/cache/http.d.ts +0 -11
  149. package/dist/cache/http.d.ts.map +1 -1
  150. package/dist/cache/http.js +1 -1
  151. package/dist/cache/http.js.map +1 -1
  152. package/dist/cache/index.d.ts +0 -5
  153. package/dist/cache/index.d.ts.map +1 -1
  154. package/dist/config/types.d.ts +19 -19
  155. package/dist/config/types.d.ts.map +1 -1
  156. package/dist/content/index.d.ts +2 -3
  157. package/dist/content/index.d.ts.map +1 -1
  158. package/dist/content/index.js +1 -2
  159. package/dist/content/index.js.map +1 -1
  160. package/dist/content/structured-data.d.ts +0 -14
  161. package/dist/content/structured-data.d.ts.map +1 -1
  162. package/dist/content/structured-data.js +3 -14
  163. package/dist/content/structured-data.js.map +1 -1
  164. package/dist/cron/index.d.ts +2 -3
  165. package/dist/cron/index.d.ts.map +1 -1
  166. package/dist/cron/index.js +0 -2
  167. package/dist/cron/index.js.map +1 -1
  168. package/dist/db/client-types.d.ts +2 -1
  169. package/dist/db/client-types.d.ts.map +1 -1
  170. package/dist/db.d.ts +0 -2
  171. package/dist/db.d.ts.map +1 -1
  172. package/dist/db.js +0 -1
  173. package/dist/db.js.map +1 -1
  174. package/dist/diagnostics/env.d.ts +1 -1
  175. package/dist/diagnostics/env.d.ts.map +1 -1
  176. package/dist/diagnostics/logger.d.ts +0 -24
  177. package/dist/diagnostics/logger.d.ts.map +1 -1
  178. package/dist/forms/entries.d.ts +0 -7
  179. package/dist/forms/entries.d.ts.map +1 -1
  180. package/dist/forms/entries.js +1 -19
  181. package/dist/forms/entries.js.map +1 -1
  182. package/dist/forms/files.d.ts +1 -1
  183. package/dist/forms/files.d.ts.map +1 -1
  184. package/dist/forms/index.d.ts +4 -6
  185. package/dist/forms/index.d.ts.map +1 -1
  186. package/dist/forms/index.js +3 -5
  187. package/dist/forms/index.js.map +1 -1
  188. package/dist/forms/prefetch.d.ts +0 -6
  189. package/dist/forms/prefetch.d.ts.map +1 -1
  190. package/dist/forms/service.d.ts +0 -4
  191. package/dist/forms/service.d.ts.map +1 -1
  192. package/dist/forms/service.js +2 -8
  193. package/dist/forms/service.js.map +1 -1
  194. package/dist/forms/webmcp.d.ts +0 -2
  195. package/dist/forms/webmcp.d.ts.map +1 -1
  196. package/dist/forms/webmcp.js +1 -1
  197. package/dist/forms/webmcp.js.map +1 -1
  198. package/dist/media/denormalize.d.ts +0 -7
  199. package/dist/media/denormalize.d.ts.map +1 -1
  200. package/dist/media/index.d.ts +0 -8
  201. package/dist/media/index.d.ts.map +1 -1
  202. package/dist/media/index.js +0 -5
  203. package/dist/media/index.js.map +1 -1
  204. package/dist/media/optimize.d.ts +0 -4
  205. package/dist/media/optimize.d.ts.map +1 -1
  206. package/dist/media/optimize.js +4 -4
  207. package/dist/media/optimize.js.map +1 -1
  208. package/dist/media/reoptimize.d.ts +2 -3
  209. package/dist/media/reoptimize.d.ts.map +1 -1
  210. package/dist/media/reoptimize.js +2 -2
  211. package/dist/media/reoptimize.js.map +1 -1
  212. package/dist/media/seo.d.ts +3 -8
  213. package/dist/media/seo.d.ts.map +1 -1
  214. package/dist/media/seo.js +1 -1
  215. package/dist/media/seo.js.map +1 -1
  216. package/dist/plan/features.d.ts +0 -1
  217. package/dist/plan/features.d.ts.map +1 -1
  218. package/dist/plan/features.js +0 -16
  219. package/dist/plan/features.js.map +1 -1
  220. package/dist/preview/event-bus.d.ts +2 -1
  221. package/dist/preview/event-bus.d.ts.map +1 -1
  222. package/dist/preview/index.d.ts +2 -1
  223. package/dist/preview/index.d.ts.map +1 -1
  224. package/dist/preview/sse-stream.d.ts +0 -13
  225. package/dist/preview/sse-stream.d.ts.map +1 -1
  226. package/dist/preview/sse-stream.js +2 -2
  227. package/dist/preview/sse-stream.js.map +1 -1
  228. package/dist/realtime/notifications.d.ts +4 -3
  229. package/dist/realtime/notifications.d.ts.map +1 -1
  230. package/dist/redirects/index.d.ts +4 -16
  231. package/dist/redirects/index.d.ts.map +1 -1
  232. package/dist/redirects/index.js +2 -9
  233. package/dist/redirects/index.js.map +1 -1
  234. package/dist/redirects/match.d.ts +0 -2
  235. package/dist/redirects/match.d.ts.map +1 -1
  236. package/dist/redirects/match.js +1 -1
  237. package/dist/redirects/match.js.map +1 -1
  238. package/dist/redirects/recovery.d.ts +0 -2
  239. package/dist/redirects/recovery.d.ts.map +1 -1
  240. package/dist/redirects/recovery.js +1 -1
  241. package/dist/redirects/recovery.js.map +1 -1
  242. package/dist/redirects/rerank.d.ts +4 -3
  243. package/dist/redirects/rerank.d.ts.map +1 -1
  244. package/dist/redirects/rerank.js +3 -18
  245. package/dist/redirects/rerank.js.map +1 -1
  246. package/dist/sections/validate.d.ts +0 -2
  247. package/dist/sections/validate.d.ts.map +1 -1
  248. package/dist/sections/validate.js +1 -1
  249. package/dist/sections/validate.js.map +1 -1
  250. package/dist/security/access.d.ts +0 -9
  251. package/dist/security/access.d.ts.map +1 -1
  252. package/dist/security/access.js +1 -1
  253. package/dist/security/access.js.map +1 -1
  254. package/dist/security/audit.d.ts +0 -20
  255. package/dist/security/audit.d.ts.map +1 -1
  256. package/dist/security/audit.js.map +1 -1
  257. package/dist/security/index.d.ts +2 -20
  258. package/dist/security/index.d.ts.map +1 -1
  259. package/dist/security/index.js +2 -7
  260. package/dist/security/index.js.map +1 -1
  261. package/dist/security/internal-keys.d.ts +0 -10
  262. package/dist/security/internal-keys.d.ts.map +1 -1
  263. package/dist/security/internal-keys.js +2 -2
  264. package/dist/security/internal-keys.js.map +1 -1
  265. package/dist/security/rate-limit-policy.d.ts +0 -2
  266. package/dist/security/rate-limit-policy.d.ts.map +1 -1
  267. package/dist/security/rate-limit-policy.js +1 -1
  268. package/dist/security/rate-limit-policy.js.map +1 -1
  269. package/dist/security/rate-limit.d.ts +2 -5
  270. package/dist/security/rate-limit.d.ts.map +1 -1
  271. package/dist/security/rate-limit.js +2 -2
  272. package/dist/security/rate-limit.js.map +1 -1
  273. package/dist/security/sanitize.d.ts +0 -3
  274. package/dist/security/sanitize.d.ts.map +1 -1
  275. package/dist/security/sanitize.js +0 -1
  276. package/dist/security/sanitize.js.map +1 -1
  277. package/dist/security/upload.d.ts +0 -3
  278. package/dist/security/upload.d.ts.map +1 -1
  279. package/dist/security/upload.js +0 -1
  280. package/dist/security/upload.js.map +1 -1
  281. package/dist/seo/ai-crawler-log.d.ts +0 -2
  282. package/dist/seo/ai-crawler-log.d.ts.map +1 -1
  283. package/dist/seo/ai-crawler-log.js +1 -1
  284. package/dist/seo/ai-crawler-log.js.map +1 -1
  285. package/dist/seo/audit-engine.d.ts +5 -5
  286. package/dist/seo/audit-engine.d.ts.map +1 -1
  287. package/dist/seo/audit-engine.js +1 -2
  288. package/dist/seo/audit-engine.js.map +1 -1
  289. package/dist/seo/audit-runner.d.ts +0 -7
  290. package/dist/seo/audit-runner.d.ts.map +1 -1
  291. package/dist/seo/audit-runner.js +1 -1
  292. package/dist/seo/audit-runner.js.map +1 -1
  293. package/dist/seo/audit-signals.d.ts +0 -2
  294. package/dist/seo/audit-signals.d.ts.map +1 -1
  295. package/dist/seo/audit-signals.js +1 -1
  296. package/dist/seo/audit-signals.js.map +1 -1
  297. package/dist/seo/autopilot-gate.d.ts +0 -1
  298. package/dist/seo/autopilot-gate.d.ts.map +1 -1
  299. package/dist/seo/autopilot-gate.js +1 -1
  300. package/dist/seo/autopilot-gate.js.map +1 -1
  301. package/dist/seo/autopilot-ledger.d.ts +2 -1
  302. package/dist/seo/autopilot-ledger.d.ts.map +1 -1
  303. package/dist/seo/autopilot-outcome.d.ts +0 -2
  304. package/dist/seo/autopilot-outcome.d.ts.map +1 -1
  305. package/dist/seo/autopilot-outcome.js +1 -1
  306. package/dist/seo/autopilot-outcome.js.map +1 -1
  307. package/dist/seo/autopilot-policy.d.ts +3 -38
  308. package/dist/seo/autopilot-policy.d.ts.map +1 -1
  309. package/dist/seo/autopilot-policy.js +3 -3
  310. package/dist/seo/autopilot-policy.js.map +1 -1
  311. package/dist/seo/autopilot.d.ts +4 -12
  312. package/dist/seo/autopilot.d.ts.map +1 -1
  313. package/dist/seo/autopilot.js +3 -3
  314. package/dist/seo/autopilot.js.map +1 -1
  315. package/dist/seo/config-store.d.ts +0 -2
  316. package/dist/seo/config-store.d.ts.map +1 -1
  317. package/dist/seo/config-store.js +1 -1
  318. package/dist/seo/config-store.js.map +1 -1
  319. package/dist/seo/digest.d.ts +8 -7
  320. package/dist/seo/digest.d.ts.map +1 -1
  321. package/dist/seo/faq-signals.d.ts +0 -4
  322. package/dist/seo/faq-signals.d.ts.map +1 -1
  323. package/dist/seo/faq-signals.js +4 -4
  324. package/dist/seo/faq-signals.js.map +1 -1
  325. package/dist/seo/fix-brief-export.d.ts +0 -2
  326. package/dist/seo/fix-brief-export.d.ts.map +1 -1
  327. package/dist/seo/fix-brief-export.js +1 -1
  328. package/dist/seo/fix-brief-export.js.map +1 -1
  329. package/dist/seo/geo-record.d.ts +0 -4
  330. package/dist/seo/geo-record.d.ts.map +1 -1
  331. package/dist/seo/geo-record.js +2 -2
  332. package/dist/seo/geo-record.js.map +1 -1
  333. package/dist/seo/geo-report.d.ts +4 -3
  334. package/dist/seo/geo-report.d.ts.map +1 -1
  335. package/dist/seo/geo-report.js +1 -1
  336. package/dist/seo/geo-report.js.map +1 -1
  337. package/dist/seo/index-status.d.ts +6 -5
  338. package/dist/seo/index-status.d.ts.map +1 -1
  339. package/dist/seo/index-status.js.map +1 -1
  340. package/dist/seo/index.d.ts +15 -90
  341. package/dist/seo/index.d.ts.map +1 -1
  342. package/dist/seo/index.js +10 -49
  343. package/dist/seo/index.js.map +1 -1
  344. package/dist/seo/issue-batch-briefs.d.ts +2 -3
  345. package/dist/seo/issue-batch-briefs.d.ts.map +1 -1
  346. package/dist/seo/issue-batch-briefs.js +2 -2
  347. package/dist/seo/issue-batch-briefs.js.map +1 -1
  348. package/dist/seo/issue-content-brief.d.ts +2 -1
  349. package/dist/seo/issue-content-brief.d.ts.map +1 -1
  350. package/dist/seo/issue-content-research.d.ts +2 -2
  351. package/dist/seo/issue-content-research.d.ts.map +1 -1
  352. package/dist/seo/issue-content-research.js +1 -1
  353. package/dist/seo/issue-content-research.js.map +1 -1
  354. package/dist/seo/issue-fix-ai.d.ts +5 -4
  355. package/dist/seo/issue-fix-ai.d.ts.map +1 -1
  356. package/dist/seo/issue-fix-ai.js +4 -19
  357. package/dist/seo/issue-fix-ai.js.map +1 -1
  358. package/dist/seo/issue-fix-apply.d.ts +2 -11
  359. package/dist/seo/issue-fix-apply.d.ts.map +1 -1
  360. package/dist/seo/issue-fix-apply.js +4 -4
  361. package/dist/seo/issue-fix-apply.js.map +1 -1
  362. package/dist/seo/issue-fix-faq.d.ts +4 -7
  363. package/dist/seo/issue-fix-faq.d.ts.map +1 -1
  364. package/dist/seo/issue-fix-faq.js +4 -4
  365. package/dist/seo/issue-fix-faq.js.map +1 -1
  366. package/dist/seo/issue-fix-link-insert.d.ts +4 -14
  367. package/dist/seo/issue-fix-link-insert.d.ts.map +1 -1
  368. package/dist/seo/issue-fix-link-insert.js +5 -4
  369. package/dist/seo/issue-fix-link-insert.js.map +1 -1
  370. package/dist/seo/issue-fix-redirect.d.ts +0 -1
  371. package/dist/seo/issue-fix-redirect.d.ts.map +1 -1
  372. package/dist/seo/issue-fix.d.ts.map +1 -1
  373. package/dist/seo/issue-fix.js +1 -0
  374. package/dist/seo/issue-fix.js.map +1 -1
  375. package/dist/seo/link-graph.d.ts +2 -3
  376. package/dist/seo/link-graph.d.ts.map +1 -1
  377. package/dist/seo/link-graph.js +0 -0
  378. package/dist/seo/link-graph.js.map +1 -1
  379. package/dist/seo/link-suggest.d.ts +8 -2
  380. package/dist/seo/link-suggest.d.ts.map +1 -1
  381. package/dist/seo/link-suggest.js +38 -50
  382. package/dist/seo/link-suggest.js.map +1 -1
  383. package/dist/seo/page-meta.d.ts +1 -10
  384. package/dist/seo/page-meta.d.ts.map +1 -1
  385. package/dist/seo/page-meta.js +1 -1
  386. package/dist/seo/page-meta.js.map +1 -1
  387. package/dist/seo/proposals-bulk-apply.d.ts +3 -2
  388. package/dist/seo/proposals-bulk-apply.d.ts.map +1 -1
  389. package/dist/seo/proposals.d.ts +2 -2
  390. package/dist/seo/proposals.d.ts.map +1 -1
  391. package/dist/seo/rendered-fetch.d.ts +2 -1
  392. package/dist/seo/rendered-fetch.d.ts.map +1 -1
  393. package/dist/seo/score.d.ts +3 -7
  394. package/dist/seo/score.d.ts.map +1 -1
  395. package/dist/seo/score.js.map +1 -1
  396. package/dist/seo/search-console/client.d.ts +0 -4
  397. package/dist/seo/search-console/client.d.ts.map +1 -1
  398. package/dist/seo/search-console/client.js +2 -2
  399. package/dist/seo/search-console/client.js.map +1 -1
  400. package/dist/seo/search-console/sync.d.ts +0 -4
  401. package/dist/seo/search-console/sync.d.ts.map +1 -1
  402. package/dist/seo/search-console/sync.js +2 -2
  403. package/dist/seo/search-console/sync.js.map +1 -1
  404. package/dist/seo/search-console/types.d.ts +2 -1
  405. package/dist/seo/search-console/types.d.ts.map +1 -1
  406. package/dist/seo/sitemap-notify.d.ts +0 -8
  407. package/dist/seo/sitemap-notify.d.ts.map +1 -1
  408. package/dist/seo/sitemap-notify.js +1 -1
  409. package/dist/seo/sitemap-notify.js.map +1 -1
  410. package/dist/seo/title-templates.d.ts +2 -5
  411. package/dist/seo/title-templates.d.ts.map +1 -1
  412. package/dist/seo/title-templates.js +2 -2
  413. package/dist/seo/title-templates.js.map +1 -1
  414. package/dist/server-site.d.ts +0 -1
  415. package/dist/server-site.d.ts.map +1 -1
  416. package/dist/server-site.js +0 -1
  417. package/dist/server-site.js.map +1 -1
  418. package/dist/storage/index.d.ts +0 -2
  419. package/dist/storage/index.d.ts.map +1 -1
  420. package/dist/storage/index.js +0 -4
  421. package/dist/storage/index.js.map +1 -1
  422. package/dist/upgrade/version-check.d.ts +0 -2
  423. package/dist/upgrade/version-check.d.ts.map +1 -1
  424. package/dist/upgrade/version-check.js +1 -1
  425. package/dist/upgrade/version-check.js.map +1 -1
  426. package/dist/webhooks/index.d.ts +0 -8
  427. package/dist/webhooks/index.d.ts.map +1 -1
  428. package/dist/webhooks/index.js +1 -16
  429. package/dist/webhooks/index.js.map +1 -1
  430. package/package.json +1 -1
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Provider-free prompt + coercion for whole-document generation. Moved from
3
+ * `@prenta/plugin-ai` (`writing/content-generator.ts`) in Spec B; the model
4
+ * call runs through `generateJsonForFeature` under the
5
+ * `ai.features.documentGeneration` feature (toggle, daily cap, budget, brand
6
+ * voice, usage metering), so this module never touches a provider.
7
+ *
8
+ * Given a prompt + a minimal collection schema (slug, labels, fields) it
9
+ * builds the system/user prompts that ask the model for JSON matching the
10
+ * field names and types, and coerces the validated reply into field data
11
+ * ready for `createDocument`.
12
+ *
13
+ * Intentionally pragmatic — it does *not* invent a page `data.sections`
14
+ * list. Author pages with `add_page_section` or `createDocument` using
15
+ * `data.sections`. This is for non-page collections (blog posts, case
16
+ * studies, services, locations, team members, …) with structured field data.
17
+ */
18
+ import { z } from 'zod';
19
+ import { clampAiContent, fenceUntrusted } from '../prompt-safety.js';
20
+ export const DOCUMENT_GENERATION_FEATURE = 'ai.features.documentGeneration';
21
+ /** Default output cap — enough for ~2000-word posts. */
22
+ export const DOCUMENT_GENERATION_MAX_TOKENS = 3000;
23
+ /** Reference material is clamped before fencing so it cannot dominate the prompt. */
24
+ const EXISTING_CONTENT_MAX_CHARS = 2000;
25
+ /** Caller-supplied modifiers land in the *system* prompt: clamp and keep to one line. */
26
+ const MODIFIER_MAX_CHARS = 200;
27
+ /**
28
+ * Sanitize a free-form modifier (`tone`, `targetAudience`) before it becomes a
29
+ * system-prompt rule: clamp, collapse every line break / whitespace run to one
30
+ * space so the caller cannot inject additional `- Rule` lines, and trim.
31
+ * Returns `undefined` for blank input so the rule line is omitted.
32
+ */
33
+ function systemPromptModifier(value) {
34
+ if (!value)
35
+ return undefined;
36
+ const flat = clampAiContent(value, MODIFIER_MAX_CHARS).replace(/\s+/g, ' ').trim();
37
+ return flat || undefined;
38
+ }
39
+ /** Any JSON object; field-level coercion happens in {@link coerceDocument}. */
40
+ export const DocumentDraftSchema = z
41
+ .record(z.string(), z.unknown())
42
+ .refine((v) => v !== null && typeof v === 'object' && !Array.isArray(v), {
43
+ message: 'expected a JSON object',
44
+ });
45
+ // ─── Core type adapters ───────────────────────────────────────────────────
46
+ /**
47
+ * Project a core `FieldDefinition` onto {@link FieldShape}. Only the
48
+ * prompt-relevant, serializable subset is carried (no validators, hooks,
49
+ * access functions, or admin conditions), so the shape stays safe to log and
50
+ * cheap to describe to the model.
51
+ */
52
+ export function toFieldShape(field) {
53
+ const shape = { type: field.type };
54
+ if (field.label)
55
+ shape.label = field.label;
56
+ if (field.required)
57
+ shape.required = true;
58
+ if (field.admin?.description)
59
+ shape.admin = { description: field.admin.description };
60
+ switch (field.type) {
61
+ case 'text':
62
+ if (typeof field.maxLength === 'number')
63
+ shape.maxLength = field.maxLength;
64
+ break;
65
+ case 'number':
66
+ if (typeof field.min === 'number')
67
+ shape.min = field.min;
68
+ if (typeof field.max === 'number')
69
+ shape.max = field.max;
70
+ if (typeof field.step === 'number')
71
+ shape.step = field.step;
72
+ break;
73
+ case 'select':
74
+ shape.options = field.options.map((o) => ({ label: o.label, value: o.value }));
75
+ break;
76
+ case 'richText':
77
+ if (field.features)
78
+ shape.features = [...field.features];
79
+ break;
80
+ case 'array':
81
+ shape.fields = Object.fromEntries(Object.entries(field.fields).map(([name, sub]) => [name, toFieldShape(sub)]));
82
+ break;
83
+ case 'boolean':
84
+ case 'date':
85
+ case 'slug':
86
+ case 'relationship':
87
+ case 'media':
88
+ case 'json':
89
+ case 'nav':
90
+ case 'color':
91
+ case 'url':
92
+ case 'email':
93
+ case 'blocks':
94
+ case 'componentBlock':
95
+ break;
96
+ default: {
97
+ const _exhaustive = field;
98
+ return _exhaustive;
99
+ }
100
+ }
101
+ return shape;
102
+ }
103
+ /** Project a core `CollectionDefinition` onto {@link CollectionShape}. */
104
+ export function toCollectionShape(collection) {
105
+ const shape = {
106
+ slug: collection.slug,
107
+ labels: collection.labels,
108
+ fields: Object.fromEntries(Object.entries(collection.fields).map(([name, f]) => [name, toFieldShape(f)])),
109
+ };
110
+ if (collection.type)
111
+ shape.type = collection.type;
112
+ if (collection.seo?.defaultSchemaType) {
113
+ shape.seo = { defaultSchemaType: collection.seo.defaultSchemaType };
114
+ }
115
+ return shape;
116
+ }
117
+ // ─── Prompt construction ──────────────────────────────────────────────────
118
+ function describeField(name, field) {
119
+ const parts = [` "${name}"`];
120
+ parts.push(`(${field.type}${field.required ? ', required' : ''})`);
121
+ if (field.label && field.label !== name)
122
+ parts.push(`label: ${field.label}`);
123
+ if (field.maxLength)
124
+ parts.push(`max ${field.maxLength} chars`);
125
+ if (typeof field.min === 'number' || typeof field.max === 'number') {
126
+ parts.push(`range ${field.min ?? '∞'}…${field.max ?? '∞'}`);
127
+ }
128
+ if (field.options && field.options.length) {
129
+ parts.push(`one of: ${field.options.map((o) => o.value).join(', ')}`);
130
+ }
131
+ if (field.type === 'array' && field.fields) {
132
+ const subFields = Object.entries(field.fields)
133
+ .map(([n, f]) => `${n}:${f.type}`)
134
+ .join(', ');
135
+ parts.push(`array of { ${subFields} }`);
136
+ }
137
+ if (field.admin?.description) {
138
+ parts.push(`— ${field.admin.description}`);
139
+ }
140
+ return parts.join(' ');
141
+ }
142
+ function buildSystemPrompt(collection, tone, targetAudience) {
143
+ const lines = [
144
+ `You are an expert content writer creating a "${collection.labels.singular}" document for a CMS.`,
145
+ 'You generate valid JSON matching the requested field schema. NEVER include markdown, code fences, or commentary.',
146
+ '',
147
+ 'Rules:',
148
+ '- Output a single JSON object whose top-level keys exactly match the field names below.',
149
+ '- Match field types: text/richText/email/url are strings; number is a number; boolean is a boolean; date is an ISO-8601 string; array is a JSON array of objects; select must use one of the listed option values; media fields should be left as null (a human will upload the asset).',
150
+ '- Respect maxLength constraints. Quality over quantity — never pad.',
151
+ '- For slug fields, generate a URL-safe lowercase string with words joined by hyphens.',
152
+ '- For richText fields, return well-structured prose with paragraph breaks, but no HTML tags.',
153
+ '- For seo fields (metaTitle, metaDescription) compose SEO-optimised copy distinct from the page title.',
154
+ "- Never invent facts. If you don't know something the prompt didn't supply, leave the field blank or null.",
155
+ ];
156
+ if (collection.seo?.defaultSchemaType) {
157
+ lines.push(`- This collection maps to Schema.org "${collection.seo.defaultSchemaType}".`);
158
+ }
159
+ if (tone)
160
+ lines.push(`- Tone: ${tone}.`);
161
+ if (targetAudience)
162
+ lines.push(`- Audience: ${targetAudience}.`);
163
+ return lines.join('\n');
164
+ }
165
+ function buildUserPrompt(input) {
166
+ const { collection, prompt, existingContent } = input;
167
+ const fieldDescriptions = Object.entries(collection.fields)
168
+ // Media is supplied by the human reviewer.
169
+ // Relations are excluded because the LLM has no view of existing
170
+ // document IDs — guessing would create dangling references that the
171
+ // validator would then reject. Humans / orchestration code wire those
172
+ // in after creation.
173
+ .filter(([, f]) => f.type !== 'media' && f.type !== 'relationship')
174
+ .map(([name, field]) => describeField(name, field))
175
+ .join('\n');
176
+ const lines = [
177
+ `Create a ${collection.labels.singular} matching this brief:`,
178
+ '',
179
+ prompt.trim(),
180
+ '',
181
+ 'Field schema:',
182
+ fieldDescriptions,
183
+ '',
184
+ 'Output ONLY a JSON object with the field values. No prose. No markdown fences.',
185
+ ];
186
+ if (existingContent) {
187
+ lines.push('', 'Existing related content for voice / fact reference:');
188
+ // Untrusted reference content — fence it so an injected instruction inside
189
+ // a referenced document can't redirect the generation.
190
+ lines.push(fenceUntrusted(existingContent, EXISTING_CONTENT_MAX_CHARS));
191
+ }
192
+ return lines.join('\n');
193
+ }
194
+ export function buildDocumentPrompt(input) {
195
+ return {
196
+ system: buildSystemPrompt(input.collection, systemPromptModifier(input.tone), systemPromptModifier(input.targetAudience)),
197
+ prompt: buildUserPrompt(input),
198
+ };
199
+ }
200
+ // ─── Output coercion ──────────────────────────────────────────────────────
201
+ function slugify(title) {
202
+ return (title
203
+ .toLowerCase()
204
+ .replace(/[^a-z0-9]+/g, '-')
205
+ .replace(/^-+|-+$/g, '')
206
+ .slice(0, 80) || 'untitled');
207
+ }
208
+ /**
209
+ * Coerce the LLM output into shapes that match each field's declared type.
210
+ * The LLM occasionally returns numbers as strings, booleans as "true"/"false",
211
+ * etc. We don't try to recover unrecoverable values — we leave bad fields
212
+ * `undefined` so the downstream field validator surfaces the error to the
213
+ * caller instead of silently persisting garbage.
214
+ */
215
+ function coerceFieldValue(value, field) {
216
+ if (value === null || value === undefined)
217
+ return value;
218
+ switch (field.type) {
219
+ case 'text':
220
+ case 'richText':
221
+ case 'email':
222
+ case 'url':
223
+ case 'slug':
224
+ case 'color':
225
+ return typeof value === 'string' ? value : String(value);
226
+ case 'number':
227
+ if (typeof value === 'number')
228
+ return value;
229
+ if (typeof value === 'string') {
230
+ const n = Number(value);
231
+ return Number.isFinite(n) ? n : undefined;
232
+ }
233
+ return undefined;
234
+ case 'boolean':
235
+ if (typeof value === 'boolean')
236
+ return value;
237
+ if (value === 'true')
238
+ return true;
239
+ if (value === 'false')
240
+ return false;
241
+ return undefined;
242
+ case 'date':
243
+ if (value instanceof Date)
244
+ return value.toISOString();
245
+ if (typeof value === 'string') {
246
+ const d = new Date(value);
247
+ return isNaN(d.getTime()) ? undefined : d.toISOString();
248
+ }
249
+ return undefined;
250
+ case 'select':
251
+ if (typeof value === 'string' && field.options?.some((o) => o.value === value))
252
+ return value;
253
+ return undefined;
254
+ case 'array':
255
+ if (!Array.isArray(value))
256
+ return [];
257
+ if (!field.fields)
258
+ return value;
259
+ return value.map((item) => {
260
+ if (item === null || typeof item !== 'object')
261
+ return item;
262
+ const obj = item;
263
+ const coerced = {};
264
+ for (const [name, sub] of Object.entries(field.fields)) {
265
+ if (obj[name] !== undefined)
266
+ coerced[name] = coerceFieldValue(obj[name], sub);
267
+ }
268
+ return coerced;
269
+ });
270
+ case 'json':
271
+ return value;
272
+ case 'media':
273
+ // The LLM doesn't have asset IDs. Leave for a human to fill.
274
+ return null;
275
+ case 'relationship':
276
+ // Same rationale as media — IDs are out-of-band knowledge. If the
277
+ // model did emit something (e.g. echoed an ID from the prompt) we
278
+ // accept strings and string arrays verbatim; otherwise drop.
279
+ if (typeof value === 'string')
280
+ return value;
281
+ if (Array.isArray(value))
282
+ return value.filter((v) => typeof v === 'string');
283
+ return undefined;
284
+ default:
285
+ return value;
286
+ }
287
+ }
288
+ /**
289
+ * Coerce a schema-validated model reply into persisted field data plus the
290
+ * derived title / slug / SEO bundle. Unknown keys (anything the collection
291
+ * doesn't declare) are dropped to keep persisted data clean.
292
+ */
293
+ export function coerceDocument(parsed, collection) {
294
+ const data = {};
295
+ for (const [name, field] of Object.entries(collection.fields)) {
296
+ const raw = parsed[name];
297
+ if (raw === undefined)
298
+ continue;
299
+ const coerced = coerceFieldValue(raw, field);
300
+ if (coerced !== undefined)
301
+ data[name] = coerced;
302
+ }
303
+ // Derive title + slug.
304
+ const title = (typeof data.title === 'string' && data.title) ||
305
+ (typeof data.question === 'string' && data.question) ||
306
+ (typeof data.name === 'string' && data.name) ||
307
+ 'Untitled';
308
+ const slug = (typeof data.slug === 'string' && data.slug.trim()) || slugify(title);
309
+ data.slug = slug;
310
+ const seo = {
311
+ metaTitle: (typeof data.metaTitle === 'string' && data.metaTitle) ||
312
+ (typeof parsed.metaTitle === 'string' && parsed.metaTitle) ||
313
+ title,
314
+ metaDescription: (typeof data.metaDescription === 'string' && data.metaDescription) ||
315
+ (typeof parsed.metaDescription === 'string' && parsed.metaDescription) ||
316
+ undefined,
317
+ schemaType: collection.seo?.defaultSchemaType,
318
+ };
319
+ return { data, title, slug, seo };
320
+ }
321
+ //# sourceMappingURL=document-generation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-generation.js","sourceRoot":"","sources":["../../../src/ai/tasks/document-generation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Embedding-ranked internal-link candidates. The query and the corpus are
3
+ * embedded through governed `embedForFeature` calls in batches of
4
+ * {@link EMBED_BATCH_SIZE} inputs (plugin-ai's `semanticSearch` made N+1
5
+ * ungoverned calls; a single request for a 5000-page site blew the provider's
6
+ * per-request input limit). Returns `null` when the runtime refuses so callers
7
+ * fall back to {@link phraseMatchLinks}.
8
+ */
9
+ export declare const LINK_SUGGEST_FEATURE = "ai.features.seoInternalLinks";
10
+ export declare const LINK_SIMILARITY_THRESHOLD = 0.35;
11
+ /** Per-item content cap before embedding — enough context, bounded token cost. */
12
+ export declare const CONTENT_MAX_CHARS = 2000;
13
+ /** Max inputs per embeddings request (the first request also carries the query). */
14
+ export declare const EMBED_BATCH_SIZE = 256;
15
+ /** Largest corpus we embed; bigger ones are pre-trimmed by {@link trimCorpusForEmbedding}. */
16
+ export declare const MAX_CORPUS_ITEMS = 1000;
17
+ export interface LinkCorpusItem {
18
+ id: string;
19
+ title: string;
20
+ content: string;
21
+ /** Recency tie-breaker for the pre-trim; optional for callers without it. */
22
+ updatedAt?: string | Date;
23
+ }
24
+ export interface RankedLink {
25
+ id: string;
26
+ title: string;
27
+ score: number;
28
+ excerpt: string;
29
+ }
30
+ /**
31
+ * Deterministic pre-trim for corpora above the embedding cap: items whose
32
+ * title/content share the most query tokens come first (a `phraseMatchLinks`-
33
+ * style overlap), then the most recently updated. Stable for ties so the
34
+ * result is reproducible. Returns the input untouched when it already fits.
35
+ */
36
+ export declare function trimCorpusForEmbedding(query: string, corpus: LinkCorpusItem[], max?: number): LinkCorpusItem[];
37
+ /**
38
+ * Rank `corpus` against `query` by embedding cosine similarity. Everything
39
+ * (toggle, budget, daily cap, key resolution, metering) is enforced inside
40
+ * `embedForFeature`; any refusal or provider failure yields `null`. Corpora
41
+ * above {@link MAX_CORPUS_ITEMS} are pre-trimmed deterministically, every item
42
+ * is clamped to {@link CONTENT_MAX_CHARS}, and requests are chunked.
43
+ */
44
+ export declare function rankByEmbedding(db: unknown, query: string, corpus: LinkCorpusItem[], opts?: {
45
+ limit?: number;
46
+ userId?: string;
47
+ fetchImpl?: typeof fetch;
48
+ }): Promise<RankedLink[] | null>;
49
+ /**
50
+ * Deterministic fallback: a source qualifies when its content contains the
51
+ * target title or one of its keywords. Score 0.6 for a title hit, 0.4 for a
52
+ * keyword-only hit (both clear the 40% wrappable floor in link-suggest).
53
+ */
54
+ export declare function phraseMatchLinks(targetTitle: string, keywords: string[], corpus: LinkCorpusItem[], limit: number): RankedLink[];
55
+ //# sourceMappingURL=link-embeddings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-embeddings.d.ts","sourceRoot":"","sources":["../../../src/ai/tasks/link-embeddings.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,eAAO,MAAM,oBAAoB,iCAAiC,CAAA;AAClE,eAAO,MAAM,yBAAyB,OAAO,CAAA;AAC7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAO,CAAA;AACrC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAMpC,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAwBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,GAAG,SAAmB,GACrB,cAAc,EAAE,CAkBlB;AA+BD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACvE,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,CA2B9B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,EAAE,MAAM,GACZ,UAAU,EAAE,CAmBd"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Embedding-ranked internal-link candidates. The query and the corpus are
3
+ * embedded through governed `embedForFeature` calls in batches of
4
+ * {@link EMBED_BATCH_SIZE} inputs (plugin-ai's `semanticSearch` made N+1
5
+ * ungoverned calls; a single request for a 5000-page site blew the provider's
6
+ * per-request input limit). Returns `null` when the runtime refuses so callers
7
+ * fall back to {@link phraseMatchLinks}.
8
+ */
9
+ import { embedForFeature } from '../runtime.js';
10
+ import { cosineSimilarity } from '../vector.js';
11
+ export const LINK_SUGGEST_FEATURE = 'ai.features.seoInternalLinks';
12
+ export const LINK_SIMILARITY_THRESHOLD = 0.35;
13
+ /** Per-item content cap before embedding — enough context, bounded token cost. */
14
+ export const CONTENT_MAX_CHARS = 2000;
15
+ /** Max inputs per embeddings request (the first request also carries the query). */
16
+ export const EMBED_BATCH_SIZE = 256;
17
+ /** Largest corpus we embed; bigger ones are pre-trimmed by {@link trimCorpusForEmbedding}. */
18
+ export const MAX_CORPUS_ITEMS = 1000;
19
+ const EXCERPT_CHARS = 200;
20
+ const DEFAULT_LIMIT = 10;
21
+ /** Query tokens shorter than this carry no signal for the pre-trim. */
22
+ const MIN_TOKEN_CHARS = 3;
23
+ function excerptAround(content, terms) {
24
+ const lower = content.toLowerCase();
25
+ let start = 0;
26
+ for (const term of terms) {
27
+ const idx = lower.indexOf(term);
28
+ if (idx >= 0) {
29
+ start = Math.max(0, idx - 50);
30
+ break;
31
+ }
32
+ }
33
+ return content.slice(start, start + EXCERPT_CHARS);
34
+ }
35
+ function updatedAtMs(item) {
36
+ if (item.updatedAt instanceof Date)
37
+ return item.updatedAt.getTime();
38
+ if (typeof item.updatedAt === 'string') {
39
+ const t = Date.parse(item.updatedAt);
40
+ return Number.isNaN(t) ? 0 : t;
41
+ }
42
+ return 0;
43
+ }
44
+ /**
45
+ * Deterministic pre-trim for corpora above the embedding cap: items whose
46
+ * title/content share the most query tokens come first (a `phraseMatchLinks`-
47
+ * style overlap), then the most recently updated. Stable for ties so the
48
+ * result is reproducible. Returns the input untouched when it already fits.
49
+ */
50
+ export function trimCorpusForEmbedding(query, corpus, max = MAX_CORPUS_ITEMS) {
51
+ if (corpus.length <= max)
52
+ return corpus;
53
+ const tokens = [
54
+ ...new Set(query
55
+ .toLowerCase()
56
+ .split(/[^\p{L}\p{N}]+/u)
57
+ .filter((t) => t.length >= MIN_TOKEN_CHARS)),
58
+ ];
59
+ const scored = corpus.map((item, index) => {
60
+ const hay = `${item.title} ${item.content}`.toLowerCase();
61
+ let overlap = 0;
62
+ for (const t of tokens)
63
+ if (hay.includes(t))
64
+ overlap++;
65
+ return { item, index, overlap, recency: updatedAtMs(item) };
66
+ });
67
+ scored.sort((a, b) => b.overlap - a.overlap || b.recency - a.recency || a.index - b.index);
68
+ return scored.slice(0, max).map((s) => s.item);
69
+ }
70
+ /**
71
+ * Embed `texts` in sequential batches of {@link EMBED_BATCH_SIZE}. The first
72
+ * batch also carries the query (so it has one extra input). Stops at the first
73
+ * refusal and returns `null` — a partially embedded corpus is not rankable.
74
+ */
75
+ async function embedInBatches(db, query, contents, opts) {
76
+ let queryVec;
77
+ const vecs = [];
78
+ for (let start = 0; start < contents.length; start += EMBED_BATCH_SIZE) {
79
+ const chunk = contents.slice(start, start + EMBED_BATCH_SIZE);
80
+ const texts = start === 0 ? [query, ...chunk] : chunk;
81
+ const res = await embedForFeature(db, LINK_SUGGEST_FEATURE, texts, {
82
+ userId: opts.userId,
83
+ fetchImpl: opts.fetchImpl,
84
+ });
85
+ if (!res.ok)
86
+ return null;
87
+ const out = start === 0 ? res.vectors.slice(1) : res.vectors;
88
+ if (start === 0)
89
+ queryVec = res.vectors[0];
90
+ vecs.push(...out);
91
+ }
92
+ if (!queryVec)
93
+ return null;
94
+ return { queryVec, vecs };
95
+ }
96
+ /**
97
+ * Rank `corpus` against `query` by embedding cosine similarity. Everything
98
+ * (toggle, budget, daily cap, key resolution, metering) is enforced inside
99
+ * `embedForFeature`; any refusal or provider failure yields `null`. Corpora
100
+ * above {@link MAX_CORPUS_ITEMS} are pre-trimmed deterministically, every item
101
+ * is clamped to {@link CONTENT_MAX_CHARS}, and requests are chunked.
102
+ */
103
+ export async function rankByEmbedding(db, query, corpus, opts = {}) {
104
+ if (corpus.length === 0)
105
+ return [];
106
+ const trimmed = trimCorpusForEmbedding(query, corpus);
107
+ const contents = trimmed.map((c) => c.content.slice(0, CONTENT_MAX_CHARS));
108
+ const embedded = await embedInBatches(db, query.slice(0, CONTENT_MAX_CHARS), contents, {
109
+ userId: opts.userId,
110
+ fetchImpl: opts.fetchImpl,
111
+ });
112
+ if (!embedded)
113
+ return null;
114
+ const { queryVec, vecs } = embedded;
115
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
116
+ const ranked = [];
117
+ trimmed.forEach((item, i) => {
118
+ const vec = vecs[i];
119
+ if (!vec)
120
+ return;
121
+ const score = cosineSimilarity(queryVec, vec);
122
+ if (score >= LINK_SIMILARITY_THRESHOLD) {
123
+ ranked.push({
124
+ id: item.id,
125
+ title: item.title,
126
+ score,
127
+ excerpt: excerptAround(item.content, terms),
128
+ });
129
+ }
130
+ });
131
+ ranked.sort((a, b) => b.score - a.score);
132
+ return ranked.slice(0, opts.limit ?? DEFAULT_LIMIT);
133
+ }
134
+ /**
135
+ * Deterministic fallback: a source qualifies when its content contains the
136
+ * target title or one of its keywords. Score 0.6 for a title hit, 0.4 for a
137
+ * keyword-only hit (both clear the 40% wrappable floor in link-suggest).
138
+ */
139
+ export function phraseMatchLinks(targetTitle, keywords, corpus, limit) {
140
+ const title = targetTitle.trim().toLowerCase();
141
+ const kws = keywords.map((k) => k.trim().toLowerCase()).filter(Boolean);
142
+ const out = [];
143
+ for (const item of corpus) {
144
+ const lower = item.content.toLowerCase();
145
+ const titleHit = title.length > 0 && lower.includes(title);
146
+ const kwHit = kws.find((k) => lower.includes(k));
147
+ if (!titleHit && !kwHit)
148
+ continue;
149
+ const term = titleHit ? title : kwHit;
150
+ out.push({
151
+ id: item.id,
152
+ title: item.title,
153
+ score: titleHit ? 0.6 : 0.4,
154
+ excerpt: excerptAround(item.content, term ? [term] : []),
155
+ });
156
+ }
157
+ out.sort((a, b) => b.score - a.score);
158
+ return out.slice(0, limit);
159
+ }
160
+ //# sourceMappingURL=link-embeddings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-embeddings.js","sourceRoot":"","sources":["../../../src/ai/tasks/link-embeddings.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Shared types for the Settings > AI governance layer.
3
- *
4
- * Phases 1-2 cover provider connections (encrypted keys) and a dynamic,
5
- * normalized model catalog. Feature config, budget, and data-policy shapes are
6
- * declared here for forward-compatibility (enforcement + metering land in later
7
- * phases) but only `features[].enabled` and `budget.monthlyTokenLimit` are
8
- * wired into the UI for now.
2
+ * Shared types for the Settings > AI governance layer: provider connections
3
+ * (encrypted or env-managed keys), the normalized model catalog, per-feature
4
+ * toggles / routing / approval / daily caps, the monthly token budget, and the
5
+ * brand voice profile. Everything declared here is read by the governed
6
+ * runtime (`runtime.ts`) or the admin UI — fields that were once reserved for
7
+ * future phases were removed in Spec B rather than left as silent no-ops.
9
8
  */
10
9
  /** Provider keys are stable code identifiers. `openai` covers OpenAI/ChatGPT. */
11
10
  export type AIProviderKey = 'openai' | 'anthropic' | 'xai' | 'custom';
@@ -82,7 +81,7 @@ export type AIProviderConnectionPublic = Omit<AIProviderConnection, 'apiKeyEncry
82
81
  keyManagedExternally: boolean;
83
82
  hasKey: boolean;
84
83
  };
85
- export type AIFeatureCategory = 'seo' | 'media' | 'content' | 'forms' | 'security' | 'developer' | 'search';
84
+ type AIFeatureCategory = 'seo' | 'media' | 'content' | 'forms' | 'security' | 'developer' | 'search';
86
85
  export interface AIFeatureConfig {
87
86
  key: string;
88
87
  label: string;
@@ -95,19 +94,11 @@ export interface AIFeatureConfig {
95
94
  fallbackModelIds?: string[];
96
95
  approvalRequired: boolean;
97
96
  allowBulkApply: boolean;
98
- maxMonthlyTokens?: number;
99
97
  maxRequestsPerDay?: number;
100
- rolesAllowed: string[];
101
- permissionsRequired: string[];
102
- dataPolicyKey?: string;
103
98
  }
104
99
  export interface AIBudgetSettings {
105
100
  monthlyTokenLimit?: number;
106
- monthlyCostLimit?: number;
107
- alertAtPercentages: number[];
108
101
  disableWhenExceeded: boolean;
109
- perFeatureLimits?: Record<string, number>;
110
- perUserLimits?: Record<string, number>;
111
102
  }
112
103
  /**
113
104
  * Brand voice profile. A reusable description of how generated copy should
@@ -145,11 +136,9 @@ export interface AISettings {
145
136
  defaultTextModelId?: string;
146
137
  defaultReasoningModelId?: string;
147
138
  defaultVisionModelId?: string;
148
- defaultEmbeddingModelId?: string;
149
139
  defaultImageGenerationModelId?: string;
150
140
  features: AIFeatureConfig[];
151
141
  budget: AIBudgetSettings;
152
- logsRetentionDays: number;
153
142
  /** Active brand voice. Absent until an admin configures it. */
154
143
  brandVoice?: BrandVoiceProfile;
155
144
  }
@@ -171,7 +160,13 @@ export interface AIConfigDocument {
171
160
  updatedAt?: string;
172
161
  updatedBy?: string;
173
162
  }
174
- /** Monthly usage summary for the budget meter (real counts; 0 until Phase 4). */
163
+ /**
164
+ * Monthly usage summary for the budget meter. Sums the token fields the
165
+ * governed runtime writes to `prenta_audit_logs` (`ai_usage` rows:
166
+ * `promptTokens` / `completionTokens` / `totalTokens`) plus legacy
167
+ * `tokensUsed` / `totalTokensUsed` rows from older handlers. Real counts —
168
+ * 0 when nothing has run in the period, never a fabricated number.
169
+ */
175
170
  export interface AIUsageSummary {
176
171
  totalTokens: number;
177
172
  promptTokens: number;
@@ -190,4 +185,5 @@ export interface AIConnectionTestResult {
190
185
  message: string;
191
186
  testedAt: string;
192
187
  }
188
+ export {};
193
189
  //# sourceMappingURL=types.d.ts.map