@wootsup/yt-builder-mcp 1.11.0 → 2.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 (853) hide show
  1. package/README.md +131 -33
  2. package/bin/node-floor.d.ts +28 -0
  3. package/bin/node-floor.js +123 -0
  4. package/bin/yt-builder-mcp.js +52 -11
  5. package/dist/auth.d.ts +3 -121
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/auth.js +40 -170
  8. package/dist/auth.js.map +1 -1
  9. package/dist/catalog/build-catalog.d.ts +4 -49
  10. package/dist/catalog/build-catalog.d.ts.map +1 -1
  11. package/dist/catalog/build-catalog.js +15 -126
  12. package/dist/catalog/build-catalog.js.map +1 -1
  13. package/dist/catalog/json-schema.d.ts +3 -0
  14. package/dist/catalog/json-schema.d.ts.map +1 -0
  15. package/dist/catalog/json-schema.js +68 -0
  16. package/dist/catalog/json-schema.js.map +1 -0
  17. package/dist/catalog/output-shapes.d.ts +6 -0
  18. package/dist/catalog/output-shapes.d.ts.map +1 -0
  19. package/dist/catalog/output-shapes.js +89 -0
  20. package/dist/catalog/output-shapes.js.map +1 -0
  21. package/dist/catalog/tool-catalog-markdown.d.ts +1 -37
  22. package/dist/catalog/tool-catalog-markdown.d.ts.map +1 -1
  23. package/dist/catalog/tool-catalog-markdown.js +9 -41
  24. package/dist/catalog/tool-catalog-markdown.js.map +1 -1
  25. package/dist/cli/doctor-command.d.ts +2 -7
  26. package/dist/cli/doctor-command.d.ts.map +1 -1
  27. package/dist/cli/doctor-command.js +33 -54
  28. package/dist/cli/doctor-command.js.map +1 -1
  29. package/dist/cli/invocation-decision.d.ts +4 -52
  30. package/dist/cli/invocation-decision.d.ts.map +1 -1
  31. package/dist/cli/invocation-decision.js +45 -54
  32. package/dist/cli/invocation-decision.js.map +1 -1
  33. package/dist/client.d.ts +3 -107
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +34 -115
  36. package/dist/client.js.map +1 -1
  37. package/dist/clients/claude-code.d.ts +0 -15
  38. package/dist/clients/claude-code.d.ts.map +1 -1
  39. package/dist/clients/claude-code.js +0 -22
  40. package/dist/clients/claude-code.js.map +1 -1
  41. package/dist/clients/claude-desktop.d.ts +0 -16
  42. package/dist/clients/claude-desktop.d.ts.map +1 -1
  43. package/dist/clients/claude-desktop.js +0 -18
  44. package/dist/clients/claude-desktop.js.map +1 -1
  45. package/dist/clients/cline.d.ts +0 -23
  46. package/dist/clients/cline.d.ts.map +1 -1
  47. package/dist/clients/cline.js +0 -33
  48. package/dist/clients/cline.js.map +1 -1
  49. package/dist/clients/codex-cli.d.ts +0 -39
  50. package/dist/clients/codex-cli.d.ts.map +1 -1
  51. package/dist/clients/codex-cli.js +0 -137
  52. package/dist/clients/codex-cli.js.map +1 -1
  53. package/dist/clients/continue.d.ts +0 -10
  54. package/dist/clients/continue.d.ts.map +1 -1
  55. package/dist/clients/continue.js +0 -26
  56. package/dist/clients/continue.js.map +1 -1
  57. package/dist/clients/cursor.d.ts +0 -9
  58. package/dist/clients/cursor.d.ts.map +1 -1
  59. package/dist/clients/cursor.js +0 -11
  60. package/dist/clients/cursor.js.map +1 -1
  61. package/dist/clients/gemini-cli.d.ts +0 -15
  62. package/dist/clients/gemini-cli.d.ts.map +1 -1
  63. package/dist/clients/gemini-cli.js +0 -17
  64. package/dist/clients/gemini-cli.js.map +1 -1
  65. package/dist/clients/home.d.ts +0 -12
  66. package/dist/clients/home.d.ts.map +1 -1
  67. package/dist/clients/home.js +0 -12
  68. package/dist/clients/home.js.map +1 -1
  69. package/dist/clients/index.d.ts +0 -150
  70. package/dist/clients/index.d.ts.map +1 -1
  71. package/dist/clients/index.js +0 -112
  72. package/dist/clients/index.js.map +1 -1
  73. package/dist/clients/roo-code.d.ts +0 -20
  74. package/dist/clients/roo-code.d.ts.map +1 -1
  75. package/dist/clients/roo-code.js +0 -22
  76. package/dist/clients/roo-code.js.map +1 -1
  77. package/dist/clients/zed.d.ts +0 -9
  78. package/dist/clients/zed.d.ts.map +1 -1
  79. package/dist/clients/zed.js +0 -18
  80. package/dist/clients/zed.js.map +1 -1
  81. package/dist/compat-floor.d.ts +35 -0
  82. package/dist/compat-floor.d.ts.map +1 -0
  83. package/dist/compat-floor.js +84 -0
  84. package/dist/compat-floor.js.map +1 -0
  85. package/dist/compat-probe.d.ts +6 -0
  86. package/dist/compat-probe.d.ts.map +1 -0
  87. package/dist/compat-probe.js +20 -0
  88. package/dist/compat-probe.js.map +1 -0
  89. package/dist/diagnostics/ca-reexec.d.ts +0 -30
  90. package/dist/diagnostics/ca-reexec.d.ts.map +1 -1
  91. package/dist/diagnostics/ca-reexec.js +0 -62
  92. package/dist/diagnostics/ca-reexec.js.map +1 -1
  93. package/dist/diagnostics/ca-trust.d.ts +0 -22
  94. package/dist/diagnostics/ca-trust.d.ts.map +1 -1
  95. package/dist/diagnostics/ca-trust.js +3 -42
  96. package/dist/diagnostics/ca-trust.js.map +1 -1
  97. package/dist/diagnostics/diagnose-network-error.d.ts +1 -38
  98. package/dist/diagnostics/diagnose-network-error.d.ts.map +1 -1
  99. package/dist/diagnostics/diagnose-network-error.js +26 -37
  100. package/dist/diagnostics/diagnose-network-error.js.map +1 -1
  101. package/dist/diagnostics/doctor.d.ts +13 -65
  102. package/dist/diagnostics/doctor.d.ts.map +1 -1
  103. package/dist/diagnostics/doctor.js +46 -70
  104. package/dist/diagnostics/doctor.js.map +1 -1
  105. package/dist/diagnostics/startup-self-probe.d.ts +3 -25
  106. package/dist/diagnostics/startup-self-probe.d.ts.map +1 -1
  107. package/dist/diagnostics/startup-self-probe.js +11 -48
  108. package/dist/diagnostics/startup-self-probe.js.map +1 -1
  109. package/dist/errors/hints.d.ts +0 -44
  110. package/dist/errors/hints.d.ts.map +1 -1
  111. package/dist/errors/hints.js +0 -32
  112. package/dist/errors/hints.js.map +1 -1
  113. package/dist/errors/mask.d.ts +0 -31
  114. package/dist/errors/mask.d.ts.map +1 -1
  115. package/dist/errors/mask.js +0 -32
  116. package/dist/errors/mask.js.map +1 -1
  117. package/dist/errors/redact-credentials.d.ts +2 -0
  118. package/dist/errors/redact-credentials.d.ts.map +1 -0
  119. package/dist/errors/redact-credentials.js +13 -0
  120. package/dist/errors/redact-credentials.js.map +1 -0
  121. package/dist/errors/sanitize.d.ts +0 -29
  122. package/dist/errors/sanitize.d.ts.map +1 -1
  123. package/dist/errors/sanitize.js +0 -40
  124. package/dist/errors/sanitize.js.map +1 -1
  125. package/dist/errors.d.ts +6 -17
  126. package/dist/errors.d.ts.map +1 -1
  127. package/dist/errors.js +16 -21
  128. package/dist/errors.js.map +1 -1
  129. package/dist/gateway/advanced-read-tool.d.ts +0 -32
  130. package/dist/gateway/advanced-read-tool.d.ts.map +1 -1
  131. package/dist/gateway/advanced-read-tool.js +43 -111
  132. package/dist/gateway/advanced-read-tool.js.map +1 -1
  133. package/dist/gateway/advanced-tool/discovery.d.ts +1 -12
  134. package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -1
  135. package/dist/gateway/advanced-tool/discovery.js +18 -16
  136. package/dist/gateway/advanced-tool/discovery.js.map +1 -1
  137. package/dist/gateway/advanced-tool/domains.d.ts +7 -66
  138. package/dist/gateway/advanced-tool/domains.d.ts.map +1 -1
  139. package/dist/gateway/advanced-tool/domains.js +36 -80
  140. package/dist/gateway/advanced-tool/domains.js.map +1 -1
  141. package/dist/gateway/advanced-tool/execute.d.ts +0 -25
  142. package/dist/gateway/advanced-tool/execute.d.ts.map +1 -1
  143. package/dist/gateway/advanced-tool/execute.js +13 -49
  144. package/dist/gateway/advanced-tool/execute.js.map +1 -1
  145. package/dist/gateway/advanced-tool/index.d.ts +1 -32
  146. package/dist/gateway/advanced-tool/index.d.ts.map +1 -1
  147. package/dist/gateway/advanced-tool/index.js +1 -35
  148. package/dist/gateway/advanced-tool/index.js.map +1 -1
  149. package/dist/gateway/advanced-tool/register.d.ts +0 -14
  150. package/dist/gateway/advanced-tool/register.d.ts.map +1 -1
  151. package/dist/gateway/advanced-tool/register.js +22 -58
  152. package/dist/gateway/advanced-tool/register.js.map +1 -1
  153. package/dist/gateway/advanced-tool.d.ts +0 -11
  154. package/dist/gateway/advanced-tool.d.ts.map +1 -1
  155. package/dist/gateway/advanced-tool.js +0 -11
  156. package/dist/gateway/advanced-tool.js.map +1 -1
  157. package/dist/gateway/capturing-server.d.ts +3 -88
  158. package/dist/gateway/capturing-server.d.ts.map +1 -1
  159. package/dist/gateway/capturing-server.js +0 -77
  160. package/dist/gateway/capturing-server.js.map +1 -1
  161. package/dist/gateway/essentials.d.ts +1 -63
  162. package/dist/gateway/essentials.d.ts.map +1 -1
  163. package/dist/gateway/essentials.js +10 -174
  164. package/dist/gateway/essentials.js.map +1 -1
  165. package/dist/gateway/test-support.d.ts +0 -28
  166. package/dist/gateway/test-support.d.ts.map +1 -1
  167. package/dist/gateway/test-support.js +0 -29
  168. package/dist/gateway/test-support.js.map +1 -1
  169. package/dist/health-response.d.ts +2 -0
  170. package/dist/health-response.d.ts.map +1 -0
  171. package/dist/health-response.js +7 -0
  172. package/dist/health-response.js.map +1 -0
  173. package/dist/icon-data.js +0 -3
  174. package/dist/icon-data.js.map +1 -1
  175. package/dist/index.d.ts +7 -23
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +46 -130
  178. package/dist/index.js.map +1 -1
  179. package/dist/install-skill.d.ts +0 -44
  180. package/dist/install-skill.d.ts.map +1 -1
  181. package/dist/install-skill.js +5 -46
  182. package/dist/install-skill.js.map +1 -1
  183. package/dist/net/extra-headers.d.ts +0 -58
  184. package/dist/net/extra-headers.d.ts.map +1 -1
  185. package/dist/net/extra-headers.js +0 -67
  186. package/dist/net/extra-headers.js.map +1 -1
  187. package/dist/net/internal-host.d.ts +0 -31
  188. package/dist/net/internal-host.d.ts.map +1 -1
  189. package/dist/net/internal-host.js +7 -65
  190. package/dist/net/internal-host.js.map +1 -1
  191. package/dist/net/proxy-dispatcher.d.ts +0 -37
  192. package/dist/net/proxy-dispatcher.d.ts.map +1 -1
  193. package/dist/net/proxy-dispatcher.js +0 -40
  194. package/dist/net/proxy-dispatcher.js.map +1 -1
  195. package/dist/net/public-fetch.d.ts +53 -0
  196. package/dist/net/public-fetch.d.ts.map +1 -0
  197. package/dist/net/public-fetch.js +143 -0
  198. package/dist/net/public-fetch.js.map +1 -0
  199. package/dist/net/public-page-report.d.ts +26 -0
  200. package/dist/net/public-page-report.d.ts.map +1 -0
  201. package/dist/net/public-page-report.js +145 -0
  202. package/dist/net/public-page-report.js.map +1 -0
  203. package/dist/net/redirect-safe-fetch.d.ts +14 -0
  204. package/dist/net/redirect-safe-fetch.d.ts.map +1 -0
  205. package/dist/net/redirect-safe-fetch.js +61 -0
  206. package/dist/net/redirect-safe-fetch.js.map +1 -0
  207. package/dist/net/session-recovery-fetch.d.ts +7 -0
  208. package/dist/net/session-recovery-fetch.d.ts.map +1 -0
  209. package/dist/net/session-recovery-fetch.js +139 -0
  210. package/dist/net/session-recovery-fetch.js.map +1 -0
  211. package/dist/net/site-basic-auth.d.ts +1 -59
  212. package/dist/net/site-basic-auth.d.ts.map +1 -1
  213. package/dist/net/site-basic-auth.js +5 -94
  214. package/dist/net/site-basic-auth.js.map +1 -1
  215. package/dist/package-version.d.ts +2 -0
  216. package/dist/package-version.d.ts.map +1 -0
  217. package/dist/package-version.js +15 -0
  218. package/dist/package-version.js.map +1 -0
  219. package/dist/platform/detect.d.ts +0 -54
  220. package/dist/platform/detect.d.ts.map +1 -1
  221. package/dist/platform/detect.js +0 -53
  222. package/dist/platform/detect.js.map +1 -1
  223. package/dist/platform/index.d.ts +0 -100
  224. package/dist/platform/index.d.ts.map +1 -1
  225. package/dist/platform/index.js +0 -106
  226. package/dist/platform/index.js.map +1 -1
  227. package/dist/platform/joomla.d.ts +0 -35
  228. package/dist/platform/joomla.d.ts.map +1 -1
  229. package/dist/platform/joomla.js +0 -35
  230. package/dist/platform/joomla.js.map +1 -1
  231. package/dist/proxy/bridge.d.ts +36 -76
  232. package/dist/proxy/bridge.d.ts.map +1 -1
  233. package/dist/proxy/bridge.js +443 -136
  234. package/dist/proxy/bridge.js.map +1 -1
  235. package/dist/proxy/confirm-elicitation.d.ts +22 -0
  236. package/dist/proxy/confirm-elicitation.d.ts.map +1 -0
  237. package/dist/proxy/confirm-elicitation.js +170 -0
  238. package/dist/proxy/confirm-elicitation.js.map +1 -0
  239. package/dist/proxy/confirm-question-contract.d.ts +6 -0
  240. package/dist/proxy/confirm-question-contract.d.ts.map +1 -0
  241. package/dist/proxy/confirm-question-contract.js +9 -0
  242. package/dist/proxy/confirm-question-contract.js.map +1 -0
  243. package/dist/proxy/confirm-question.d.ts +2 -0
  244. package/dist/proxy/confirm-question.d.ts.map +1 -0
  245. package/dist/proxy/confirm-question.js +125 -0
  246. package/dist/proxy/confirm-question.js.map +1 -0
  247. package/dist/proxy/instructions.d.ts +4 -0
  248. package/dist/proxy/instructions.d.ts.map +1 -0
  249. package/dist/proxy/instructions.js +12 -0
  250. package/dist/proxy/instructions.js.map +1 -0
  251. package/dist/proxy/mode.d.ts +14 -9
  252. package/dist/proxy/mode.d.ts.map +1 -1
  253. package/dist/proxy/mode.js +58 -34
  254. package/dist/proxy/mode.js.map +1 -1
  255. package/dist/proxy/multi-site.d.ts +42 -0
  256. package/dist/proxy/multi-site.d.ts.map +1 -0
  257. package/dist/proxy/multi-site.js +134 -0
  258. package/dist/proxy/multi-site.js.map +1 -0
  259. package/dist/proxy/request-headers.d.ts +9 -0
  260. package/dist/proxy/request-headers.d.ts.map +1 -0
  261. package/dist/proxy/request-headers.js +17 -0
  262. package/dist/proxy/request-headers.js.map +1 -0
  263. package/dist/proxy/shadow-tools.d.ts +9 -0
  264. package/dist/proxy/shadow-tools.d.ts.map +1 -0
  265. package/dist/proxy/shadow-tools.js +42 -0
  266. package/dist/proxy/shadow-tools.js.map +1 -0
  267. package/dist/proxy/tools-list-cap.d.ts +7 -0
  268. package/dist/proxy/tools-list-cap.d.ts.map +1 -0
  269. package/dist/proxy/tools-list-cap.js +14 -0
  270. package/dist/proxy/tools-list-cap.js.map +1 -0
  271. package/dist/proxy/transport-uncertainty.d.ts +2 -0
  272. package/dist/proxy/transport-uncertainty.d.ts.map +1 -0
  273. package/dist/proxy/transport-uncertainty.js +8 -0
  274. package/dist/proxy/transport-uncertainty.js.map +1 -0
  275. package/dist/proxy/upstream-error.d.ts +7 -0
  276. package/dist/proxy/upstream-error.d.ts.map +1 -0
  277. package/dist/proxy/upstream-error.js +35 -0
  278. package/dist/proxy/upstream-error.js.map +1 -0
  279. package/dist/proxy/wire-dialect.d.ts +5 -0
  280. package/dist/proxy/wire-dialect.d.ts.map +1 -0
  281. package/dist/proxy/wire-dialect.js +33 -0
  282. package/dist/proxy/wire-dialect.js.map +1 -0
  283. package/dist/server.d.ts +14 -82
  284. package/dist/server.d.ts.map +1 -1
  285. package/dist/server.js +107 -203
  286. package/dist/server.js.map +1 -1
  287. package/dist/setup/banner-color.d.ts +13 -0
  288. package/dist/setup/banner-color.d.ts.map +1 -0
  289. package/dist/setup/banner-color.js +93 -0
  290. package/dist/setup/banner-color.js.map +1 -0
  291. package/dist/setup/banner.d.ts +22 -0
  292. package/dist/setup/banner.d.ts.map +1 -0
  293. package/dist/setup/banner.js +119 -0
  294. package/dist/setup/banner.js.map +1 -0
  295. package/dist/setup/ca-recovery.d.ts +20 -0
  296. package/dist/setup/ca-recovery.d.ts.map +1 -0
  297. package/dist/setup/ca-recovery.js +92 -0
  298. package/dist/setup/ca-recovery.js.map +1 -0
  299. package/dist/setup/closing.d.ts +15 -0
  300. package/dist/setup/closing.d.ts.map +1 -0
  301. package/dist/setup/closing.js +43 -0
  302. package/dist/setup/closing.js.map +1 -0
  303. package/dist/setup/corporate-ca.d.ts +16 -0
  304. package/dist/setup/corporate-ca.d.ts.map +1 -0
  305. package/dist/setup/corporate-ca.js +32 -0
  306. package/dist/setup/corporate-ca.js.map +1 -0
  307. package/dist/setup/host-platform.d.ts +8 -0
  308. package/dist/setup/host-platform.d.ts.map +1 -0
  309. package/dist/setup/host-platform.js +21 -0
  310. package/dist/setup/host-platform.js.map +1 -0
  311. package/dist/setup/install-configs.d.ts +18 -0
  312. package/dist/setup/install-configs.d.ts.map +1 -0
  313. package/dist/setup/install-configs.js +70 -0
  314. package/dist/setup/install-configs.js.map +1 -0
  315. package/dist/setup/multi-site-loop.d.ts +14 -0
  316. package/dist/setup/multi-site-loop.d.ts.map +1 -0
  317. package/dist/setup/multi-site-loop.js +20 -0
  318. package/dist/setup/multi-site-loop.js.map +1 -0
  319. package/dist/setup/phases.d.ts +18 -0
  320. package/dist/setup/phases.d.ts.map +1 -0
  321. package/dist/setup/phases.js +55 -0
  322. package/dist/setup/phases.js.map +1 -0
  323. package/dist/setup/registry-remedy.d.ts +12 -0
  324. package/dist/setup/registry-remedy.d.ts.map +1 -0
  325. package/dist/setup/registry-remedy.js +37 -0
  326. package/dist/setup/registry-remedy.js.map +1 -0
  327. package/dist/setup/resume.d.ts +28 -0
  328. package/dist/setup/resume.d.ts.map +1 -0
  329. package/dist/setup/resume.js +99 -0
  330. package/dist/setup/resume.js.map +1 -0
  331. package/dist/setup-ca-env.d.ts +0 -30
  332. package/dist/setup-ca-env.d.ts.map +1 -1
  333. package/dist/setup-ca-env.js +0 -28
  334. package/dist/setup-ca-env.js.map +1 -1
  335. package/dist/setup-ca-reprobe.d.ts +0 -25
  336. package/dist/setup-ca-reprobe.d.ts.map +1 -1
  337. package/dist/setup-ca-reprobe.js +0 -34
  338. package/dist/setup-ca-reprobe.js.map +1 -1
  339. package/dist/setup-cli-confirm.d.ts +0 -34
  340. package/dist/setup-cli-confirm.d.ts.map +1 -1
  341. package/dist/setup-cli-confirm.js +0 -36
  342. package/dist/setup-cli-confirm.js.map +1 -1
  343. package/dist/setup-cli.d.ts +6 -167
  344. package/dist/setup-cli.d.ts.map +1 -1
  345. package/dist/setup-cli.js +161 -173
  346. package/dist/setup-cli.js.map +1 -1
  347. package/dist/setup-npx-spec.d.ts +0 -36
  348. package/dist/setup-npx-spec.d.ts.map +1 -1
  349. package/dist/setup-npx-spec.js +0 -38
  350. package/dist/setup-npx-spec.js.map +1 -1
  351. package/dist/setup-prompts.d.ts +5 -70
  352. package/dist/setup-prompts.d.ts.map +1 -1
  353. package/dist/setup-prompts.js +67 -107
  354. package/dist/setup-prompts.js.map +1 -1
  355. package/dist/setup-token.d.ts +0 -34
  356. package/dist/setup-token.d.ts.map +1 -1
  357. package/dist/setup-token.js +0 -40
  358. package/dist/setup-token.js.map +1 -1
  359. package/dist/setup-wizard-defaults.d.ts +0 -36
  360. package/dist/setup-wizard-defaults.d.ts.map +1 -1
  361. package/dist/setup-wizard-defaults.js +0 -75
  362. package/dist/setup-wizard-defaults.js.map +1 -1
  363. package/dist/setup-wizard-handshake.d.ts +1 -19
  364. package/dist/setup-wizard-handshake.d.ts.map +1 -1
  365. package/dist/setup-wizard-handshake.js +8 -45
  366. package/dist/setup-wizard-handshake.js.map +1 -1
  367. package/dist/setup-wizard-types.d.ts +16 -209
  368. package/dist/setup-wizard-types.d.ts.map +1 -1
  369. package/dist/setup-wizard-types.js +0 -9
  370. package/dist/setup-wizard-types.js.map +1 -1
  371. package/dist/setup-wizard.d.ts +0 -28
  372. package/dist/setup-wizard.d.ts.map +1 -1
  373. package/dist/setup-wizard.js +167 -412
  374. package/dist/setup-wizard.js.map +1 -1
  375. package/dist/setup.d.ts +0 -15
  376. package/dist/setup.d.ts.map +1 -1
  377. package/dist/setup.js +0 -15
  378. package/dist/setup.js.map +1 -1
  379. package/dist/sites/bootstrap.d.ts +0 -38
  380. package/dist/sites/bootstrap.d.ts.map +1 -1
  381. package/dist/sites/bootstrap.js +0 -44
  382. package/dist/sites/bootstrap.js.map +1 -1
  383. package/dist/sites/cli/add-site.d.ts +0 -76
  384. package/dist/sites/cli/add-site.d.ts.map +1 -1
  385. package/dist/sites/cli/add-site.js +0 -71
  386. package/dist/sites/cli/add-site.js.map +1 -1
  387. package/dist/sites/cli/list-sites.d.ts +0 -31
  388. package/dist/sites/cli/list-sites.d.ts.map +1 -1
  389. package/dist/sites/cli/list-sites.js +0 -45
  390. package/dist/sites/cli/list-sites.js.map +1 -1
  391. package/dist/sites/cli/remove-site.d.ts +0 -37
  392. package/dist/sites/cli/remove-site.d.ts.map +1 -1
  393. package/dist/sites/cli/remove-site.js +0 -37
  394. package/dist/sites/cli/remove-site.js.map +1 -1
  395. package/dist/sites/cli/set-default.d.ts +0 -25
  396. package/dist/sites/cli/set-default.d.ts.map +1 -1
  397. package/dist/sites/cli/set-default.js +0 -22
  398. package/dist/sites/cli/set-default.js.map +1 -1
  399. package/dist/sites/cli/test-site.d.ts +0 -44
  400. package/dist/sites/cli/test-site.d.ts.map +1 -1
  401. package/dist/sites/cli/test-site.js +0 -39
  402. package/dist/sites/cli/test-site.js.map +1 -1
  403. package/dist/sites/client-pool.d.ts +11 -164
  404. package/dist/sites/client-pool.d.ts.map +1 -1
  405. package/dist/sites/client-pool.js +73 -194
  406. package/dist/sites/client-pool.js.map +1 -1
  407. package/dist/sites/config-problem.d.ts +2 -0
  408. package/dist/sites/config-problem.d.ts.map +1 -0
  409. package/dist/sites/config-problem.js +13 -0
  410. package/dist/sites/config-problem.js.map +1 -0
  411. package/dist/sites/env-bridge.d.ts +0 -69
  412. package/dist/sites/env-bridge.d.ts.map +1 -1
  413. package/dist/sites/env-bridge.js +2 -71
  414. package/dist/sites/env-bridge.js.map +1 -1
  415. package/dist/sites/op-inject.d.ts +23 -0
  416. package/dist/sites/op-inject.d.ts.map +1 -0
  417. package/dist/sites/op-inject.js +131 -0
  418. package/dist/sites/op-inject.js.map +1 -0
  419. package/dist/sites/paths.d.ts +0 -19
  420. package/dist/sites/paths.d.ts.map +1 -1
  421. package/dist/sites/paths.js +0 -19
  422. package/dist/sites/paths.js.map +1 -1
  423. package/dist/sites/probe.d.ts +1 -47
  424. package/dist/sites/probe.d.ts.map +1 -1
  425. package/dist/sites/probe.js +4 -46
  426. package/dist/sites/probe.js.map +1 -1
  427. package/dist/sites/registry.d.ts +4 -135
  428. package/dist/sites/registry.d.ts.map +1 -1
  429. package/dist/sites/registry.js +4 -98
  430. package/dist/sites/registry.js.map +1 -1
  431. package/dist/sites/schema.d.ts +3 -51
  432. package/dist/sites/schema.d.ts.map +1 -1
  433. package/dist/sites/schema.js +6 -52
  434. package/dist/sites/schema.js.map +1 -1
  435. package/dist/sites/secret-resolver.d.ts +6 -76
  436. package/dist/sites/secret-resolver.d.ts.map +1 -1
  437. package/dist/sites/secret-resolver.js +39 -104
  438. package/dist/sites/secret-resolver.js.map +1 -1
  439. package/dist/sites/store.d.ts +12 -69
  440. package/dist/sites/store.d.ts.map +1 -1
  441. package/dist/sites/store.js +37 -86
  442. package/dist/sites/store.js.map +1 -1
  443. package/dist/sites/substituted-registry.d.ts +11 -0
  444. package/dist/sites/substituted-registry.d.ts.map +1 -0
  445. package/dist/sites/substituted-registry.js +13 -0
  446. package/dist/sites/substituted-registry.js.map +1 -0
  447. package/dist/sites/tools/fetch-public-url.d.ts +4 -0
  448. package/dist/sites/tools/fetch-public-url.d.ts.map +1 -0
  449. package/dist/sites/tools/fetch-public-url.js +231 -0
  450. package/dist/sites/tools/fetch-public-url.js.map +1 -0
  451. package/dist/sites/tools/index.d.ts +2 -26
  452. package/dist/sites/tools/index.d.ts.map +1 -1
  453. package/dist/sites/tools/index.js +5 -28
  454. package/dist/sites/tools/index.js.map +1 -1
  455. package/dist/sites/tools/sites-list.d.ts +3 -47
  456. package/dist/sites/tools/sites-list.d.ts.map +1 -1
  457. package/dist/sites/tools/sites-list.js +68 -80
  458. package/dist/sites/tools/sites-list.js.map +1 -1
  459. package/dist/sites/tools/sites-overview.d.ts +4 -0
  460. package/dist/sites/tools/sites-overview.d.ts.map +1 -0
  461. package/dist/sites/tools/sites-overview.js +209 -0
  462. package/dist/sites/tools/sites-overview.js.map +1 -0
  463. package/dist/sites/tools/sites-test.d.ts +2 -45
  464. package/dist/sites/tools/sites-test.d.ts.map +1 -1
  465. package/dist/sites/tools/sites-test.js +1 -56
  466. package/dist/sites/tools/sites-test.js.map +1 -1
  467. package/dist/sites/tools/use-site.d.ts +2 -45
  468. package/dist/sites/tools/use-site.d.ts.map +1 -1
  469. package/dist/sites/tools/use-site.js +31 -80
  470. package/dist/sites/tools/use-site.js.map +1 -1
  471. package/dist/sites/unknown-name-result.d.ts +10 -0
  472. package/dist/sites/unknown-name-result.d.ts.map +1 -0
  473. package/dist/sites/unknown-name-result.js +13 -0
  474. package/dist/sites/unknown-name-result.js.map +1 -0
  475. package/dist/skill-instructions.d.ts +7 -0
  476. package/dist/skill-instructions.d.ts.map +1 -0
  477. package/dist/skill-instructions.js +84 -0
  478. package/dist/skill-instructions.js.map +1 -0
  479. package/dist/skill-loader.d.ts +0 -102
  480. package/dist/skill-loader.d.ts.map +1 -1
  481. package/dist/skill-loader.js +4 -142
  482. package/dist/skill-loader.js.map +1 -1
  483. package/dist/skill-manifest.d.ts +0 -20
  484. package/dist/skill-manifest.d.ts.map +1 -1
  485. package/dist/skill-manifest.js +1 -21
  486. package/dist/skill-manifest.js.map +1 -1
  487. package/dist/skill-topics.d.ts +19 -0
  488. package/dist/skill-topics.d.ts.map +1 -0
  489. package/dist/skill-topics.js +181 -0
  490. package/dist/skill-topics.js.map +1 -0
  491. package/dist/tools/active-site.d.ts +16 -0
  492. package/dist/tools/active-site.d.ts.map +1 -0
  493. package/dist/tools/active-site.js +55 -0
  494. package/dist/tools/active-site.js.map +1 -0
  495. package/dist/tools/bind-guidance.d.ts +2 -72
  496. package/dist/tools/bind-guidance.d.ts.map +1 -1
  497. package/dist/tools/bind-guidance.js +2 -72
  498. package/dist/tools/bind-guidance.js.map +1 -1
  499. package/dist/tools/budgeted-table.d.ts +16 -0
  500. package/dist/tools/budgeted-table.d.ts.map +1 -0
  501. package/dist/tools/budgeted-table.js +203 -0
  502. package/dist/tools/budgeted-table.js.map +1 -0
  503. package/dist/tools/elements/bind-output-schema.d.ts +69 -0
  504. package/dist/tools/elements/bind-output-schema.d.ts.map +1 -0
  505. package/dist/tools/elements/bind-output-schema.js +64 -0
  506. package/dist/tools/elements/bind-output-schema.js.map +1 -0
  507. package/dist/tools/elements/builders.d.ts +1 -22
  508. package/dist/tools/elements/builders.d.ts.map +1 -1
  509. package/dist/tools/elements/builders.js +171 -133
  510. package/dist/tools/elements/builders.js.map +1 -1
  511. package/dist/tools/elements/index.d.ts +0 -26
  512. package/dist/tools/elements/index.d.ts.map +1 -1
  513. package/dist/tools/elements/index.js +0 -25
  514. package/dist/tools/elements/index.js.map +1 -1
  515. package/dist/tools/elements/read-output-schemas.d.ts +121 -0
  516. package/dist/tools/elements/read-output-schemas.d.ts.map +1 -0
  517. package/dist/tools/elements/read-output-schemas.js +225 -0
  518. package/dist/tools/elements/read-output-schemas.js.map +1 -0
  519. package/dist/tools/elements/write-output-schemas.d.ts +185 -0
  520. package/dist/tools/elements/write-output-schemas.d.ts.map +1 -0
  521. package/dist/tools/elements/write-output-schemas.js +98 -0
  522. package/dist/tools/elements/write-output-schemas.js.map +1 -0
  523. package/dist/tools/elements.d.ts +0 -11
  524. package/dist/tools/elements.d.ts.map +1 -1
  525. package/dist/tools/elements.js +0 -11
  526. package/dist/tools/elements.js.map +1 -1
  527. package/dist/tools/format/health-format.d.ts +42 -43
  528. package/dist/tools/format/health-format.d.ts.map +1 -1
  529. package/dist/tools/format/health-format.js +32 -55
  530. package/dist/tools/format/health-format.js.map +1 -1
  531. package/dist/tools/format/inspection-format.d.ts +4 -49
  532. package/dist/tools/format/inspection-format.d.ts.map +1 -1
  533. package/dist/tools/format/inspection-format.js +46 -114
  534. package/dist/tools/format/inspection-format.js.map +1 -1
  535. package/dist/tools/format/pages-format.d.ts +32 -69
  536. package/dist/tools/format/pages-format.d.ts.map +1 -1
  537. package/dist/tools/format/pages-format.js +115 -133
  538. package/dist/tools/format/pages-format.js.map +1 -1
  539. package/dist/tools/format/sources-format.d.ts +5 -39
  540. package/dist/tools/format/sources-format.d.ts.map +1 -1
  541. package/dist/tools/format/sources-format.js +57 -87
  542. package/dist/tools/format/sources-format.js.map +1 -1
  543. package/dist/tools/health-output-schemas.d.ts +82 -0
  544. package/dist/tools/health-output-schemas.d.ts.map +1 -0
  545. package/dist/tools/health-output-schemas.js +130 -0
  546. package/dist/tools/health-output-schemas.js.map +1 -0
  547. package/dist/tools/health.d.ts +2 -20
  548. package/dist/tools/health.d.ts.map +1 -1
  549. package/dist/tools/health.js +59 -184
  550. package/dist/tools/health.js.map +1 -1
  551. package/dist/tools/index.d.ts +1 -26
  552. package/dist/tools/index.d.ts.map +1 -1
  553. package/dist/tools/index.js +13 -33
  554. package/dist/tools/index.js.map +1 -1
  555. package/dist/tools/inspection.d.ts +21 -17
  556. package/dist/tools/inspection.d.ts.map +1 -1
  557. package/dist/tools/inspection.js +431 -268
  558. package/dist/tools/inspection.js.map +1 -1
  559. package/dist/tools/library.d.ts +118 -126
  560. package/dist/tools/library.d.ts.map +1 -1
  561. package/dist/tools/library.js +723 -456
  562. package/dist/tools/library.js.map +1 -1
  563. package/dist/tools/local-content/builders.d.ts +1 -17
  564. package/dist/tools/local-content/builders.d.ts.map +1 -1
  565. package/dist/tools/local-content/builders.js +105 -48
  566. package/dist/tools/local-content/builders.js.map +1 -1
  567. package/dist/tools/local-content/index.d.ts +0 -15
  568. package/dist/tools/local-content/index.d.ts.map +1 -1
  569. package/dist/tools/local-content/index.js +0 -14
  570. package/dist/tools/local-content/index.js.map +1 -1
  571. package/dist/tools/local-content/schemas.d.ts +31 -47
  572. package/dist/tools/local-content/schemas.d.ts.map +1 -1
  573. package/dist/tools/local-content/schemas.js +33 -62
  574. package/dist/tools/local-content/schemas.js.map +1 -1
  575. package/dist/tools/modules/builders.d.ts +4 -0
  576. package/dist/tools/modules/builders.d.ts.map +1 -0
  577. package/dist/tools/modules/builders.js +58 -0
  578. package/dist/tools/modules/builders.js.map +1 -0
  579. package/dist/tools/modules/index.d.ts +2 -0
  580. package/dist/tools/modules/index.d.ts.map +1 -0
  581. package/dist/tools/modules/index.js +2 -0
  582. package/dist/tools/modules/index.js.map +1 -0
  583. package/dist/tools/modules/module-id-schema.d.ts +4 -0
  584. package/dist/tools/modules/module-id-schema.d.ts.map +1 -0
  585. package/dist/tools/modules/module-id-schema.js +14 -0
  586. package/dist/tools/modules/module-id-schema.js.map +1 -0
  587. package/dist/tools/modules/module-type-fields.d.ts +10 -0
  588. package/dist/tools/modules/module-type-fields.d.ts.map +1 -0
  589. package/dist/tools/modules/module-type-fields.js +34 -0
  590. package/dist/tools/modules/module-type-fields.js.map +1 -0
  591. package/dist/tools/modules/modules-create.d.ts +59 -0
  592. package/dist/tools/modules/modules-create.d.ts.map +1 -0
  593. package/dist/tools/modules/modules-create.js +154 -0
  594. package/dist/tools/modules/modules-create.js.map +1 -0
  595. package/dist/tools/modules/modules-delete.d.ts +51 -0
  596. package/dist/tools/modules/modules-delete.d.ts.map +1 -0
  597. package/dist/tools/modules/modules-delete.js +180 -0
  598. package/dist/tools/modules/modules-delete.js.map +1 -0
  599. package/dist/tools/modules/modules-update.d.ts +43 -0
  600. package/dist/tools/modules/modules-update.d.ts.map +1 -0
  601. package/dist/tools/modules/modules-update.js +177 -0
  602. package/dist/tools/modules/modules-update.js.map +1 -0
  603. package/dist/tools/modules/placement-hints.d.ts +5 -0
  604. package/dist/tools/modules/placement-hints.d.ts.map +1 -0
  605. package/dist/tools/modules/placement-hints.js +90 -0
  606. package/dist/tools/modules/placement-hints.js.map +1 -0
  607. package/dist/tools/modules/placement-ways.d.ts +13 -0
  608. package/dist/tools/modules/placement-ways.d.ts.map +1 -0
  609. package/dist/tools/modules/placement-ways.js +61 -0
  610. package/dist/tools/modules/placement-ways.js.map +1 -0
  611. package/dist/tools/multi-items/builders.d.ts +1 -21
  612. package/dist/tools/multi-items/builders.d.ts.map +1 -1
  613. package/dist/tools/multi-items/builders.js +22 -67
  614. package/dist/tools/multi-items/builders.js.map +1 -1
  615. package/dist/tools/multi-items/index.d.ts +0 -15
  616. package/dist/tools/multi-items/index.d.ts.map +1 -1
  617. package/dist/tools/multi-items/index.js +0 -15
  618. package/dist/tools/multi-items/index.js.map +1 -1
  619. package/dist/tools/multi-items/item-container-map.d.ts +0 -35
  620. package/dist/tools/multi-items/item-container-map.d.ts.map +1 -1
  621. package/dist/tools/multi-items/item-container-map.js +0 -35
  622. package/dist/tools/multi-items/item-container-map.js.map +1 -1
  623. package/dist/tools/multi-items/output-schemas.d.ts +101 -0
  624. package/dist/tools/multi-items/output-schemas.d.ts.map +1 -0
  625. package/dist/tools/multi-items/output-schemas.js +74 -0
  626. package/dist/tools/multi-items/output-schemas.js.map +1 -0
  627. package/dist/tools/navigation/builders.d.ts +3 -0
  628. package/dist/tools/navigation/builders.d.ts.map +1 -0
  629. package/dist/tools/navigation/builders.js +25 -0
  630. package/dist/tools/navigation/builders.js.map +1 -0
  631. package/dist/tools/navigation/index.d.ts +2 -0
  632. package/dist/tools/navigation/index.d.ts.map +1 -0
  633. package/dist/tools/navigation/index.js +2 -0
  634. package/dist/tools/navigation/index.js.map +1 -0
  635. package/dist/tools/navigation/schemas.d.ts +16 -0
  636. package/dist/tools/navigation/schemas.d.ts.map +1 -0
  637. package/dist/tools/navigation/schemas.js +11 -0
  638. package/dist/tools/navigation/schemas.js.map +1 -0
  639. package/dist/tools/output-schema-metadata.d.ts +5 -0
  640. package/dist/tools/output-schema-metadata.d.ts.map +1 -0
  641. package/dist/tools/output-schema-metadata.js +83 -0
  642. package/dist/tools/output-schema-metadata.js.map +1 -0
  643. package/dist/tools/pages/builders.d.ts +1 -41
  644. package/dist/tools/pages/builders.d.ts.map +1 -1
  645. package/dist/tools/pages/builders.js +373 -359
  646. package/dist/tools/pages/builders.js.map +1 -1
  647. package/dist/tools/pages/header-transparency.d.ts +34 -0
  648. package/dist/tools/pages/header-transparency.d.ts.map +1 -0
  649. package/dist/tools/pages/header-transparency.js +85 -0
  650. package/dist/tools/pages/header-transparency.js.map +1 -0
  651. package/dist/tools/pages/index.d.ts +1 -19
  652. package/dist/tools/pages/index.d.ts.map +1 -1
  653. package/dist/tools/pages/index.js +1 -19
  654. package/dist/tools/pages/index.js.map +1 -1
  655. package/dist/tools/pages/schemas.d.ts +392 -224
  656. package/dist/tools/pages/schemas.d.ts.map +1 -1
  657. package/dist/tools/pages/schemas.js +605 -458
  658. package/dist/tools/pages/schemas.js.map +1 -1
  659. package/dist/tools/pages/write-output-schemas.d.ts +139 -0
  660. package/dist/tools/pages/write-output-schemas.d.ts.map +1 -0
  661. package/dist/tools/pages/write-output-schemas.js +138 -0
  662. package/dist/tools/pages/write-output-schemas.js.map +1 -0
  663. package/dist/tools/pages.d.ts +0 -11
  664. package/dist/tools/pages.d.ts.map +1 -1
  665. package/dist/tools/pages.js +0 -11
  666. package/dist/tools/pages.js.map +1 -1
  667. package/dist/tools/pool-resolve-helper.d.ts +0 -52
  668. package/dist/tools/pool-resolve-helper.d.ts.map +1 -1
  669. package/dist/tools/pool-resolve-helper.js +29 -104
  670. package/dist/tools/pool-resolve-helper.js.map +1 -1
  671. package/dist/tools/progress-phases.d.ts +0 -42
  672. package/dist/tools/progress-phases.d.ts.map +1 -1
  673. package/dist/tools/progress-phases.js +0 -44
  674. package/dist/tools/progress-phases.js.map +1 -1
  675. package/dist/tools/response-budget.d.ts +8 -0
  676. package/dist/tools/response-budget.d.ts.map +1 -0
  677. package/dist/tools/response-budget.js +7 -0
  678. package/dist/tools/response-budget.js.map +1 -0
  679. package/dist/tools/shared-schemas.d.ts +12 -43
  680. package/dist/tools/shared-schemas.d.ts.map +1 -1
  681. package/dist/tools/shared-schemas.js +42 -97
  682. package/dist/tools/shared-schemas.js.map +1 -1
  683. package/dist/tools/skill/builders.d.ts +3 -0
  684. package/dist/tools/skill/builders.d.ts.map +1 -0
  685. package/dist/tools/skill/builders.js +81 -0
  686. package/dist/tools/skill/builders.js.map +1 -0
  687. package/dist/tools/sources/builders.d.ts +1 -11
  688. package/dist/tools/sources/builders.d.ts.map +1 -1
  689. package/dist/tools/sources/builders.js +195 -173
  690. package/dist/tools/sources/builders.js.map +1 -1
  691. package/dist/tools/sources/index.d.ts +0 -17
  692. package/dist/tools/sources/index.d.ts.map +1 -1
  693. package/dist/tools/sources/index.js +0 -16
  694. package/dist/tools/sources/index.js.map +1 -1
  695. package/dist/tools/sources/read-output-schemas.d.ts +101 -0
  696. package/dist/tools/sources/read-output-schemas.d.ts.map +1 -0
  697. package/dist/tools/sources/read-output-schemas.js +105 -0
  698. package/dist/tools/sources/read-output-schemas.js.map +1 -0
  699. package/dist/tools/sources/source-name-argument.d.ts +17 -0
  700. package/dist/tools/sources/source-name-argument.d.ts.map +1 -0
  701. package/dist/tools/sources/source-name-argument.js +64 -0
  702. package/dist/tools/sources/source-name-argument.js.map +1 -0
  703. package/dist/tools/sources/write-output-schemas.d.ts +100 -0
  704. package/dist/tools/sources/write-output-schemas.d.ts.map +1 -0
  705. package/dist/tools/sources/write-output-schemas.js +61 -0
  706. package/dist/tools/sources/write-output-schemas.js.map +1 -0
  707. package/dist/tools/sources.d.ts +0 -12
  708. package/dist/tools/sources.d.ts.map +1 -1
  709. package/dist/tools/sources.js +0 -12
  710. package/dist/tools/sources.js.map +1 -1
  711. package/dist/tools/sparse-fields.d.ts +19 -194
  712. package/dist/tools/sparse-fields.d.ts.map +1 -1
  713. package/dist/tools/sparse-fields.js +212 -196
  714. package/dist/tools/sparse-fields.js.map +1 -1
  715. package/dist/tools/strict-input.d.ts +5 -0
  716. package/dist/tools/strict-input.d.ts.map +1 -0
  717. package/dist/tools/strict-input.js +55 -0
  718. package/dist/tools/strict-input.js.map +1 -0
  719. package/dist/tools/sublayout/builders.d.ts +1 -21
  720. package/dist/tools/sublayout/builders.d.ts.map +1 -1
  721. package/dist/tools/sublayout/builders.js +10 -40
  722. package/dist/tools/sublayout/builders.js.map +1 -1
  723. package/dist/tools/sublayout/index.d.ts +1 -11
  724. package/dist/tools/sublayout/index.d.ts.map +1 -1
  725. package/dist/tools/sublayout/index.js +1 -11
  726. package/dist/tools/sublayout/index.js.map +1 -1
  727. package/dist/tools/sublayout/output-schemas.d.ts +57 -0
  728. package/dist/tools/sublayout/output-schemas.d.ts.map +1 -0
  729. package/dist/tools/sublayout/output-schemas.js +51 -0
  730. package/dist/tools/sublayout/output-schemas.js.map +1 -0
  731. package/dist/tools/tool-builder/annotations.d.ts +1 -49
  732. package/dist/tools/tool-builder/annotations.d.ts.map +1 -1
  733. package/dist/tools/tool-builder/annotations.js +9 -49
  734. package/dist/tools/tool-builder/annotations.js.map +1 -1
  735. package/dist/tools/tool-builder/confirm-preview.d.ts +24 -0
  736. package/dist/tools/tool-builder/confirm-preview.d.ts.map +1 -0
  737. package/dist/tools/tool-builder/confirm-preview.js +37 -0
  738. package/dist/tools/tool-builder/confirm-preview.js.map +1 -0
  739. package/dist/tools/tool-builder/define.d.ts +6 -26
  740. package/dist/tools/tool-builder/define.d.ts.map +1 -1
  741. package/dist/tools/tool-builder/define.js +0 -25
  742. package/dist/tools/tool-builder/define.js.map +1 -1
  743. package/dist/tools/tool-builder/index.d.ts +3 -26
  744. package/dist/tools/tool-builder/index.d.ts.map +1 -1
  745. package/dist/tools/tool-builder/index.js +2 -25
  746. package/dist/tools/tool-builder/index.js.map +1 -1
  747. package/dist/tools/tool-builder/results.d.ts +4 -111
  748. package/dist/tools/tool-builder/results.d.ts.map +1 -1
  749. package/dist/tools/tool-builder/results.js +258 -250
  750. package/dist/tools/tool-builder/results.js.map +1 -1
  751. package/dist/tools/tool-builder/types.d.ts +5 -59
  752. package/dist/tools/tool-builder/types.d.ts.map +1 -1
  753. package/dist/tools/tool-builder/types.js +0 -7
  754. package/dist/tools/tool-builder/types.js.map +1 -1
  755. package/dist/tools/tool-builder.d.ts +0 -14
  756. package/dist/tools/tool-builder.d.ts.map +1 -1
  757. package/dist/tools/tool-builder.js +0 -14
  758. package/dist/tools/tool-builder.js.map +1 -1
  759. package/dist/uninstall-skill.d.ts +0 -41
  760. package/dist/uninstall-skill.d.ts.map +1 -1
  761. package/dist/uninstall-skill.js +0 -49
  762. package/dist/uninstall-skill.js.map +1 -1
  763. package/dist/uninstall.d.ts +0 -41
  764. package/dist/uninstall.d.ts.map +1 -1
  765. package/dist/uninstall.js +1 -29
  766. package/dist/uninstall.js.map +1 -1
  767. package/dist/update/update-check.d.ts +0 -26
  768. package/dist/update/update-check.d.ts.map +1 -1
  769. package/dist/update/update-check.js +0 -25
  770. package/dist/update/update-check.js.map +1 -1
  771. package/manifest.json +4 -5
  772. package/package.json +5 -5
  773. package/skills/yt-builder-mcp/SKILL.md +2958 -382
  774. package/dist/tools/elements/handlers-write.d.ts +0 -70
  775. package/dist/tools/elements/handlers-write.d.ts.map +0 -1
  776. package/dist/tools/elements/handlers-write.js +0 -700
  777. package/dist/tools/elements/handlers-write.js.map +0 -1
  778. package/dist/tools/elements/handlers.d.ts +0 -125
  779. package/dist/tools/elements/handlers.d.ts.map +0 -1
  780. package/dist/tools/elements/handlers.js +0 -571
  781. package/dist/tools/elements/handlers.js.map +0 -1
  782. package/dist/tools/elements/layout-hierarchy-rules.d.ts +0 -62
  783. package/dist/tools/elements/layout-hierarchy-rules.d.ts.map +0 -1
  784. package/dist/tools/elements/layout-hierarchy-rules.js +0 -154
  785. package/dist/tools/elements/layout-hierarchy-rules.js.map +0 -1
  786. package/dist/tools/elements/schema-validation.d.ts +0 -96
  787. package/dist/tools/elements/schema-validation.d.ts.map +0 -1
  788. package/dist/tools/elements/schema-validation.js +0 -249
  789. package/dist/tools/elements/schema-validation.js.map +0 -1
  790. package/dist/tools/elicitation.d.ts +0 -87
  791. package/dist/tools/elicitation.d.ts.map +0 -1
  792. package/dist/tools/elicitation.js +0 -100
  793. package/dist/tools/elicitation.js.map +0 -1
  794. package/dist/tools/format/elements-format.d.ts +0 -75
  795. package/dist/tools/format/elements-format.d.ts.map +0 -1
  796. package/dist/tools/format/elements-format.js +0 -324
  797. package/dist/tools/format/elements-format.js.map +0 -1
  798. package/dist/tools/format/source-schema-format.d.ts +0 -74
  799. package/dist/tools/format/source-schema-format.d.ts.map +0 -1
  800. package/dist/tools/format/source-schema-format.js +0 -208
  801. package/dist/tools/format/source-schema-format.js.map +0 -1
  802. package/dist/tools/layout-flatten.d.ts +0 -84
  803. package/dist/tools/layout-flatten.d.ts.map +0 -1
  804. package/dist/tools/layout-flatten.js +0 -129
  805. package/dist/tools/layout-flatten.js.map +0 -1
  806. package/dist/tools/local-content/handlers.d.ts +0 -54
  807. package/dist/tools/local-content/handlers.d.ts.map +0 -1
  808. package/dist/tools/local-content/handlers.js +0 -316
  809. package/dist/tools/local-content/handlers.js.map +0 -1
  810. package/dist/tools/multi-items/clean-implode-handler.d.ts +0 -37
  811. package/dist/tools/multi-items/clean-implode-handler.d.ts.map +0 -1
  812. package/dist/tools/multi-items/clean-implode-handler.js +0 -65
  813. package/dist/tools/multi-items/clean-implode-handler.js.map +0 -1
  814. package/dist/tools/multi-items/format/inspection-format.d.ts +0 -41
  815. package/dist/tools/multi-items/format/inspection-format.d.ts.map +0 -1
  816. package/dist/tools/multi-items/format/inspection-format.js +0 -64
  817. package/dist/tools/multi-items/format/inspection-format.js.map +0 -1
  818. package/dist/tools/multi-items/inspect-handler.d.ts +0 -97
  819. package/dist/tools/multi-items/inspect-handler.d.ts.map +0 -1
  820. package/dist/tools/multi-items/inspect-handler.js +0 -323
  821. package/dist/tools/multi-items/inspect-handler.js.map +0 -1
  822. package/dist/tools/multi-items/inspection-cache.d.ts +0 -85
  823. package/dist/tools/multi-items/inspection-cache.d.ts.map +0 -1
  824. package/dist/tools/multi-items/inspection-cache.js +0 -126
  825. package/dist/tools/multi-items/inspection-cache.js.map +0 -1
  826. package/dist/tools/pages/handlers-audit.d.ts +0 -129
  827. package/dist/tools/pages/handlers-audit.d.ts.map +0 -1
  828. package/dist/tools/pages/handlers-audit.js +0 -704
  829. package/dist/tools/pages/handlers-audit.js.map +0 -1
  830. package/dist/tools/pages/handlers-read.d.ts +0 -95
  831. package/dist/tools/pages/handlers-read.d.ts.map +0 -1
  832. package/dist/tools/pages/handlers-read.js +0 -929
  833. package/dist/tools/pages/handlers-read.js.map +0 -1
  834. package/dist/tools/pages/handlers-write.d.ts +0 -212
  835. package/dist/tools/pages/handlers-write.d.ts.map +0 -1
  836. package/dist/tools/pages/handlers-write.js +0 -418
  837. package/dist/tools/pages/handlers-write.js.map +0 -1
  838. package/dist/tools/pages/target.d.ts +0 -162
  839. package/dist/tools/pages/target.d.ts.map +0 -1
  840. package/dist/tools/pages/target.js +0 -239
  841. package/dist/tools/pages/target.js.map +0 -1
  842. package/dist/tools/sources/handlers-bind.d.ts +0 -148
  843. package/dist/tools/sources/handlers-bind.d.ts.map +0 -1
  844. package/dist/tools/sources/handlers-bind.js +0 -655
  845. package/dist/tools/sources/handlers-bind.js.map +0 -1
  846. package/dist/tools/sources/handlers.d.ts +0 -114
  847. package/dist/tools/sources/handlers.d.ts.map +0 -1
  848. package/dist/tools/sources/handlers.js +0 -285
  849. package/dist/tools/sources/handlers.js.map +0 -1
  850. package/dist/tools/sublayout/inspect-sublayout-handler.d.ts +0 -95
  851. package/dist/tools/sublayout/inspect-sublayout-handler.d.ts.map +0 -1
  852. package/dist/tools/sublayout/inspect-sublayout-handler.js +0 -181
  853. package/dist/tools/sublayout/inspect-sublayout-handler.js.map +0 -1
@@ -1,84 +1,12 @@
1
- /**
2
- * Layout Library tools — browse + fetch + insert + save the YOOtheme Pro
3
- * Library catalog (layouts / element presets), PLUS the Image Sourcing pair
4
- * (Task D1) that fills layout image slots with real photography. Task 2
5
- * (Layout Library, Spec #2) + Task 3 (Spec #3) + Task 6 (Spec #6) + Task 7
6
- * (Spec #7) + Task D1 (Image Sourcing).
7
- *
8
- * yootheme_builder_library_browse
9
- * → paginated, taxonomy-filtered catalog rows (tableResult — Wave G.2).
10
- * yootheme_builder_library_get
11
- * → a SINGLE item's full tree + derived section list (the natural next
12
- * call after library_browse — id comes straight off a browse row).
13
- * yootheme_builder_library_insert
14
- * → compose + persist a browsed/fetched item onto a page/template
15
- * through the REAL hardened write chain.
16
- * yootheme_builder_library_save_as
17
- * → the REVERSE direction: save a page/subtree INTO "My Layouts".
18
- * yootheme_builder_library_delete
19
- * → the INVERSE of library_save_as: remove ONE saved entry from the
20
- * isolated "WootsUp AI Layouts" store (source:"ai" only; the curated
21
- * catalog is read-only). ADVANCED, like library_save_as.
22
- * yootheme_builder_image_search
23
- * → search Unsplash / Pexels (real photography) or YOOtheme Pro Images
24
- * (curated decorative/background assets) — forwards to the SAME
25
- * `?key=<theme apikey>` proxy auth `library_browse` uses (Task A0-C1,
26
- * PHP `ImageSearchService`). Returns candidate rows; `total:0` means
27
- * no Library API key configured, mirroring browse's empty-catalog hint.
28
- * yootheme_builder_image_import
29
- * → sideload ONE chosen photo (typically a `full_url` from
30
- * image_search) into the site's own Media Library. Returns
31
- * `{imported:true, local_url}` to bind via element_update_settings,
32
- * or `{imported:false, reason}` on a caught failure (never a 500).
33
- *
34
- * browse/get/image_search are read-only forwards, exactly like the sibling
35
- * L1 read tools (sources_list / template_summary / pages_list): resolve the
36
- * site via the pool, GET the plugin REST endpoint, project into a table +
37
- * strict structuredContent, stamp the result-level `_meta` (site_id/
38
- * site_url/platform/resolved_via) via withSiteMeta. insert/save_as/
39
- * image_import are WRITE forwards (withWriteSiteRetry — pre-flights a KNOWN
40
- * read-only token before any body build, mirroring every other write tool's
41
- * U2 pattern).
42
- *
43
- * ── Tiering (Task 7, deliberate) ─────────────────────────────────────────
44
- * `library_save_as` is registered as an ADVANCED tool — it is NOT added to
45
- * `ESSENTIAL_TOOLS` (essentials.ts) and does NOT appear as a first-class
46
- * `tools/list` entry. `tools/list` sits close to the 28000B token-baseline
47
- * ceiling (see `tests/perf/token-baseline.test.ts`); a 4th Layout-Library L1
48
- * tool would risk busting it. `CapturingServer` (gateway/capturing-server.ts)
49
- * automatically captures ANY registered tool whose name is absent from both
50
- * `ESSENTIAL_TOOLS` and `DIRECT_TOP_LEVEL_TOOLS` into the L2 advanced
51
- * registry — reachable via `yootheme_builder_advanced({tool, arguments})` —
52
- * so simply NOT listing this name in essentials.ts is the entire tiering
53
- * decision (mirrors how `yootheme_builder_clean_implode_directives` /
54
- * `yootheme_builder_element_unbind_source` are advanced-only today). The
55
- * name falls through the gateway's domain taxonomy (domains.ts) to "misc"
56
- * (no `library` domain exists there) — intentional, NOT a bug (see
57
- * `domain-order.test.ts`'s own pinned "misc" fallthrough cases): it keeps the
58
- * gateway's own tools/list-visible description text from growing at all,
59
- * while `{tool: "yootheme_builder_library_save_as"}` discovery and
60
- * `{tool, arguments}` execution both remain fully reachable (the gateway
61
- * dispatches by an exact Map lookup, independent of the domain-grouped
62
- * DESCRIPTION text — see `gateway/advanced-tool/execute.ts`).
63
- *
64
- * @license MIT
65
- */
66
- import { tableResult } from '@getimo/mcp-toolkit';
67
1
  import { z } from 'zod';
68
2
  import { RestError } from '../errors.js';
69
3
  import { withSiteRetry, withWriteSiteRetry } from './pool-resolve-helper.js';
70
- import { SITE_ID_SCHEMA } from './shared-schemas.js';
4
+ import { applyMaxChars, MAX_CHARS } from './sparse-fields.js';
5
+ import { ETAG, INCLUDE_META_SCHEMA, SITE_ID_SCHEMA } from './shared-schemas.js';
71
6
  import { creating, defineTool, destructive, errorResult, jsonResult, mutating, readOnly, structuredResult, withSiteMeta, } from './tool-builder.js';
72
- // ─── outputSchema (Wave G.2 §4) ────────────────────────────────────
73
- /**
74
- * Fixed section-purpose enum (Task 8, Section Corpus Phase 1) — mirrors the
75
- * PHP-side `SectionPurposeClassifier` / `SectionIndexEntry` purpose values
76
- * byte-for-byte (design spec §A, Global Constraints). Only applies when
77
- * `kind="section"`. Declared here (ahead of `LIBRARY_ITEM_SCHEMA`, which
78
- * consumes it for the browse row's `purpose` field) rather than further
79
- * down where the original Task 8 pass left it, since Task 8b (this file)
80
- * now needs it at both the item-schema AND the browse-filter-schema sites.
81
- */
7
+ import { budgetedTableResult } from "./budgeted-table.js";
8
+ import { COMPACT_DEFAULT, RESPONSE_BUDGET } from "./response-budget.js";
9
+ import { CONFIRM_PREVIEW_FIELDS, HOST_PROMPT_PROMISE, requireOnExecutedWrite } from './tool-builder/confirm-preview.js';
82
10
  export const SECTION_PURPOSE_VALUES = [
83
11
  'hero',
84
12
  'features',
@@ -93,39 +21,10 @@ export const SECTION_PURPOSE_VALUES = [
93
21
  'logos',
94
22
  'content',
95
23
  ];
96
- /**
97
- * Track 1 Task 7 (WootsUp AI Layouts) — the `source` axis for
98
- * browse/get/insert: `"catalog"` (the remote YOOtheme Pro Library, the
99
- * DEFAULT) or `"ai"` (the plugin's OWN isolated "WootsUp AI Layouts" store,
100
- * saved via library_save_as, no apikey). Mirrors the PHP-side
101
- * `LibrarySourceRegistry` keys byte-for-byte.
102
- */
103
24
  const LIBRARY_SOURCE_VALUES = ['catalog', 'ai'];
104
- /**
105
- * The wire default for the `source` axis. It is NEVER forwarded (a catalog
106
- * browse/get/insert is byte-identical to the pre-Task-7 request — the PHP
107
- * controller reads the SAME absent-param default), so only `"ai"` ever
108
- * appears on the wire. Mirrors `LibrarySourceRegistry::DEFAULT_SOURCE`.
109
- */
110
25
  const DEFAULT_LIBRARY_SOURCE = 'catalog';
111
- /**
112
- * Track 1 Task 7 (WootsUp AI Layouts) — the `destination` axis for
113
- * library_save_as: `"ai"` (save INTO the isolated AI store, the DEFAULT) or
114
- * `"native"` (the escape hatch — save into YOOtheme's native "My Layouts").
115
- */
116
26
  const SAVE_DESTINATION_VALUES = ['ai', 'native'];
117
- /**
118
- * The wire default for the `destination` axis — the AI store. NEVER forwarded
119
- * (an ai-default save is byte-identical to the pre-Task-7 body; the PHP
120
- * `MyLayoutsStore` reads the SAME absent-param `ai` default), so only
121
- * `"native"` ever appears on the wire.
122
- */
123
27
  const DEFAULT_SAVE_DESTINATION = 'ai';
124
- /**
125
- * Mirrors `CatalogService::mapItem()` (PHP): every key is always PRESENT
126
- * (never omitted) but each value may be `null` when the raw catalog item
127
- * did not carry that taxonomy field — hence `.nullable()`, not `.optional()`.
128
- */
129
28
  const LIBRARY_ITEM_META_SCHEMA = z.object({
130
29
  topic: z.string().nullable(),
131
30
  type: z.string().nullable(),
@@ -133,23 +32,13 @@ const LIBRARY_ITEM_META_SCHEMA = z.object({
133
32
  website: z.string().nullable(),
134
33
  image: z.string().nullable(),
135
34
  });
136
- /**
137
- * Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — a `kind="section"` browse
138
- * row (`SectionCatalog::mapEntry()`, PHP) carries a DIFFERENT shape than a
139
- * layout/element row: no `meta` wrapper, TOP-LEVEL `topic`/`type`/`website`,
140
- * plus `layout_id`/`section_index`/`purpose`/`element_signature`/
141
- * `visual_effects`/`has_images`/`image_count`/`element_count`. All of these
142
- * are `.optional()` — additive, never present on a layout/element row — so a
143
- * strict-outputSchema host still accepts the pre-existing byte-unchanged
144
- * layout/element shape.
145
- */
146
35
  const LIBRARY_ITEM_SCHEMA = z.object({
147
36
  id: z.string(),
148
37
  name: z.string(),
149
- kind: z.enum(['layout', 'element', 'section']),
150
- meta: LIBRARY_ITEM_META_SCHEMA,
151
- thumbnail_url: z.string().nullable(),
152
- layout_id: z.string().optional(),
38
+ kind: z.enum(['layout', 'element', 'section']).optional(),
39
+ meta: LIBRARY_ITEM_META_SCHEMA.optional(),
40
+ thumbnail_url: z.string().nullable().optional(),
41
+ library_layout_id: z.string().optional(),
153
42
  section_index: z.number().optional(),
154
43
  purpose: z.enum(SECTION_PURPOSE_VALUES).optional(),
155
44
  element_signature: z.string().optional(),
@@ -159,24 +48,21 @@ const LIBRARY_ITEM_SCHEMA = z.object({
159
48
  website: z.string().nullable().optional(),
160
49
  has_images: z.boolean().optional(),
161
50
  image_count: z.number().optional(),
162
- // Close-out finding (f): CONTENT elements only — excludes structural
163
- // section/row/column wrappers. Distinct from library_get's
164
- // `sections[].element_count` (LIBRARY_SECTION_SCHEMA below), which
165
- // counts ALL descendants including structural nodes — the same
166
- // section reports a different count from each; do not assume parity.
167
- element_count: z
51
+ content_element_count: z
168
52
  .number()
169
53
  .optional()
170
- .describe('CONTENT elements only (excludes structural section/row/column wrappers). ' +
171
- 'Distinct from library_get\'s sections[].element_count, which counts ALL descendants.'),
54
+ .describe('CONTENT elements in this section — the section/row/column wrappers are NOT counted. ' +
55
+ 'library_get\'s sections[].element_count and library_insert\'s element_count count ' +
56
+ 'every node including those wrappers, so the same section reads higher there.'),
172
57
  });
173
58
  export const LIBRARY_BROWSE_OUTPUT_SCHEMA = z.object({
174
59
  items: z.array(LIBRARY_ITEM_SCHEMA),
175
60
  total: z.number(),
176
- // `CatalogService::browse()` always emits the key; `null` means "last page".
177
61
  next_cursor: z.string().nullable(),
62
+ api_key_configured: z.boolean().optional(),
63
+ images_note: z.string().optional(),
64
+ detail_note: z.string().optional(),
178
65
  });
179
- // ─── library_get (Task 3) ───────────────────────────────────────────
180
66
  const LIBRARY_SECTION_SCHEMA = z.object({
181
67
  index: z.number(),
182
68
  name: z.string(),
@@ -185,28 +71,14 @@ const LIBRARY_SECTION_SCHEMA = z.object({
185
71
  });
186
72
  export const LIBRARY_GET_OUTPUT_SCHEMA = z.object({
187
73
  id: z.string(),
188
- // Task 8b (Section Corpus Phase 1, PLAN-GAP fix): "section" is a valid
189
- // resolved kind — `LibraryController::getSection()` (PHP) always
190
- // returns a literal `kind: "section"` for a composite-id fetch.
191
74
  kind: z.enum(['layout', 'element', 'section']),
192
75
  name: z.string(),
193
76
  version: z.string(),
194
- // The raw fetched tree (`{type, children, version, images, ...}` for a
195
- // layout; a single element node for a preset) — an arbitrary JSON
196
- // object, not a fixed shape.
197
77
  tree: z.record(z.string(), z.unknown()),
198
- // Top-level `tree.children` (layouts only — always `[]` for a preset,
199
- // even when its own tree happens to carry `children`).
200
78
  sections: z.array(LIBRARY_SECTION_SCHEMA),
201
79
  images_manifest_count: z.number(),
80
+ element_count_note: z.string().optional(),
202
81
  });
203
- // ─── library_insert (Task 6) ─────────────────────────────────────────
204
- /**
205
- * Mirrors `LibraryInsertService::insert()`'s `$target` union (PHP) exactly:
206
- * a fresh page, a splice into an EXISTING template/page (layout/section on
207
- * the layout root, or a preset on a Column), or a splice into a
208
- * sublayout/fragment element.
209
- */
210
82
  const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
211
83
  z.object({
212
84
  new_page: z.object({
@@ -241,33 +113,49 @@ const LIBRARY_INSERT_TARGET_SCHEMA = z.union([
241
113
  'or a Column for an element preset (kind:"element").'),
242
114
  }),
243
115
  ]);
244
- /**
245
- * A DOWNGRADED UI-invariant violation (2026-07-12 fix, "trusted
246
- * library-insert downgrades enum/type violations to warnings"): a pure
247
- * value-shape mismatch (`invalid_enum_value` / `type_mismatch`) that
248
- * YOOtheme's OWN factory content carried but the site's installed YOOtheme
249
- * schema doesn't declare — a version-skew artifact, allowed through rather
250
- * than hard-rejecting the whole insert. Every OTHER violation class still
251
- * 422s (`library.ui_invariant_violation`) exactly as before.
252
- */
253
116
  const LIBRARY_INSERT_WARNING_SCHEMA = z.object({
254
117
  field: z.string(),
255
118
  code: z.string(),
256
119
  message: z.string(),
257
120
  });
258
- export const LIBRARY_INSERT_OUTPUT_SCHEMA = z.object({
259
- template_id: z.string(),
260
- inserted_path: z.string(),
261
- element_count: z.number(),
262
- images_mode: z.enum(['placeholder', 'curated']),
263
- images_resolved: z.object({
121
+ const LIBRARY_INSERT_IMAGE_FAILURE_SCHEMA = z.object({
122
+ prop_path: z.string(),
123
+ exception_class: z.string(),
124
+ message: z.string(),
125
+ });
126
+ export const LIBRARY_INSERT_OUTPUT_SCHEMA = z
127
+ .object({
128
+ layout_id: z.string(),
129
+ inserted_path: z.string().optional(),
130
+ inserted_paths: z.array(z.string()).optional(),
131
+ element_count: z.number().optional(),
132
+ images_mode: z.enum(['placeholder', 'curated', 'as_saved']).optional(),
133
+ images_resolved: z
134
+ .object({
264
135
  downloaded: z.number(),
265
136
  placeholders: z.number(),
266
- }),
267
- new_etag: z.string(),
268
- frontend_url: z.string().nullable(),
137
+ })
138
+ .optional(),
139
+ etag: z.string().optional(),
140
+ new_etag: z.string().optional(),
141
+ frontend_url: z.string().nullable().optional(),
142
+ images_note: z.string().optional(),
143
+ media_note: z.string().optional(),
269
144
  warnings: z.array(LIBRARY_INSERT_WARNING_SCHEMA).optional(),
270
- });
145
+ image_import_failures: z.array(LIBRARY_INSERT_IMAGE_FAILURE_SCHEMA).optional(),
146
+ root_type_inserted: z.string().optional(),
147
+ would_remove_count: z.number().optional(),
148
+ would_remove_types: z.array(z.string()).optional(),
149
+ ...CONFIRM_PREVIEW_FIELDS,
150
+ })
151
+ .superRefine((v, ctx) => requireOnExecutedWrite(v, ctx, [
152
+ 'inserted_path',
153
+ 'element_count',
154
+ 'images_mode',
155
+ 'images_resolved',
156
+ 'new_etag',
157
+ 'frontend_url',
158
+ ]));
271
159
  const LIBRARY_TABLE_COLUMNS = [
272
160
  { key: 'id', label: 'ID', width: 10, llmOnly: true },
273
161
  { key: 'name', label: 'NAME', width: 26 },
@@ -282,17 +170,6 @@ function asNullableString(v) {
282
170
  function isSectionPurpose(v) {
283
171
  return typeof v === 'string' && SECTION_PURPOSE_VALUES.includes(v);
284
172
  }
285
- /**
286
- * Task 8b (Section Corpus Phase 1, PLAN-GAP fix) — `requestedKind` is the
287
- * caller's own `library_browse({kind})` argument. It is needed because a
288
- * `kind="section"` row from `SectionCatalog::mapEntry()` (PHP) carries NO
289
- * per-row `kind` field at all (unlike layout/element rows, which always do)
290
- * — the whole response is homogeneous per browse call, since `kind` is a
291
- * REST-level filter. So: `r.kind` wins when the row DOES carry one
292
- * (layout/element, and defensively "section" too, should REST ever add it);
293
- * otherwise fall back to what the caller asked for; otherwise "layout"
294
- * (today's pre-Task-8b default, byte-unchanged).
295
- */
296
173
  function mapLibraryItem(raw, requestedKind) {
297
174
  if (raw === null || typeof raw !== 'object')
298
175
  return undefined;
@@ -312,15 +189,11 @@ function mapLibraryItem(raw, requestedKind) {
312
189
  },
313
190
  thumbnail_url: asNullableString(r.thumbnail_url),
314
191
  };
315
- // ADDITIVE ONLY: a layout/element row returns exactly `base` — no
316
- // section keys are ever added, not even as `undefined` — so the
317
- // pre-Task-8b shape stays byte-unchanged (regression-tested via
318
- // toStrictEqual in library.test.ts).
319
192
  if (kind !== 'section')
320
193
  return base;
321
194
  return {
322
195
  ...base,
323
- layout_id: typeof r.layout_id === 'string' ? r.layout_id : undefined,
196
+ library_layout_id: typeof r.library_layout_id === 'string' ? r.library_layout_id : undefined,
324
197
  section_index: typeof r.section_index === 'number' ? r.section_index : undefined,
325
198
  purpose: isSectionPurpose(r.purpose) ? r.purpose : undefined,
326
199
  element_signature: typeof r.element_signature === 'string' ? r.element_signature : undefined,
@@ -332,41 +205,17 @@ function mapLibraryItem(raw, requestedKind) {
332
205
  website: asNullableString(r.website),
333
206
  has_images: typeof r.has_images === 'boolean' ? r.has_images : undefined,
334
207
  image_count: typeof r.image_count === 'number' ? r.image_count : undefined,
335
- element_count: typeof r.element_count === 'number' ? r.element_count : undefined,
208
+ content_element_count: typeof r.content_element_count === 'number' ? r.content_element_count : undefined,
336
209
  };
337
210
  }
338
- /**
339
- * PAGES_LIBRARY_MEDIA-10 the Layout Library + Image Sourcing domain is
340
- * WordPress-only (the `src/modules/library` host module bootstraps on WP only;
341
- * plugin 1.10.0). On a Joomla site the REST endpoints do not exist, so a
342
- * library/image call used to return an empty `{items:[],total:0}` (or a bare
343
- * 404) with no hint about WHY. This returns an explicit
344
- * `not_supported_on_platform` (501) instead, so the agent stops retrying and
345
- * knows the capability simply is not there on this platform. Returns `null` on
346
- * WordPress so the real forward runs.
347
- */
348
- function joomlaNotSupportedGuard(client, toolName, context) {
349
- if (client.getPlatform().kind !== 'joomla')
350
- return null;
351
- return errorResult({
352
- error: new Error(`${toolName} is not supported on Joomla — the Library + image domain is ` +
353
- 'WordPress-only in plugin 1.10.0.'),
354
- context: { ...context, platform: 'joomla', error_code: 'not_supported_on_platform', status: 501 },
355
- hint: 'This capability exists only on WordPress sites. On Joomla, build layouts with ' +
356
- 'the element/page tools; there is no Library catalog or image proxy to browse.',
357
- });
358
- }
359
- export async function handleLibraryBrowse({ client }, { kind, source, topic, type, role, website, element_type, purpose, has_effects, query, limit, cursor }) {
360
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_browse', { kind: kind ?? 'layout' });
361
- if (guard !== null)
362
- return guard;
211
+ export const BROWSE_IMAGES_NOTE = 'Images: an insert uses generic PLACEHOLDERS by default '
212
+ + '- has_images describes the catalog entry, not the inserted page. Pass '
213
+ + 'images:"curated" to yootheme_builder_library_insert to download the real photos.';
214
+ export async function handleLibraryBrowse({ client }, { kind, source, topic, type, role, website, element_type, purpose, has_effects, query, limit, cursor, include_meta }) {
363
215
  try {
364
216
  const qs = new URLSearchParams();
365
217
  if (kind !== undefined)
366
218
  qs.set('kind', kind);
367
- // Only forward a NON-default source (the PHP controller reads the same
368
- // absent-param `catalog` default) so a catalog browse is byte-identical
369
- // to the pre-Task-7 request.
370
219
  if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
371
220
  qs.set('source', source);
372
221
  if (typeof topic === 'string' && topic !== '')
@@ -397,25 +246,51 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
397
246
  .filter((i) => i !== undefined);
398
247
  const total = typeof data.total === 'number' ? data.total : items.length;
399
248
  const nextCursor = typeof data.next_cursor === 'string' ? data.next_cursor : null;
249
+ const apiKeyConfigured = typeof data.api_key_configured === 'boolean' ? data.api_key_configured : undefined;
250
+ const detailNote = typeof data.detail_note === 'string' && data.detail_note !== '' ? data.detail_note : undefined;
400
251
  const rows = items;
401
- const toolkitResult = tableResult(rows, {
252
+ const toolkitResult = budgetedTableResult(rows, {
402
253
  columns: [...LIBRARY_TABLE_COLUMNS],
403
254
  header: (count) => `${String(count)} of ${String(total)} library items`,
404
- footer: 'Activate one on a page with yootheme_builder_element_add / page_dynamize, ' +
405
- 'or pass `cursor` (next_cursor) for the next page.',
406
- });
407
- // HARD CONSTRAINT (Task 8): browse NEVER returns an image content
408
- // block, even for kind="section" rows that carry a thumbnail_url —
409
- // tableResult() only ever emits text rows. Use
410
- // yootheme_builder_library_get's include_thumbnail on a shortlisted
411
- // id for an actual image.
255
+ footer: 'Activate one on a page with yootheme_builder_element_add / page_generate_dynamic, ' +
256
+ 'or pass `cursor` (next_cursor) for the next page. ' +
257
+ BROWSE_IMAGES_NOTE,
258
+ includeMeta: include_meta,
259
+ }, RESPONSE_BUDGET.BULKY);
412
260
  return structuredResult(toolkitResult, {
413
261
  items,
414
262
  total,
415
263
  next_cursor: nextCursor,
264
+ images_note: BROWSE_IMAGES_NOTE,
265
+ ...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
266
+ ...(detailNote !== undefined ? { detail_note: detailNote } : {}),
416
267
  });
417
268
  }
418
269
  catch (e) {
270
+ if (isLibraryNotConfigured(e)) {
271
+ return errorResult({
272
+ error: e,
273
+ context: {
274
+ kind: kind ?? 'layout',
275
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
276
+ error_code: LIBRARY_NOT_CONFIGURED,
277
+ ...libraryUnavailableContext(e),
278
+ },
279
+ hint: LIBRARY_NOT_CONFIGURED_HINT,
280
+ });
281
+ }
282
+ if (isLibraryUpstreamUnavailable(e)) {
283
+ return errorResult({
284
+ error: e,
285
+ context: {
286
+ kind: kind ?? 'layout',
287
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
288
+ error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
289
+ ...libraryUnavailableContext(e),
290
+ },
291
+ hint: LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
292
+ });
293
+ }
419
294
  return errorResult({
420
295
  error: e,
421
296
  context: { kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, topic, type, role, website, element_type, purpose, has_effects, query },
@@ -423,6 +298,69 @@ export async function handleLibraryBrowse({ client }, { kind, source, topic, typ
423
298
  });
424
299
  }
425
300
  }
301
+ const LIBRARY_UPSTREAM_UNAVAILABLE = 'library_upstream_unavailable';
302
+ export const LIBRARY_UPSTREAM_UNAVAILABLE_HINT = 'This is a CATALOG FETCH FAILURE, not an empty catalog. Do NOT retry with different '
303
+ + 'filters or a different id; the YOOtheme Pro Library never answered with data. An HTTP 401 '
304
+ + 'here means api.yootheme.com REFUSED the Library API key this site has configured. WHY it '
305
+ + 'was refused is not visible from here, so do NOT name a cause: report that the key was '
306
+ + 'refused and let the site owner check it with YOOtheme. The keyless source:"ai" store and '
307
+ + 'yootheme_builder_page_get_layout still work.';
308
+ function restErrorBody(e) {
309
+ if (!(e instanceof RestError))
310
+ return undefined;
311
+ const body = e.body;
312
+ if (body === null || typeof body !== 'object' || Array.isArray(body))
313
+ return undefined;
314
+ return body;
315
+ }
316
+ const LIBRARY_NOT_CONFIGURED = 'library_api_key_not_configured';
317
+ export const LIBRARY_NOT_CONFIGURED_HINT = 'This is a CONFIGURATION GAP, not an empty catalog and not a refusal: no request reached the '
318
+ + 'upstream catalog, so nothing here tells you what it holds. Do NOT retry with different '
319
+ + 'filters, and do NOT assume the customer forgot to enter a key. Run yootheme_builder_diagnose '
320
+ + 'and read library_key.state first: it separates "no key entered", which the customer fixes in '
321
+ + 'the YOOtheme Customizer theme settings, from a lookup that found no YOOtheme update site at '
322
+ + 'all, where there is nothing to fix in the key field. Its hint names who has to act. Without a '
323
+ + 'key you can still BROWSE the bundled section index (library_browse with kind:"section", '
324
+ + 'including its filters), read the keyless source:"ai" store, and use '
325
+ + 'yootheme_builder_page_get_layout. What the key unlocks is kind:"layout"/"element" browsing '
326
+ + 'plus library_get detail and library_insert for every kind.';
327
+ function isLibraryUpstreamUnavailable(e) {
328
+ return libraryCodeMatches(e, LIBRARY_UPSTREAM_UNAVAILABLE);
329
+ }
330
+ function isLibraryNotConfigured(e) {
331
+ return libraryCodeMatches(e, LIBRARY_NOT_CONFIGURED);
332
+ }
333
+ function libraryCodeMatches(e, code) {
334
+ if (e instanceof RestError && typeof e.code === 'string' && e.code.endsWith(code)) {
335
+ return true;
336
+ }
337
+ const body = restErrorBody(e);
338
+ if (body === undefined)
339
+ return false;
340
+ if (body.error_code === code)
341
+ return true;
342
+ const data = body.data;
343
+ return (data !== null
344
+ && typeof data === 'object'
345
+ && !Array.isArray(data)
346
+ && data.error_code === code);
347
+ }
348
+ function libraryUnavailableContext(e) {
349
+ const body = restErrorBody(e);
350
+ if (body === undefined)
351
+ return {};
352
+ const data = body.data !== null && typeof body.data === 'object' && !Array.isArray(body.data)
353
+ ? body.data
354
+ : {};
355
+ const out = {};
356
+ const upstream = body.upstream_status ?? data.upstream_status;
357
+ if (typeof upstream === 'number')
358
+ out.upstream_status = upstream;
359
+ const configured = body.api_key_configured ?? data.api_key_configured;
360
+ if (typeof configured === 'boolean')
361
+ out.api_key_configured = configured;
362
+ return out;
363
+ }
426
364
  function mapLibrarySection(raw) {
427
365
  if (raw === null || typeof raw !== 'object')
428
366
  return undefined;
@@ -435,14 +373,6 @@ function mapLibrarySection(raw) {
435
373
  }
436
374
  return { index: r.index, name: r.name, path: r.path, element_count: r.element_count };
437
375
  }
438
- /**
439
- * Task 8 (Section Corpus Phase 1) — the REST `/library/get` response's
440
- * `thumbnail` field (present ONLY when `include_thumbnail=true` was passed
441
- * AND the server-side fetch succeeded — see `LibraryController::attachThumbnail()`).
442
- * A missing/malformed field (either key absent, or either value not a
443
- * non-empty string) is treated as "no thumbnail" — the caller never emits a
444
- * broken image block.
445
- */
446
376
  function mapThumbnail(raw) {
447
377
  if (raw === null || typeof raw !== 'object')
448
378
  return undefined;
@@ -453,15 +383,76 @@ function mapThumbnail(raw) {
453
383
  return undefined;
454
384
  return { mime_type: r.mime_type, data_base64: r.data_base64 };
455
385
  }
456
- export async function handleLibraryGet({ client }, { id, kind, source, include_thumbnail }) {
457
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_get', { id });
458
- if (guard !== null)
459
- return guard;
386
+ function asPlainRecord(value) {
387
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
388
+ ? value
389
+ : {};
390
+ }
391
+ function collectElementTypeCounts(node, counts) {
392
+ const n = asPlainRecord(node);
393
+ if (typeof n.type === 'string' && n.type !== '') {
394
+ counts.set(n.type, (counts.get(n.type) ?? 0) + 1);
395
+ }
396
+ if (Array.isArray(n.children)) {
397
+ for (const child of n.children)
398
+ collectElementTypeCounts(child, counts);
399
+ }
400
+ }
401
+ function elementTypeCounts(tree) {
402
+ const counts = new Map();
403
+ collectElementTypeCounts(tree, counts);
404
+ return Object.fromEntries([...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])));
405
+ }
406
+ function encodedLength(payload) {
407
+ return JSON.stringify(payload).length;
408
+ }
409
+ function truncatedHint(sectionCount) {
410
+ const sectionAdvice = sectionCount > 0
411
+ ? ' To inspect ONE section instead of the whole item, call this tool again with ' +
412
+ 'kind:"section" and an id of "<this id>#<sectionIndex>" (see sections[].index) — a ' +
413
+ 'single section is a fraction of the full size.'
414
+ : '';
415
+ const whatSurvived = sectionCount > 0
416
+ ? ' What is missing is the ELEMENTS inside the sections; the `sections` list beside ' +
417
+ 'this note is complete and unabridged (every section, its name and element count).'
418
+ : '';
419
+ return ('The full tree was replaced by this summary because the payload was too large.' +
420
+ whatSurvived +
421
+ ' To INSERT this item you do not need the tree at all — call ' +
422
+ 'yootheme_builder_library_insert with the same id directly.' +
423
+ sectionAdvice +
424
+ ' Passing a larger max_chars recovers more of the tree.');
425
+ }
426
+ const LIBRARY_GET_DEFAULT_STRUCTURED_BUDGET_CHARS = 20_000;
427
+ function shrinkLibraryGetTree(payload, budget) {
428
+ const bytesFull = encodedLength(payload);
429
+ const tree = asPlainRecord(payload.tree);
430
+ if (bytesFull <= budget)
431
+ return tree;
432
+ const rootType = typeof tree.type === 'string' ? tree.type : undefined;
433
+ const sections = Array.isArray(payload.sections) ? payload.sections : [];
434
+ const counts = elementTypeCounts(tree);
435
+ const summaryTree = {
436
+ ...(rootType !== undefined ? { type: rootType } : {}),
437
+ truncated: true,
438
+ bytes_full: bytesFull,
439
+ element_type_counts: counts,
440
+ hint: truncatedHint(sections.length),
441
+ };
442
+ if (encodedLength({ ...payload, tree: summaryTree }) <= budget)
443
+ return summaryTree;
444
+ const top = Object.fromEntries(Object.entries(counts).slice(0, 5));
445
+ return {
446
+ ...summaryTree,
447
+ element_type_counts: top,
448
+ element_type_counts_truncated: Object.keys(counts).length > Object.keys(top).length,
449
+ };
450
+ }
451
+ export async function handleLibraryGet({ client }, { id, kind, source, include_thumbnail, max_chars }) {
460
452
  try {
461
453
  const qs = new URLSearchParams({ id });
462
454
  if (kind !== undefined)
463
455
  qs.set('kind', kind);
464
- // Only forward a NON-default source (see handleLibraryBrowse).
465
456
  if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
466
457
  qs.set('source', source);
467
458
  if (include_thumbnail === true)
@@ -484,24 +475,58 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
484
475
  sections,
485
476
  images_manifest_count: typeof data.images_manifest_count === 'number' ? data.images_manifest_count : 0,
486
477
  };
487
- // Task 8: an image content block is ADDITIONAL to the JSON text
488
- // block, never a replacement — `structuredContent` (below) stays
489
- // exactly the pre-Task-8 shape so a strict-outputSchema host never
490
- // sees an undeclared key, and a large base64 blob is never
491
- // duplicated into structuredContent alongside the image block.
492
- // Gated on the CALLER's own `include_thumbnail === true` (not just
493
- // "the REST body happened to carry a thumbnail field") — a defensive
494
- // belt-and-braces on top of the server-side opt-in gate, so a future
495
- // REST regression can never surprise-inject an unrequested,
496
- // token-costly image block.
478
+ const structuredBudget = max_chars ?? LIBRARY_GET_DEFAULT_STRUCTURED_BUDGET_CHARS;
479
+ const finalTree = shrinkLibraryGetTree(payload, structuredBudget);
480
+ const structuredPayload = {
481
+ id: payload.id,
482
+ kind: payload.kind,
483
+ name: payload.name,
484
+ version: payload.version,
485
+ tree: finalTree,
486
+ sections: payload.sections,
487
+ images_manifest_count: payload.images_manifest_count,
488
+ };
497
489
  const thumbnail = include_thumbnail === true ? mapThumbnail(data.thumbnail) : undefined;
498
- const jsonEnvelope = jsonResult(payload);
490
+ const jsonEnvelope = jsonResult(structuredPayload);
499
491
  const content = thumbnail !== undefined
500
492
  ? [...jsonEnvelope.content, { type: 'image', data: thumbnail.data_base64, mimeType: thumbnail.mime_type }]
501
493
  : jsonEnvelope.content;
502
- return structuredResult({ content }, payload);
494
+ return structuredResult(applyMaxChars({ content }, max_chars), structuredPayload);
503
495
  }
504
496
  catch (e) {
497
+ if (isLibraryNotConfigured(e)) {
498
+ return errorResult({
499
+ error: e,
500
+ context: {
501
+ id,
502
+ kind: kind ?? 'layout',
503
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
504
+ error_code: LIBRARY_NOT_CONFIGURED,
505
+ ...libraryUnavailableContext(e),
506
+ },
507
+ hint: LIBRARY_NOT_CONFIGURED_HINT,
508
+ });
509
+ }
510
+ if (isLibraryUpstreamUnavailable(e)) {
511
+ const usedDefaultSource = source === undefined || source === DEFAULT_LIBRARY_SOURCE;
512
+ return errorResult({
513
+ error: e,
514
+ context: {
515
+ id,
516
+ kind: kind ?? 'layout',
517
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
518
+ error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
519
+ ...libraryUnavailableContext(e),
520
+ },
521
+ hint: usedDefaultSource
522
+ ? LIBRARY_UPSTREAM_UNAVAILABLE_HINT
523
+ + ' If this id came from your OWN store (library_save_as, or '
524
+ + 'library_browse with source:"ai"), it does not live in the remote '
525
+ + 'catalog at all: retry with source:"ai". The catalog was queried '
526
+ + 'because `source` defaults to "catalog".'
527
+ : LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
528
+ });
529
+ }
505
530
  return errorResult({
506
531
  error: e,
507
532
  context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE },
@@ -510,15 +535,6 @@ export async function handleLibraryGet({ client }, { id, kind, source, include_t
510
535
  });
511
536
  }
512
537
  }
513
- /**
514
- * 2026-07-12 fix ("trusted library-insert downgrades enum/type violations to
515
- * warnings") — maps the REST `warnings[]` array (each `{field, code,
516
- * message}`) if present. Mirrors the OTHER `map*` helpers in this file:
517
- * malformed entries are dropped rather than propagated, and an absent/empty
518
- * source array maps to `undefined` (the key is OMITTED from the result, not
519
- * emitted as `[]`) — this is the "if present" contract: pre-fix inserts
520
- * (a REST body carrying no `warnings` key at all) round-trip byte-unchanged.
521
- */
522
538
  function mapLibraryInsertWarnings(raw) {
523
539
  if (!Array.isArray(raw) || raw.length === 0)
524
540
  return undefined;
@@ -533,55 +549,148 @@ function mapLibraryInsertWarnings(raw) {
533
549
  }
534
550
  return warnings.length > 0 ? warnings : undefined;
535
551
  }
552
+ function mapLibraryInsertImageFailures(raw) {
553
+ if (!Array.isArray(raw) || raw.length === 0)
554
+ return undefined;
555
+ const failures = [];
556
+ for (const entry of raw) {
557
+ if (entry === null || typeof entry !== 'object')
558
+ continue;
559
+ const f = entry;
560
+ if (typeof f.prop_path !== 'string' || typeof f.exception_class !== 'string')
561
+ continue;
562
+ if (typeof f.message !== 'string')
563
+ continue;
564
+ failures.push({
565
+ prop_path: f.prop_path,
566
+ exception_class: f.exception_class,
567
+ message: f.message,
568
+ });
569
+ }
570
+ return failures.length > 0 ? failures : undefined;
571
+ }
572
+ function isLibraryInsertPreview(raw) {
573
+ return raw !== null && typeof raw === 'object' && raw.preview === true;
574
+ }
575
+ function mapLibraryInsertPreview(r) {
576
+ const types = Array.isArray(r.would_remove_types)
577
+ ? r.would_remove_types.filter((t) => typeof t === 'string')
578
+ : undefined;
579
+ return {
580
+ preview: true,
581
+ applied: false,
582
+ requires_confirmation: true,
583
+ requires_confirm: true,
584
+ operation: typeof r.operation === 'string' ? r.operation : 'insert this Library item',
585
+ warning: typeof r.warning === 'string' ? r.warning : '',
586
+ instruction: typeof r.instruction === 'string'
587
+ ? r.instruction
588
+ : 'Ask the user to confirm, then call again with `confirm: true`.',
589
+ hint: typeof r.hint === 'string' ? r.hint : '',
590
+ received_confirm: r.received_confirm,
591
+ received_confirm_type: typeof r.received_confirm_type === 'string' ? r.received_confirm_type : 'absent',
592
+ expected_confirm: typeof r.expected_confirm === 'string' ? r.expected_confirm : '',
593
+ ...(typeof r.step_reversible === 'boolean' ? { step_reversible: r.step_reversible } : {}),
594
+ layout_id: typeof r.layout_id === 'string' ? r.layout_id : '',
595
+ ...(typeof r.etag === 'string' && r.etag !== '' ? { etag: r.etag } : {}),
596
+ ...(typeof r.would_remove_count === 'number' ? { would_remove_count: r.would_remove_count } : {}),
597
+ ...(types !== undefined ? { would_remove_types: types } : {}),
598
+ };
599
+ }
536
600
  function mapLibraryInsertResult(raw) {
537
601
  if (raw === null || typeof raw !== 'object')
538
602
  return undefined;
539
603
  const r = raw;
540
- if (typeof r.template_id !== 'string' || typeof r.inserted_path !== 'string')
604
+ if (typeof r.layout_id !== 'string' || typeof r.inserted_path !== 'string')
541
605
  return undefined;
542
606
  const resolvedRaw = r.images_resolved !== null && typeof r.images_resolved === 'object'
543
607
  ? r.images_resolved
544
608
  : {};
545
609
  const warnings = mapLibraryInsertWarnings(r.warnings);
610
+ const imageFailures = mapLibraryInsertImageFailures(r.image_import_failures);
611
+ const mode = r.images_mode === 'curated' || r.images_mode === 'as_saved' ? r.images_mode : 'placeholder';
612
+ const placeholders = typeof resolvedRaw.placeholders === 'number' ? resolvedRaw.placeholders : 0;
613
+ const imagesNote = mode === 'placeholder' && placeholders > 0
614
+ ? `${String(placeholders)} image(s) are generic placeholders, not the photos shown on `
615
+ + 'the catalog thumbnail. The layout renders, but it does not look like its preview. '
616
+ + 'Re-insert with images:"curated" to download the real images into the Media Library.'
617
+ : undefined;
546
618
  return {
547
- template_id: r.template_id,
619
+ layout_id: r.layout_id,
548
620
  inserted_path: r.inserted_path,
549
621
  element_count: typeof r.element_count === 'number' ? r.element_count : 0,
550
- images_mode: r.images_mode === 'curated' ? 'curated' : 'placeholder',
622
+ images_mode: mode,
551
623
  images_resolved: {
552
624
  downloaded: typeof resolvedRaw.downloaded === 'number' ? resolvedRaw.downloaded : 0,
553
- placeholders: typeof resolvedRaw.placeholders === 'number' ? resolvedRaw.placeholders : 0,
625
+ placeholders,
554
626
  },
555
627
  new_etag: typeof r.new_etag === 'string' ? r.new_etag : '',
556
628
  frontend_url: typeof r.frontend_url === 'string' ? r.frontend_url : null,
629
+ ...(imagesNote !== undefined ? { images_note: imagesNote } : {}),
630
+ ...(typeof r.media_note === 'string' && r.media_note !== '' ? { media_note: r.media_note } : {}),
557
631
  ...(warnings !== undefined ? { warnings } : {}),
632
+ ...(imageFailures !== undefined ? { image_import_failures: imageFailures } : {}),
633
+ root_type_inserted: typeof r.root_type_inserted === 'string' ? r.root_type_inserted : '',
558
634
  };
559
635
  }
560
- export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, etag }) {
561
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_insert', { id });
562
- if (guard !== null)
563
- return guard;
636
+ export async function handleLibraryInsert({ client }, { id, kind, source, target, section_index, images, insert_mode, index, confirm, etag }) {
564
637
  const resolvedImages = images ?? 'placeholder';
565
638
  try {
566
639
  const body = { id, target, images: resolvedImages };
567
640
  if (kind !== undefined)
568
641
  body.kind = kind;
569
- // Only forward a NON-default source (see handleLibraryBrowse); the PHP
570
- // LibraryInsertService reads the same absent-param `catalog` default.
571
642
  if (source !== undefined && source !== DEFAULT_LIBRARY_SOURCE)
572
643
  body.source = source;
573
644
  if (section_index !== undefined)
574
645
  body.section_index = section_index;
646
+ if (insert_mode !== undefined)
647
+ body.insert_mode = insert_mode;
648
+ if (index !== undefined)
649
+ body.index = index;
650
+ if (confirm !== undefined)
651
+ body.confirm = confirm;
575
652
  if (typeof etag === 'string' && etag !== '')
576
653
  body.etag = etag;
577
654
  const data = await client.post('/library/insert', { body });
655
+ if (isLibraryInsertPreview(data)) {
656
+ const preview = mapLibraryInsertPreview(data);
657
+ return structuredResult(jsonResult(preview), preview);
658
+ }
578
659
  const mapped = mapLibraryInsertResult(data);
579
660
  if (mapped === undefined) {
580
- throw new Error('Malformed /library/insert response — missing template_id/inserted_path.');
661
+ throw new Error('Malformed /library/insert response — missing layout_id/inserted_path.');
581
662
  }
582
663
  return structuredResult(jsonResult(mapped), mapped);
583
664
  }
584
665
  catch (e) {
666
+ if (isLibraryNotConfigured(e)) {
667
+ return errorResult({
668
+ error: e,
669
+ context: {
670
+ id,
671
+ kind: kind ?? 'layout',
672
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
673
+ images: resolvedImages,
674
+ error_code: LIBRARY_NOT_CONFIGURED,
675
+ ...libraryUnavailableContext(e),
676
+ },
677
+ hint: LIBRARY_NOT_CONFIGURED_HINT,
678
+ });
679
+ }
680
+ if (isLibraryUpstreamUnavailable(e)) {
681
+ return errorResult({
682
+ error: e,
683
+ context: {
684
+ id,
685
+ kind: kind ?? 'layout',
686
+ source: source ?? DEFAULT_LIBRARY_SOURCE,
687
+ images: resolvedImages,
688
+ error_code: LIBRARY_UPSTREAM_UNAVAILABLE,
689
+ ...libraryUnavailableContext(e),
690
+ },
691
+ hint: LIBRARY_UPSTREAM_UNAVAILABLE_HINT,
692
+ });
693
+ }
585
694
  return errorResult({
586
695
  error: e,
587
696
  context: { id, kind: kind ?? 'layout', source: source ?? DEFAULT_LIBRARY_SOURCE, images: resolvedImages },
@@ -589,26 +698,20 @@ export async function handleLibraryInsert({ client }, { id, kind, source, target
589
698
  });
590
699
  }
591
700
  }
592
- /**
593
- * PAGES_LIBRARY_MEDIA-4 — status/code-aware catch hint for `library_insert`.
594
- *
595
- * The former static hint always said "Verify the id via library_get first" —
596
- * but a 404 `not_found`/`unavailable` means the catalog item cannot be FETCHED
597
- * (no Library API key configured, or the id is genuinely absent), so library_get
598
- * returns an empty success and confirms nothing → a loop. On that class steer
599
- * to the real fixes (configure the apikey, or use the keyless source:"ai")
600
- * instead. Keep the target-column / stale-etag guidance for the 422/412 classes
601
- * they actually describe.
602
- */
603
701
  function libraryInsertHint(e) {
604
702
  if (e instanceof RestError) {
605
703
  const code = e.code ?? '';
606
- if (e.status === 404 || code.includes('not_found') || code.includes('unavailable')) {
607
- return ('The Library item could not be fetched (missing key or absent id). Do NOT ' +
608
- 'loop on yootheme_builder_library_get — it returns an empty success for an ' +
609
- 'unfetchable id. Either configure the YOOtheme Pro Library API key ' +
610
- '(Customizer → Theme Settings → apikey), or use source:"ai" (your keyless ' +
611
- '"WootsUp AI Layouts" store).');
704
+ if (isLibraryNotConfigured(e)) {
705
+ return LIBRARY_NOT_CONFIGURED_HINT;
706
+ }
707
+ if (isLibraryUpstreamUnavailable(e)) {
708
+ return LIBRARY_UPSTREAM_UNAVAILABLE_HINT;
709
+ }
710
+ if (e.status === 404 || code.includes('not_found')) {
711
+ return ('The Library item could not be fetched (no key configured, or the id is not in ' +
712
+ 'the catalog). Do NOT loop on yootheme_builder_library_get. Either configure the ' +
713
+ 'YOOtheme Pro Library API key (Customizer, Theme Settings, apikey), or use ' +
714
+ 'source:"ai" (your keyless "WootsUp AI Layouts" store).');
612
715
  }
613
716
  if (e.status === 422) {
614
717
  return ('A 422 target_not_a_column means a preset (kind:"element") was pointed at a ' +
@@ -624,7 +727,6 @@ function libraryInsertHint(e) {
624
727
  'hull first with yootheme_builder_scaffold_section. A 412 means the etag is stale — ' +
625
728
  're-read (get_etag/pages_list) and retry.');
626
729
  }
627
- // ─── library_save_as (Task 7) ────────────────────────────────────────────
628
730
  const LIBRARY_SAVE_SOURCE_SCHEMA = z.object({
629
731
  template_id: z
630
732
  .string()
@@ -640,6 +742,9 @@ export const LIBRARY_SAVE_AS_OUTPUT_SCHEMA = z.object({
640
742
  name: z.string(),
641
743
  storage: z.string(),
642
744
  invalid_source_possible: z.boolean(),
745
+ destination: z.string().optional(),
746
+ kind: z.string().optional(),
747
+ storage_note: z.string().optional(),
643
748
  });
644
749
  function mapLibrarySaveAsResult(raw) {
645
750
  if (raw === null || typeof raw !== 'object')
@@ -648,25 +753,31 @@ function mapLibrarySaveAsResult(raw) {
648
753
  if (typeof r.library_id !== 'string' || typeof r.name !== 'string' || typeof r.storage !== 'string') {
649
754
  return undefined;
650
755
  }
756
+ const storage = r.storage;
757
+ const isOwnStore = storage === 'ai' || storage === 'wootsup-ai-layouts';
651
758
  return {
652
759
  library_id: r.library_id,
653
760
  name: r.name,
654
- storage: r.storage,
761
+ storage,
655
762
  invalid_source_possible: r.invalid_source_possible === true,
763
+ storage_note: isOwnStore
764
+ ? 'Saved to your isolated "WootsUp AI Layouts" store, NOT to YOOtheme\'s '
765
+ + 'built-in My Layouts: it will not appear in the builder\'s Library panel. '
766
+ + 'Reuse it with library_browse/library_insert using source:"ai", remove it '
767
+ + 'with library_delete. Saving again with destination:"native" puts a copy '
768
+ + 'where the builder shows it, but that copy is one-way: no tool here can '
769
+ + 'find or delete it afterwards.'
770
+ : 'Saved to YOOtheme\'s built-in My Layouts, so the builder shows it under '
771
+ + 'that panel. This copy is ONE-WAY for this server: library_browse/'
772
+ + 'library_get/library_insert reach only "catalog" and "ai" entries, and '
773
+ + 'library_delete removes only "ai" ones, so nothing here can find, reuse '
774
+ + 'or remove it again. Save it with destination:"ai" as well if you want to '
775
+ + 'keep working with it from here.',
656
776
  };
657
777
  }
658
778
  export async function handleLibrarySaveAs({ client }, { source, name, destination }) {
659
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_save_as', {
660
- template_id: source.template_id,
661
- });
662
- if (guard !== null)
663
- return guard;
664
779
  try {
665
780
  const body = { source, name };
666
- // Only forward a NON-default destination (the PHP MyLayoutsStore reads
667
- // the same absent-param `ai` default), so an ai-default save is
668
- // byte-identical to the pre-Task-7 body — only the `native` escape
669
- // hatch ever appears on the wire.
670
781
  if (destination !== undefined && destination !== DEFAULT_SAVE_DESTINATION)
671
782
  body.destination = destination;
672
783
  const data = await client.post('/library/save', { body });
@@ -686,12 +797,22 @@ export async function handleLibrarySaveAs({ client }, { source, name, destinatio
686
797
  });
687
798
  }
688
799
  }
689
- // ─── library_delete (PAGES_LIBRARY_MEDIA-16) ─────────────────────────────
690
- export const LIBRARY_DELETE_OUTPUT_SCHEMA = z.object({
800
+ export const LIBRARY_DELETE_OUTPUT_SCHEMA = z
801
+ .object({
691
802
  deleted: z.boolean(),
692
803
  id: z.string(),
693
804
  deleted_count: z.number(),
694
805
  source: z.string(),
806
+ ...CONFIRM_PREVIEW_FIELDS,
807
+ })
808
+ .superRefine((v, ctx) => {
809
+ if (v.requires_confirmation === true && v.deleted !== false) {
810
+ ctx.addIssue({
811
+ code: 'custom',
812
+ path: ['deleted'],
813
+ message: 'a confirm preview must report deleted:false — nothing was removed',
814
+ });
815
+ }
695
816
  });
696
817
  function mapLibraryDeleteResult(raw) {
697
818
  if (raw === null || typeof raw !== 'object')
@@ -707,14 +828,8 @@ function mapLibraryDeleteResult(raw) {
707
828
  };
708
829
  }
709
830
  export async function handleLibraryDelete({ client }, { id, source }) {
710
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_library_delete', { id });
711
- if (guard !== null)
712
- return guard;
713
831
  try {
714
- const body = { id };
715
- // Only forward a NON-default source. The PHP controller reads the same
716
- // absent-param "ai" default; the curated catalog (source:"catalog") is
717
- // read-only, so forwarding it is what makes the controller answer 403.
832
+ const body = { id, confirm: true };
718
833
  if (source !== undefined && source !== 'ai')
719
834
  body.source = source;
720
835
  const data = await client.post('/library/delete', { body });
@@ -732,11 +847,6 @@ export async function handleLibraryDelete({ client }, { id, source }) {
732
847
  });
733
848
  }
734
849
  }
735
- /**
736
- * Status/code-aware catch hint for library_delete. A 404 means the id was
737
- * already gone (not a failure to act on — the end state is what you wanted); a
738
- * 403 means a read-only source (only source:"ai" is deletable).
739
- */
740
850
  function libraryDeleteHint(e) {
741
851
  if (e instanceof RestError) {
742
852
  if (e.status === 404) {
@@ -751,57 +861,57 @@ function libraryDeleteHint(e) {
751
861
  return ('Pass the `id` of an entry from yootheme_builder_library_browse({source:"ai"}). Only ' +
752
862
  'source:"ai" entries are deletable — the curated library is read-only.');
753
863
  }
754
- // ─── image_search / image_import (Task D1, Image Sourcing) ────────────────
755
- //
756
- // Thin GET/POST forwards to `/library/image-search` (Task C1) mirroring
757
- // library_browse's read-projection pattern, and `/library/image-import`
758
- // mirroring library_insert's write-forward pattern. Response shapes are
759
- // pinned by the PHP controller tests
760
- // (tests/php/unit/Library/LibraryControllerImageSearchTest.php +
761
- // LibraryControllerImageImportTest.php):
762
- // GET /library/image-search → {items, total, source, query, hint?}
763
- // POST /library/image-import → {imported:true, local_url} |
764
- // {imported:false, reason}
765
- const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages'];
766
- /**
767
- * Cross-source orientation dialect (D1 concern, see the plan doc): the
768
- * Unsplash proxy accepts "squarish", the Pexels proxy accepts "square" for
769
- * the SAME concept. `ImageSearchService::buildQueryString()` (PHP) forwards
770
- * whatever string is supplied verbatim, per-source, with NO translation — so
771
- * this schema accepts BOTH spellings instead of forcing one source's
772
- * vocabulary onto the other (which would either reject a legitimate Pexels
773
- * "square" request or silently mis-filter it).
774
- */
864
+ const IMAGE_SOURCE_VALUES = ['unsplash', 'pexels', 'proimages', 'local'];
775
865
  const IMAGE_ORIENTATION_VALUES = ['landscape', 'portrait', 'squarish', 'square'];
866
+ const IMAGE_LICENSE_KEYS = {
867
+ license: z.string().nullable().optional(),
868
+ attribution_required: z.boolean().nullable().optional(),
869
+ license_url: z.string().optional(),
870
+ license_note: z.string().optional(),
871
+ };
776
872
  const IMAGE_RESULT_SCHEMA = z.object({
777
873
  id: z.string(),
778
874
  source: z.string(),
779
875
  thumb_url: z.string(),
780
876
  full_url: z.string(),
781
- width: z.number(),
782
- height: z.number(),
783
- alt: z.string(),
784
- author: z.string(),
785
- author_url: z.string(),
786
- orientation: z.string(),
877
+ width: z.number().nullable(),
878
+ height: z.number().nullable(),
879
+ alt: z.string().optional(),
880
+ author: z.string().optional(),
881
+ author_url: z.string().optional(),
882
+ orientation: z.string().optional(),
883
+ ...IMAGE_LICENSE_KEYS,
787
884
  });
788
885
  export const IMAGE_SEARCH_OUTPUT_SCHEMA = z.object({
789
886
  items: z.array(IMAGE_RESULT_SCHEMA),
790
887
  total: z.number(),
791
888
  source: z.string(),
792
889
  query: z.string(),
793
- // Present ONLY on the empty-result path (no Library API key configured,
794
- // or genuinely zero matches — the REST layer cannot and must not
795
- // distinguish the two; see LibraryControllerImageSearchTest).
796
890
  hint: z.string().optional(),
891
+ api_key_configured: z.boolean().optional(),
892
+ next_offset: z.number().int().min(0).optional(),
893
+ local_note: z.string().optional(),
797
894
  });
798
895
  export const IMAGE_IMPORT_OUTPUT_SCHEMA = z.object({
799
896
  imported: z.boolean(),
800
- // Present only when imported:true.
801
897
  local_url: z.string().optional(),
802
- // Present only when imported:false (a caught importer failure — never a 500).
803
898
  reason: z.string().optional(),
899
+ failure: z.enum(['rejected', 'unavailable']).optional(),
900
+ media_note: z.string().optional(),
901
+ ...IMAGE_LICENSE_KEYS,
804
902
  });
903
+ function pickImageLicense(r) {
904
+ return {
905
+ ...(typeof r.license === 'string' || r.license === null ? { license: r.license } : {}),
906
+ ...(typeof r.attribution_required === 'boolean' || r.attribution_required === null
907
+ ? { attribution_required: r.attribution_required }
908
+ : {}),
909
+ ...(typeof r.license_url === 'string' ? { license_url: r.license_url } : {}),
910
+ ...(typeof r.license_note === 'string' && r.license_note !== ''
911
+ ? { license_note: r.license_note }
912
+ : {}),
913
+ };
914
+ }
805
915
  function mapImageResult(raw) {
806
916
  if (raw === null || typeof raw !== 'object')
807
917
  return undefined;
@@ -813,12 +923,13 @@ function mapImageResult(raw) {
813
923
  source: typeof r.source === 'string' ? r.source : '',
814
924
  thumb_url: typeof r.thumb_url === 'string' ? r.thumb_url : '',
815
925
  full_url: typeof r.full_url === 'string' ? r.full_url : '',
816
- width: typeof r.width === 'number' ? r.width : 0,
817
- height: typeof r.height === 'number' ? r.height : 0,
818
- alt: typeof r.alt === 'string' ? r.alt : '',
819
- author: typeof r.author === 'string' ? r.author : '',
820
- author_url: typeof r.author_url === 'string' ? r.author_url : '',
821
- orientation: typeof r.orientation === 'string' ? r.orientation : '',
926
+ width: typeof r.width === 'number' ? r.width : null,
927
+ height: typeof r.height === 'number' ? r.height : null,
928
+ ...(typeof r.alt === 'string' ? { alt: r.alt } : {}),
929
+ ...(typeof r.author === 'string' ? { author: r.author } : {}),
930
+ ...(typeof r.author_url === 'string' ? { author_url: r.author_url } : {}),
931
+ ...(typeof r.orientation === 'string' ? { orientation: r.orientation } : {}),
932
+ ...pickImageLicense(r),
822
933
  };
823
934
  }
824
935
  const IMAGE_TABLE_COLUMNS = [
@@ -826,63 +937,156 @@ const IMAGE_TABLE_COLUMNS = [
826
937
  { key: 'dimensions', label: 'SIZE', width: 10 },
827
938
  { key: 'author', label: 'AUTHOR', width: 18 },
828
939
  { key: 'orientation', label: 'ORIENT', width: 10 },
829
- // llmOnly: the copyable next-call value (yootheme_builder_image_import's
830
- // `url` arg) — excluded from the Rich Card UI (too long for a table
831
- // cell) but present in the ASCII text the LLM reads. Always present in
832
- // structuredContent.items[].full_url regardless of table membership.
833
940
  { key: 'full_url', label: 'FULL_URL', width: 50, llmOnly: true },
834
941
  ];
835
- export async function handleImageSearch({ client }, { source, query, per_page, orientation, color }) {
836
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_image_search', { query });
837
- if (guard !== null)
838
- return guard;
942
+ export async function handleImageSearch({ client }, { source, query, per_page, orientation, color, offset, include_meta }) {
839
943
  const resolvedSource = source ?? 'unsplash';
840
944
  try {
841
945
  const qs = new URLSearchParams();
842
946
  qs.set('source', resolvedSource);
843
- qs.set('query', query);
947
+ if (typeof query === 'string' && query !== '')
948
+ qs.set('query', query);
844
949
  if (per_page !== undefined)
845
950
  qs.set('per_page', String(per_page));
846
951
  if (typeof orientation === 'string' && orientation !== '')
847
952
  qs.set('orientation', orientation);
848
953
  if (typeof color === 'string' && color !== '')
849
954
  qs.set('color', color);
955
+ if (offset !== undefined)
956
+ qs.set('offset', String(offset));
850
957
  const data = await client.get(`/library/image-search?${qs.toString()}`);
851
958
  const rawItems = Array.isArray(data.items) ? data.items : [];
852
959
  const items = rawItems.map(mapImageResult).filter((i) => i !== undefined);
853
960
  const total = typeof data.total === 'number' ? data.total : items.length;
854
961
  const resolvedResponseSource = typeof data.source === 'string' ? data.source : resolvedSource;
855
- const resolvedQuery = typeof data.query === 'string' ? data.query : query;
962
+ const resolvedQuery = typeof data.query === 'string' ? data.query : (query ?? '');
856
963
  const hint = typeof data.hint === 'string' ? data.hint : undefined;
964
+ const nextOffset = typeof data.next_offset === 'number' && Number.isInteger(data.next_offset) && data.next_offset >= 0
965
+ ? data.next_offset
966
+ : undefined;
967
+ const localNote = typeof data.local_note === 'string' && data.local_note !== '' ? data.local_note : undefined;
968
+ const apiKeyConfigured = typeof data.api_key_configured === 'boolean' ? data.api_key_configured : undefined;
857
969
  const rows = items.map((i) => ({
858
970
  ...i,
859
- dimensions: `${String(i.width)}x${String(i.height)}`,
971
+ dimensions: `${i.width === null ? '?' : String(i.width)}x${i.height === null ? '?' : String(i.height)}`,
860
972
  }));
861
- const toolkitResult = tableResult(rows, {
973
+ const toolkitResult = budgetedTableResult(rows, {
862
974
  columns: [...IMAGE_TABLE_COLUMNS],
863
975
  header: (count) => `${String(count)} of ${String(total)} ${resolvedResponseSource} image${total === 1 ? '' : 's'} for "${resolvedQuery}"`,
864
976
  footer: hint
977
+ ?? localNote
865
978
  ?? 'Import a chosen row\'s full_url with yootheme_builder_image_import, then bind '
866
979
  + 'the returned local_url via element_update_settings.',
867
- });
980
+ includeMeta: include_meta,
981
+ }, COMPACT_DEFAULT);
868
982
  return structuredResult(toolkitResult, {
869
983
  items,
870
984
  total,
871
985
  source: resolvedResponseSource,
872
986
  query: resolvedQuery,
873
987
  ...(hint !== undefined ? { hint } : {}),
988
+ ...(nextOffset !== undefined ? { next_offset: nextOffset } : {}),
989
+ ...(localNote !== undefined ? { local_note: localNote } : {}),
990
+ ...(apiKeyConfigured !== undefined ? { api_key_configured: apiKeyConfigured } : {}),
874
991
  });
875
992
  }
876
993
  catch (e) {
994
+ if (isImageSearchNotConfigured(e)) {
995
+ return errorResult({
996
+ error: e,
997
+ context: {
998
+ source: resolvedSource,
999
+ query,
1000
+ error_code: IMAGE_SEARCH_NOT_CONFIGURED,
1001
+ ...imageSearchUnavailableContext(e),
1002
+ },
1003
+ hint: IMAGE_SEARCH_NOT_CONFIGURED_HINT,
1004
+ });
1005
+ }
1006
+ if (isImageSearchUnavailable(e)) {
1007
+ return errorResult({
1008
+ error: e,
1009
+ context: {
1010
+ source: resolvedSource,
1011
+ query,
1012
+ error_code: 'image_search_unavailable',
1013
+ ...imageSearchUnavailableContext(e),
1014
+ },
1015
+ hint: IMAGE_SEARCH_UNAVAILABLE_HINT,
1016
+ });
1017
+ }
877
1018
  return errorResult({
878
1019
  error: e,
879
1020
  context: { source: resolvedSource, query, per_page, orientation, color },
880
- hint: 'total:0 usually means no YOOtheme Pro Library API key is configured (the same '
881
- + 'theme apikey library_browse/insert use). Run yootheme_builder_diagnose to verify auth. '
1021
+ hint: 'Run yootheme_builder_diagnose to verify auth and connectivity, then retry. '
882
1022
  + 'yootheme_builder_image_import still works with a direct image URL (no key needed).',
883
1023
  });
884
1024
  }
885
1025
  }
1026
+ const IMAGE_SEARCH_UNAVAILABLE = 'image_search_unavailable';
1027
+ export const IMAGE_SEARCH_UNAVAILABLE_HINT = 'This is a SEARCH FAILURE, not an empty result. Do NOT retry with a broader query; the '
1028
+ + 'provider never answered with results. An HTTP 401 here means the YOOtheme Pro Library API '
1029
+ + 'key this site has configured was REFUSED for this call. WHY it was refused is not visible '
1030
+ + 'from here, so do NOT name a cause: report that the key was refused and let the site owner '
1031
+ + 'check it with YOOtheme. yootheme_builder_image_import still works with a direct Unsplash, '
1032
+ + 'Pexels or YOOtheme library image URL (no key needed).';
1033
+ const IMAGE_SEARCH_NOT_CONFIGURED = 'image_search_api_key_not_configured';
1034
+ export const IMAGE_SEARCH_NOT_CONFIGURED_HINT = 'This is a CONFIGURATION GAP, not an empty result: no YOOtheme Pro Library API key could be '
1035
+ + 'read on this site, so no search request was made and nothing is known about what the '
1036
+ + 'provider holds. Do NOT retry with a broader query. Run yootheme_builder_diagnose and read '
1037
+ + 'library_key.state before naming a fix: it separates "no key entered", which the customer '
1038
+ + 'fixes in the YOOtheme Customizer theme settings, from a lookup that found no YOOtheme update '
1039
+ + 'site at all, where there is nothing to fix in the key field. yootheme_builder_image_import '
1040
+ + 'still works with a direct Unsplash, Pexels or YOOtheme library image URL (no key needed).';
1041
+ function imageSearchErrorBody(e) {
1042
+ if (!(e instanceof RestError))
1043
+ return undefined;
1044
+ const body = e.body;
1045
+ if (body === null || typeof body !== 'object' || Array.isArray(body))
1046
+ return undefined;
1047
+ return body;
1048
+ }
1049
+ function isImageSearchUnavailable(e) {
1050
+ return imageSearchCodeMatches(e, IMAGE_SEARCH_UNAVAILABLE);
1051
+ }
1052
+ function isImageSearchNotConfigured(e) {
1053
+ return imageSearchCodeMatches(e, IMAGE_SEARCH_NOT_CONFIGURED);
1054
+ }
1055
+ function imageSearchCodeMatches(e, code) {
1056
+ if (e instanceof RestError && typeof e.code === 'string' && e.code.endsWith(code)) {
1057
+ return true;
1058
+ }
1059
+ const body = imageSearchErrorBody(e);
1060
+ if (body === undefined)
1061
+ return false;
1062
+ if (body.error_code === code)
1063
+ return true;
1064
+ const data = body.data;
1065
+ return (data !== null
1066
+ && typeof data === 'object'
1067
+ && !Array.isArray(data)
1068
+ && data.error_code === code);
1069
+ }
1070
+ function imageSearchUnavailableContext(e) {
1071
+ const body = imageSearchErrorBody(e);
1072
+ if (body === undefined)
1073
+ return {};
1074
+ const data = body.data !== null && typeof body.data === 'object' && !Array.isArray(body.data)
1075
+ ? body.data
1076
+ : {};
1077
+ const out = {};
1078
+ const upstream = body.upstream_status ?? data.upstream_status;
1079
+ if (typeof upstream === 'number')
1080
+ out.upstream_status = upstream;
1081
+ const configured = body.api_key_configured ?? data.api_key_configured;
1082
+ if (typeof configured === 'boolean')
1083
+ out.api_key_configured = configured;
1084
+ return out;
1085
+ }
1086
+ const IMAGE_IMPORT_FAILURE_VALUES = ['rejected', 'unavailable'];
1087
+ function mapImageImportFailure(raw) {
1088
+ return IMAGE_IMPORT_FAILURE_VALUES.find((v) => v === raw);
1089
+ }
886
1090
  function mapImageImportResult(raw) {
887
1091
  if (raw === null || typeof raw !== 'object')
888
1092
  return undefined;
@@ -892,17 +1096,41 @@ function mapImageImportResult(raw) {
892
1096
  if (r.imported) {
893
1097
  if (typeof r.local_url !== 'string' || r.local_url === '')
894
1098
  return undefined;
895
- return { imported: true, local_url: r.local_url };
1099
+ return {
1100
+ imported: true,
1101
+ local_url: r.local_url,
1102
+ ...(typeof r.media_note === 'string' && r.media_note !== '' ? { media_note: r.media_note } : {}),
1103
+ ...pickImageLicense(r),
1104
+ };
896
1105
  }
1106
+ const failure = mapImageImportFailure(r.failure);
897
1107
  return {
898
1108
  imported: false,
899
1109
  reason: typeof r.reason === 'string' && r.reason !== '' ? r.reason : 'Import failed for an unknown reason.',
1110
+ ...(failure === undefined ? {} : { failure }),
900
1111
  };
901
1112
  }
902
- export async function handleImageImport({ client }, { url }) {
903
- const guard = joomlaNotSupportedGuard(client, 'yootheme_builder_image_import', { url });
904
- if (guard !== null)
905
- return guard;
1113
+ export async function handleImageImport({ client }, args) {
1114
+ if (args.url !== undefined
1115
+ && args.full_url !== undefined
1116
+ && args.url !== args.full_url) {
1117
+ return errorResult({
1118
+ error: new Error('The "url" and "full_url" arguments are two names for ONE value and were '
1119
+ + 'given different ones. Pass only the url to import.'),
1120
+ context: { url: args.url, full_url: args.full_url },
1121
+ hint: 'Drop one of the two. "full_url" is accepted only because that is the name a '
1122
+ + 'yootheme_builder_image_search row carries.',
1123
+ });
1124
+ }
1125
+ const url = args.url ?? args.full_url;
1126
+ if (url === undefined || url === '') {
1127
+ return errorResult({
1128
+ error: new Error('The "url" argument is required — a full_url from yootheme_builder_image_search '
1129
+ + 'is accepted under either name.'),
1130
+ context: {},
1131
+ hint: 'Pass a full_url from yootheme_builder_image_search.',
1132
+ });
1133
+ }
906
1134
  try {
907
1135
  const data = await client.post('/library/image-import', { body: { url } });
908
1136
  const mapped = mapImageImportResult(data);
@@ -912,6 +1140,12 @@ export async function handleImageImport({ client }, { url }) {
912
1140
  return structuredResult(jsonResult(mapped), mapped);
913
1141
  }
914
1142
  catch (e) {
1143
+ if (e instanceof RestError) {
1144
+ const mapped = mapImageImportResult(e.body);
1145
+ if (mapped !== undefined && !mapped.imported) {
1146
+ return structuredResult(jsonResult(mapped), mapped);
1147
+ }
1148
+ }
915
1149
  return errorResult({
916
1150
  error: e,
917
1151
  context: { url },
@@ -926,13 +1160,15 @@ export function buildLibraryTools(pool) {
926
1160
  defineTool({
927
1161
  name: 'yootheme_builder_library_browse',
928
1162
  description: 'Browse the YOOtheme Pro Library catalog: pre-designed page LAYOUTS, element ' +
929
- 'PRESETS, or purpose-classified SECTIONS (hero/features/cta/...). Filter with ' +
930
- '`kind` ("layout" default | "element" | "section"), `topic`, `type`/`role`, ' +
931
- '`element_type`, `purpose`/`has_effects` (sections only), `website`, `query`. ' +
932
- 'Returns `{items, total, next_cursor}`; a "section" id is the composite ' +
933
- '"<layoutId>#<sectionIndex>" feeds straight into library_get/library_insert. ' +
934
- '`total:0` usually means no Library API key configured. Operates on the default ' +
935
- 'site unless site_id is provided.',
1163
+ 'PRESETS, or purpose-classified SECTIONS. Filter: `kind`, `topic`, `type`/`role`, ' +
1164
+ '`element_type`, `purpose`, `query`. A section id is the composite ' +
1165
+ '"<layoutId>#<sectionIndex>" feeds library_get/insert. ' +
1166
+ '`source`: "catalog" (default) or "ai" YOUR saved entries from ' +
1167
+ 'library_save_as, which library_delete removes again both gateway-only, ' +
1168
+ 'call via yootheme_builder_advanced({tool, arguments}). `total:0` = the ' +
1169
+ 'catalog matched nothing; a read with no data answers ' +
1170
+ '`library_upstream_unavailable` or `library_api_key_not_configured`, never an ' +
1171
+ 'empty page.',
936
1172
  inputSchema: {
937
1173
  site_id: SITE_ID_SCHEMA,
938
1174
  kind: z
@@ -967,7 +1203,13 @@ export function buildLibraryTools(pool) {
967
1203
  .string()
968
1204
  .min(1)
969
1205
  .optional()
970
- .describe('Page-role filter (e.g. "Home", "Contact", "Services"). Only applies when kind="layout". Exact match.'),
1206
+ .describe('Filters on how the SOURCE demo website filed this layout in ITS OWN menu ' +
1207
+ '(e.g. "Home", "Contact", "Services") — sourced verbatim from the YOOtheme ' +
1208
+ 'Pro Library catalog, NOT a classification of what the layout actually ' +
1209
+ 'builds. A layout can carry a role unrelated to its content if that is ' +
1210
+ 'where the source site\'s own navigation put it; prefer `query` (name ' +
1211
+ 'match) or browsing without this filter when the exact wording is ' +
1212
+ 'unknown. Only applies when kind="layout". Exact match.'),
971
1213
  website: z
972
1214
  .string()
973
1215
  .min(1)
@@ -981,7 +1223,11 @@ export function buildLibraryTools(pool) {
981
1223
  purpose: z
982
1224
  .enum(SECTION_PURPOSE_VALUES)
983
1225
  .optional()
984
- .describe('Section-purpose filter (e.g. "hero", "features", "cta"). Only applies when kind="section". Exact match.'),
1226
+ .describe('Section-purpose filter (e.g. "hero", "features", "cta"). Only applies when ' +
1227
+ 'kind="section". Exact match. BROAD buckets, not topics: hundreds of sections ' +
1228
+ 'share one, none names a specific subject, so the closest-looking value is a ' +
1229
+ 'guess. For a subject ("services") use `query` instead - it matches the ' +
1230
+ 'section NAME.'),
985
1231
  has_effects: z
986
1232
  .boolean()
987
1233
  .optional()
@@ -1003,6 +1249,7 @@ export function buildLibraryTools(pool) {
1003
1249
  .string()
1004
1250
  .optional()
1005
1251
  .describe('Opaque cursor from a previous response\'s next_cursor — fetches the next page.'),
1252
+ include_meta: INCLUDE_META_SCHEMA,
1006
1253
  },
1007
1254
  outputSchema: LIBRARY_BROWSE_OUTPUT_SCHEMA,
1008
1255
  annotations: readOnly('Browse Layout Library'),
@@ -1010,13 +1257,12 @@ export function buildLibraryTools(pool) {
1010
1257
  }),
1011
1258
  defineTool({
1012
1259
  name: 'yootheme_builder_library_get',
1013
- description: 'Fetch a SINGLE YOOtheme Pro Library layout or element preset by `id` — call ' +
1014
- 'after yootheme_builder_library_browse (items[].id feeds this). Returns ' +
1015
- '`{id, kind, name, version, tree, sections, images_manifest_count}`: `tree` is ' +
1016
- 'the raw JSON; `sections` lists `tree.children` (layouts only, presets return ' +
1017
- '`[]`), each `{index, name, path, element_count}`. `include_thumbnail:true` ' +
1018
- 'also returns an image block (costs tokens see its own description). ' +
1019
- 'Read-only; does not insert/bind. Operates on the default site unless site_id is provided.',
1260
+ description: 'Fetch ONE Library layout or element preset by `id` — call after ' +
1261
+ 'library_browse (items[].id feeds this). `include_thumbnail:true` adds an ' +
1262
+ 'image (costs tokens). This DETAIL endpoint checks the key, browse does not: no ' +
1263
+ 'key gives 503 library_api_key_not_configured, a refused key 503 ' +
1264
+ 'library_upstream_unavailable. Report the refusal, not a cause. `source`: ' +
1265
+ '"catalog" (default) or "ai" for your own library_save_as (gateway-only) entries.',
1020
1266
  inputSchema: {
1021
1267
  site_id: SITE_ID_SCHEMA,
1022
1268
  id: z
@@ -1035,6 +1281,15 @@ export function buildLibraryTools(pool) {
1035
1281
  .describe('Which library the `id` belongs to: "catalog" (remote YOOtheme Pro Library, ' +
1036
1282
  'default) or "ai" (your OWN "WootsUp AI Layouts"). Must match the source the ' +
1037
1283
  'id came from in yootheme_builder_library_browse.'),
1284
+ max_chars: MAX_CHARS.describe('Cap (chars) on this result. On THIS tool it ALSO bounds structuredContent — ' +
1285
+ 'an oversized `tree` is replaced by a compact summary inside the same key ' +
1286
+ '(element_type_counts, truncated:true, bytes_full, hint) instead of riding ' +
1287
+ 'through whole; small items are unaffected. Applies even when omitted ' +
1288
+ '(default safety cap: 20000 chars). Over the plugin-served endpoint both ' +
1289
+ 'legs already carry the same shortened payload. ' +
1290
+ 'Raising it stops helping at the limit your own client puts on a single tool ' +
1291
+ 'result, which this server cannot see. ' +
1292
+ 'Out of range, the cap that actually applied comes back as `max_chars`.'),
1038
1293
  include_thumbnail: z
1039
1294
  .boolean()
1040
1295
  .default(false)
@@ -1050,14 +1305,14 @@ export function buildLibraryTools(pool) {
1050
1305
  }),
1051
1306
  defineTool({
1052
1307
  name: 'yootheme_builder_library_insert',
1053
- description: 'Insert a YOOtheme Pro Library item (layout, section, or preset) onto a page — ' +
1054
- 'call after yootheme_builder_library_get/browse. `target`: new_page (creates a ' +
1055
- 'page), {template_id,element_path?} (splices onto the layout root, or a preset ' +
1056
- 'onto a Column), or sublayout (splices rows into a fragment). A preset ' +
1057
- '(kind:"element") MUST target a Column 422 otherwise; scaffold one first with ' +
1058
- 'yootheme_builder_scaffold_section. `images`: "placeholder" (default) or ' +
1059
- '"curated" (downloads to Media Library). Optional `etag` locks (412 if stale). ' +
1060
- 'Operates on the default site unless site_id is provided.',
1308
+ description: 'Insert a Library item onto a page — call after library_get/browse. `target`: ' +
1309
+ 'new_page, {template_id,element_path?} (splices onto the layout root, or a ' +
1310
+ 'preset onto a Column), or sublayout. A preset (kind:"element") MUST target a ' +
1311
+ 'Column (422 otherwise) scaffold one with scaffold_section. `images`: ' +
1312
+ '"placeholder" (default) or "curated" (downloads to Media Library). Placement: ' +
1313
+ '`insert_mode` for layouts, `index` for presets. `etag` ' +
1314
+ 'locks (412 if stale). `source`: "catalog" (default) or "ai" to insert your own ' +
1315
+ 'saved entry.',
1061
1316
  inputSchema: {
1062
1317
  site_id: SITE_ID_SCHEMA,
1063
1318
  id: z
@@ -1087,30 +1342,42 @@ export function buildLibraryTools(pool) {
1087
1342
  .enum(['placeholder', 'curated'])
1088
1343
  .default('placeholder')
1089
1344
  .describe('"placeholder" (default, safe, no network I/O) or "curated" (downloads + sideloads every unique image into the Media Library).'),
1090
- etag: z
1091
- .string()
1345
+ insert_mode: z
1346
+ .enum(['replace', 'top', 'bottom'])
1347
+ .default('replace')
1348
+ .describe('How the composed content joins the target\'s EXISTING children — mirrors the ' +
1349
+ 'Library dialog\'s own picker. "replace" (default) drops them; "top"/"bottom" ' +
1350
+ 'prepend/append. Ignored for target:new_page (a fresh page starts empty). ' +
1351
+ 'REFUSED for kind:"element" — a preset takes `index`.'),
1352
+ index: z
1353
+ .number()
1354
+ .int()
1355
+ .min(0)
1092
1356
  .optional()
1093
- .describe('Optional optimistic-lock ETag (from get_etag/pages_list). Mismatched 412; caller must re-read and retry.'),
1357
+ .describe('kind:"element" ONLY (refused otherwise): where the preset lands among the ' +
1358
+ 'Column\'s children. 0 = first, omit to append, past-end clamps — as in ' +
1359
+ 'element_add. A preset never replaces what the Column holds.'),
1360
+ confirm: z
1361
+ .boolean()
1362
+ .optional()
1363
+ .describe('Set true to proceed when insert_mode:"replace" would remove the target\'s ' +
1364
+ 'existing content. Omit for a preview naming what would be lost; "top"/' +
1365
+ '"bottom", an empty target and kind:"element" (a preset only ever adds) ' +
1366
+ 'never need it. ' +
1367
+ HOST_PROMPT_PROMISE),
1368
+ etag: ETAG,
1094
1369
  },
1095
1370
  outputSchema: LIBRARY_INSERT_OUTPUT_SCHEMA,
1096
- annotations: creating('Insert Layout Library Item'),
1371
+ annotations: destructive('Insert Layout Library Item'),
1097
1372
  handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_insert', async (client, site) => withSiteMeta(await handleLibraryInsert({ client }, rest), site)),
1098
1373
  }),
1099
- // Task 7 (Layout Library, Spec #7) — ADVANCED tool (deliberately NOT
1100
- // added to essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
1101
- // yootheme_builder_advanced({tool, arguments}), never a first-class
1102
- // tools/list entry. See the module docblock's "Tiering" note.
1103
1374
  defineTool({
1104
1375
  name: 'yootheme_builder_library_save_as',
1105
1376
  description: 'Save a page or subtree into a reusable library — the REVERSE of ' +
1106
- 'yootheme_builder_library_insert. `source`: {template_id, ' +
1107
- 'element_path?} (omit element_path to save the WHOLE page). ' +
1108
- '`destination`: "ai" (default your isolated "WootsUp AI Layouts", ' +
1109
- 'reusable via library_browse/insert source:"ai") or "native" ' +
1110
- '(YOOtheme\'s built-in My Layouts). Returns `{library_id, name, ' +
1111
- 'storage, invalid_source_possible}` (the last flags bound sources ' +
1112
- 'that may need re-binding after reload). Advanced/gateway-only. ' +
1113
- 'Operates on the default site unless site_id is provided.',
1377
+ 'library_insert. `source`: {template_id, element_path?} (omit element_path to ' +
1378
+ 'save the WHOLE page). `destination`: "ai" (default — your isolated "WootsUp AI ' +
1379
+ 'Layouts", reusable via library_browse source:"ai") or "native" (YOOtheme\'s ' +
1380
+ 'built-in My Layouts).',
1114
1381
  inputSchema: {
1115
1382
  site_id: SITE_ID_SCHEMA,
1116
1383
  source: LIBRARY_SAVE_SOURCE_SCHEMA.describe('The page/subtree to save.'),
@@ -1118,10 +1385,6 @@ export function buildLibraryTools(pool) {
1118
1385
  .string()
1119
1386
  .min(1)
1120
1387
  .describe('Display name for the new library entry (e.g. "Hero Section").'),
1121
- // PAGES_LIBRARY_MEDIA-15: `.optional()` (not `.default('ai')`) so
1122
- // the generated schema does NOT advertise a REQUIRED field that
1123
- // also carries a default. The handler applies the `ai` default
1124
- // when omitted (it only forwards a NON-default `native`).
1125
1388
  destination: z
1126
1389
  .enum(SAVE_DESTINATION_VALUES)
1127
1390
  .optional()
@@ -1131,32 +1394,15 @@ export function buildLibraryTools(pool) {
1131
1394
  '(the escape hatch: save into YOOtheme\'s built-in "My Layouts").'),
1132
1395
  },
1133
1396
  outputSchema: LIBRARY_SAVE_AS_OUTPUT_SCHEMA,
1134
- // PAGES_LIBRARY_MEDIA-15: title names the DEFAULT destination (the AI
1135
- // Layouts store), not "My Layouts" (which is the non-default `native`).
1136
1397
  annotations: creating('Save to AI Layouts library'),
1137
1398
  handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_save_as', async (client, site) => withSiteMeta(await handleLibrarySaveAs({ client }, rest), site)),
1138
1399
  }),
1139
- // PAGES_LIBRARY_MEDIA-16 — ADVANCED tool (deliberately NOT added to
1140
- // essentials.ts/ESSENTIAL_TOOLS): reachable ONLY via
1141
- // yootheme_builder_advanced({tool, arguments}). The INVERSE of
1142
- // library_save_as. Annotated destructive() — it IRREVERSIBLY removes a
1143
- // saved AI-Layouts entry (a layout the user deliberately saved via
1144
- // library_save_as; there is no undo). This matches the codebase's own
1145
- // "irreversible → destructive → host confirm-gate" precedent:
1146
- // element_delete + pages_delete are destructive (audit F9), and
1147
- // clean_implode_directives was re-classified mutating→destructive in
1148
- // Wave H5 for the same reason (cannot be restored). Deleting saved user
1149
- // data should fire the host's destructive-approval gate, not slip
1150
- // through as an ordinary mutating write.
1151
1400
  defineTool({
1152
1401
  name: 'yootheme_builder_library_delete',
1153
- description: 'Delete ONE saved entry from your "WootsUp AI Layouts" store (the isolated library ' +
1154
- 'yootheme_builder_library_save_as writes into) — clean up experimental sections/layouts ' +
1155
- 'the agent saved. Pass the `id` from a library_browse(source:"ai") row. `source` defaults ' +
1156
- 'to "ai", the ONLY deletable store; the curated library is read-only, so source:"catalog" ' +
1157
- 'returns a 403. IRREVERSIBLE — requires confirm:true. Returns `{deleted, id, deleted_count, ' +
1158
- 'source}`; an unknown id returns a 404. Advanced/gateway-only. Operates on the default site ' +
1159
- 'unless site_id is provided.',
1402
+ description: 'Delete ONE saved entry from your "WootsUp AI Layouts" store (library_save_as ' +
1403
+ 'writes into it) — pass the `id` from a library_browse(source:"ai") row. ' +
1404
+ '`source` defaults to "ai", the ONLY deletable store; the curated library is ' +
1405
+ 'read-only, source:"catalog" returns 403. IRREVERSIBLE — requires confirm:true.',
1160
1406
  inputSchema: {
1161
1407
  site_id: SITE_ID_SCHEMA,
1162
1408
  id: z
@@ -1170,9 +1416,6 @@ export function buildLibraryTools(pool) {
1170
1416
  .describe('Which library to delete from. Only "ai" (your OWN "WootsUp AI Layouts") is ' +
1171
1417
  'deletable; "catalog" (the curated remote YOOtheme Pro Library) is read-only ' +
1172
1418
  'and returns a 403. Omit to default to "ai".'),
1173
- // Destructive-tool confirm gate (matches element_delete / pages_delete):
1174
- // z.literal(true) makes the MCP boundary REJECT any call without
1175
- // confirm:true, so an irreversible delete can never fire by accident.
1176
1419
  confirm: z
1177
1420
  .literal(true)
1178
1421
  .describe('Required — must be `true`. Deleting a saved AI-Layouts entry is IRREVERSIBLE ' +
@@ -1182,28 +1425,33 @@ export function buildLibraryTools(pool) {
1182
1425
  annotations: destructive('Delete AI Layouts entry'),
1183
1426
  handler: async ({ site_id, ...rest }) => withWriteSiteRetry(pool, site_id, 'yootheme_builder_library_delete', async (client, site) => withSiteMeta(await handleLibraryDelete({ client }, rest), site)),
1184
1427
  }),
1185
- // Task D1 (Image Sourcing) — read-only forward, mirrors library_browse.
1186
1428
  defineTool({
1187
1429
  name: 'yootheme_builder_image_search',
1188
- description: 'Search real on-topic photos (Unsplash/Pexels) or YOOtheme Pro Images to fill '
1189
- + 'layout image slots with REAL, on-brand photography instead of bare '
1190
- + 'placeholders. Returns candidate rows with `full_url` (pass straight to '
1191
- + 'yootheme_builder_image_import) plus dimensions/author/orientation. '
1192
- + '`total:0` usually means no Library API key is configured (the same theme '
1193
- + 'apikey library_browse/insert use). Operates on the default site unless '
1194
- + 'site_id is provided.',
1430
+ description: 'Search on-topic photos (Unsplash/Pexels), YOOtheme Pro Images, or (source:"local") '
1431
+ + 'this site\'s own media library. Rows carry `full_url` (feeds image_import) plus '
1432
+ + 'dimensions/author. `total:0` is the ONLY shape meaning the provider matched '
1433
+ + 'nothing; a search that never happened answers an `error_code` instead. No '
1434
+ + 'apikey gives `image_search_api_key_not_configured`; a provider that REFUSES '
1435
+ + 'gives `error_code: image_search_unavailable`, which no broader query can fix. '
1436
+ + 'A key can be CONFIGURED and still be REFUSED (401): report that the upstream '
1437
+ + 'refused it, never why (source:"local" is exempt — see its field description). '
1438
+ + 'Works on WordPress and Joomla.',
1195
1439
  inputSchema: {
1196
1440
  site_id: SITE_ID_SCHEMA,
1197
1441
  source: z
1198
1442
  .enum(IMAGE_SOURCE_VALUES)
1199
1443
  .default('unsplash')
1200
1444
  .describe('Image source. unsplash/pexels = real photography; proimages = YOOtheme '
1201
- + 'Pro curated decorative/background assets, not topical photography.'),
1445
+ + 'Pro curated decorative/background assets, not topical photography; '
1446
+ + 'local = this site\'s own media library — no key, no image_import '
1447
+ + '(bind full_url directly), paginated via `offset`/`next_offset`.'),
1202
1448
  query: z
1203
1449
  .string()
1204
1450
  .min(1)
1451
+ .optional()
1205
1452
  .describe('Search terms, e.g. "coffee roasting", "modern architecture interior". '
1206
- + 'Search deliberately by industry + tone, not a generic term like "business".'),
1453
+ + 'Search deliberately by industry + tone, not a generic term like "business". '
1454
+ + 'Optional only for source:"local" (lists without one); the other three require it.'),
1207
1455
  per_page: z
1208
1456
  .number()
1209
1457
  .int()
@@ -1214,40 +1462,59 @@ export function buildLibraryTools(pool) {
1214
1462
  orientation: z
1215
1463
  .enum(IMAGE_ORIENTATION_VALUES)
1216
1464
  .optional()
1217
- .describe('landscape for heroes/banners, portrait for cards. "squarish" is the '
1218
- + 'Unsplash spelling, "square" is the Pexels spelling for the same shape. '
1219
- + 'Both are accepted; ignored for proimages.'),
1465
+ .describe('landscape for heroes/banners, portrait for cards. Each square spelling '
1466
+ + 'belongs to ONE source: "squarish" to unsplash, "square" to pexels. '
1467
+ + 'Crossing them fails two different ways, and only one is visible: '
1468
+ + '"square" on unsplash is refused upstream (HTTP 400, '
1469
+ + 'image_search_unavailable), while "squarish" on pexels is silently '
1470
+ + 'ignored and you get unfiltered rows back. Even on its own source '
1471
+ + '"squarish" is approximate: unsplash still returns landscape and '
1472
+ + 'portrait rows for it. Ignored for proimages and local.'),
1220
1473
  color: z
1221
1474
  .string()
1222
1475
  .min(1)
1223
1476
  .optional()
1224
- .describe('Optional upstream color filter (e.g. "black_and_white"). Passed through as-is.'),
1477
+ .describe('Optional upstream color filter (e.g. "black_and_white"). Passed through as-is; ignored for local.'),
1478
+ offset: z
1479
+ .number()
1480
+ .int()
1481
+ .min(0)
1482
+ .optional()
1483
+ .describe('source:"local" only: pass back a prior response\'s `next_offset` to continue. '
1484
+ + 'Ignored by the three proxy sources.'),
1485
+ include_meta: INCLUDE_META_SCHEMA,
1225
1486
  },
1226
1487
  outputSchema: IMAGE_SEARCH_OUTPUT_SCHEMA,
1227
1488
  annotations: readOnly('Search Images'),
1228
1489
  handler: async ({ site_id, ...rest }) => withSiteRetry(pool, site_id, async (client, site) => withSiteMeta(await handleImageSearch({ client }, rest), site)),
1229
1490
  }),
1230
- // Task D1 (Image Sourcing) — write forward, mirrors library_insert.
1231
- // mutating() (writes to the Media Library) but NOT destructive() — it
1232
- // only creates, never deletes (Global Constraints, plan doc).
1233
1491
  defineTool({
1234
1492
  name: 'yootheme_builder_image_import',
1235
- description: 'Sideload a photo into the site\'s own Media Library. Accepts ANY public image '
1236
- + 'URL it does NOT require a Library API key, so it still works when '
1237
- + 'image_search is unavailable (typically pass a `full_url` from '
1238
- + 'yootheme_builder_image_search). Returns `{imported:true, local_url}` where '
1239
- + 'local_url is a SITE-RELATIVE path (e.g. /wp-content/uploads/...). Bind it via '
1240
- + 'element_update_settings({props:{image: local_url}, merge:true}). A caught '
1241
- + 'import failure returns `{imported:false, reason}`, never a 500. Operates on '
1242
- + 'the default site unless site_id is provided.',
1493
+ description: 'Sideload a photo into the site\'s Media Library. Only Unsplash, Pexels and the '
1494
+ + 'YOOtheme library CDN are accepted; other hosts are refused. Returns '
1495
+ + '`{imported:true, local_url}`; shape is platform-specific: WordPress leads '
1496
+ + 'with a slash (/wp-content/uploads/...), Joomla does NOT (images/yootheme/...). '
1497
+ + 'Bind it VERBATIM via element_update_settings({props:{image: local_url}, ' +
1498
+ 'merge:true}). Give the address as `url` OR as `full_url`, the field name an ' +
1499
+ 'image_search row carries; one of the two is required. Identical re-imports ' +
1500
+ 'reuse the existing file in the current upload folder. Failures return ' +
1501
+ '`{imported:false, reason, failure}`; `unavailable` = no url works now.',
1243
1502
  inputSchema: {
1244
1503
  site_id: SITE_ID_SCHEMA,
1245
1504
  url: z
1246
1505
  .string()
1247
1506
  .url()
1507
+ .optional()
1248
1508
  .describe('The image URL to sideload (typically a full_url from '
1249
1509
  + 'yootheme_builder_image_search). Downloads it into the site Media '
1250
1510
  + 'Library and returns a local URL to bind via element_update_settings.'),
1511
+ full_url: z
1512
+ .string()
1513
+ .url()
1514
+ .optional()
1515
+ .describe('Second name for `url` — the field name an image_search row carries, so a '
1516
+ + 'row can be passed on unchanged. Pass ONE of the two; two different '
1517
+ + 'values are refused rather than reconciled.'),
1251
1518
  },
1252
1519
  outputSchema: IMAGE_IMPORT_OUTPUT_SCHEMA,
1253
1520
  annotations: mutating('Import Image'),