synthos 0.8.0 → 0.9.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 (359) hide show
  1. package/README.md +1 -1
  2. package/default-pages/application/page.html +42 -0
  3. package/default-pages/application/page.json +10 -0
  4. package/default-pages/elevenlabs_effects_studio/page.html +1363 -0
  5. package/default-pages/elevenlabs_effects_studio/page.json +11 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +801 -0
  7. package/default-pages/elevenlabs_voice_studio/page.json +11 -0
  8. package/default-pages/{json_tools.html → json_tools/page.html} +13 -11
  9. package/default-pages/json_tools/page.json +10 -0
  10. package/default-pages/my_notes/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json +5 -0
  11. package/default-pages/my_notes/page.html +132 -0
  12. package/default-pages/{my_notes.json → my_notes/page.json} +2 -2
  13. package/default-pages/neon_asteroids/files/Ambient_Space.mp3 +0 -0
  14. package/default-pages/neon_asteroids/files/Ambient_Space2.mp3 +0 -0
  15. package/default-pages/neon_asteroids/files/Ambient_Space3.mp3 +0 -0
  16. package/default-pages/neon_asteroids/files/Asteroid_Explosion.mp3 +0 -0
  17. package/default-pages/neon_asteroids/files/Hyperspace_Jump.mp3 +0 -0
  18. package/default-pages/neon_asteroids/files/Laser_Fire.mp3 +0 -0
  19. package/default-pages/neon_asteroids/files/Menu_Navigate.mp3 +0 -0
  20. package/default-pages/neon_asteroids/files/Power_Up_Collect.mp3 +0 -0
  21. package/default-pages/neon_asteroids/files/Saucer_Alert.mp3 +0 -0
  22. package/default-pages/neon_asteroids/files/Ship_Thrust.mp3 +0 -0
  23. package/default-pages/neon_asteroids/files/effects.json +74 -0
  24. package/default-pages/neon_asteroids/page.html +1822 -0
  25. package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
  26. package/default-pages/{oregon_trail.html → oregon_trail/page.html} +14 -12
  27. package/default-pages/{oregon_trail.json → oregon_trail/page.json} +2 -2
  28. package/default-pages/retro_game_starter/page.html +1308 -0
  29. package/default-pages/retro_game_starter/page.json +12 -0
  30. package/default-pages/{sidebar_page.html → sidebar_page/page.html} +12 -10
  31. package/default-pages/sidebar_page/page.json +10 -0
  32. package/default-pages/{solar_explorer.html → solar_explorer/page.html} +14 -11
  33. package/default-pages/{solar_explorer.json → solar_explorer/page.json} +2 -2
  34. package/default-pages/{solar_tutorial.html → solar_tutorial/page.html} +12 -10
  35. package/default-pages/solar_tutorial/page.json +10 -0
  36. package/default-pages/{two-panel_page.html → two-panel_page/page.html} +13 -11
  37. package/default-pages/two-panel_page/page.json +10 -0
  38. package/default-pages/{us_map.html → us_map/page.html} +193 -192
  39. package/default-pages/{us_map.json → us_map/page.json} +12 -12
  40. package/default-pages/{us_map_1850.html → us_map_1850/page.html} +326 -325
  41. package/default-pages/{us_map_1850.json → us_map_1850/page.json} +12 -12
  42. package/default-pages/{western_cities_1850.html → western_cities_1850/page.html} +527 -526
  43. package/default-pages/{western_cities_1850.json → western_cities_1850/page.json} +12 -12
  44. package/default-themes/aurora-dawn.json +19 -0
  45. package/default-themes/aurora-dawn.v3.css +198 -0
  46. package/default-themes/aurora-dusk.json +19 -0
  47. package/default-themes/aurora-dusk.v3.css +200 -0
  48. package/default-themes/cosmos-dawn.json +19 -0
  49. package/default-themes/cosmos-dawn.v3.css +198 -0
  50. package/default-themes/cosmos-dusk.json +19 -0
  51. package/default-themes/cosmos-dusk.v3.css +200 -0
  52. package/default-themes/high-contrast-dark.json +19 -0
  53. package/default-themes/high-contrast-dark.v3.css +200 -0
  54. package/default-themes/high-contrast-light.json +19 -0
  55. package/default-themes/high-contrast-light.v3.css +198 -0
  56. package/default-themes/nebula-dawn.v2.css +110 -0
  57. package/default-themes/nebula-dawn.v3.css +199 -0
  58. package/default-themes/nebula-dusk.v2.css +104 -0
  59. package/default-themes/nebula-dusk.v3.css +201 -0
  60. package/default-themes/solar-flare-dawn.json +19 -0
  61. package/default-themes/solar-flare-dawn.v3.css +198 -0
  62. package/default-themes/solar-flare-dusk.json +19 -0
  63. package/default-themes/solar-flare-dusk.v3.css +200 -0
  64. package/dist/agents/index.d.ts +1 -1
  65. package/dist/agents/index.d.ts.map +1 -1
  66. package/dist/agents/index.js +2 -1
  67. package/dist/agents/index.js.map +1 -1
  68. package/dist/agents/openclaw/gatewayManager.d.ts +4 -0
  69. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -1
  70. package/dist/agents/openclaw/gatewayManager.js +27 -11
  71. package/dist/agents/openclaw/gatewayManager.js.map +1 -1
  72. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -1
  73. package/dist/agents/openclaw/openclawProvider.js +2 -4
  74. package/dist/agents/openclaw/openclawProvider.js.map +1 -1
  75. package/dist/agents/openclaw/sshTunnelManager.d.ts +2 -0
  76. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -1
  77. package/dist/agents/openclaw/sshTunnelManager.js +31 -12
  78. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -1
  79. package/dist/builders/anthropic.d.ts +31 -0
  80. package/dist/builders/anthropic.d.ts.map +1 -0
  81. package/dist/builders/anthropic.js +227 -0
  82. package/dist/builders/anthropic.js.map +1 -0
  83. package/dist/builders/fireworksai.d.ts +9 -0
  84. package/dist/builders/fireworksai.d.ts.map +1 -0
  85. package/dist/builders/fireworksai.js +57 -0
  86. package/dist/builders/fireworksai.js.map +1 -0
  87. package/dist/builders/index.d.ts +13 -0
  88. package/dist/builders/index.d.ts.map +1 -0
  89. package/dist/builders/index.js +31 -0
  90. package/dist/builders/index.js.map +1 -0
  91. package/dist/builders/openai.d.ts +8 -0
  92. package/dist/builders/openai.d.ts.map +1 -0
  93. package/dist/builders/openai.js +87 -0
  94. package/dist/builders/openai.js.map +1 -0
  95. package/dist/builders/types.d.ts +54 -0
  96. package/dist/builders/types.d.ts.map +1 -0
  97. package/dist/builders/types.js +211 -0
  98. package/dist/builders/types.js.map +1 -0
  99. package/dist/connectors/index.d.ts.map +1 -1
  100. package/dist/connectors/index.js +3 -2
  101. package/dist/connectors/index.js.map +1 -1
  102. package/dist/connectors/registry.d.ts +2 -1
  103. package/dist/connectors/registry.d.ts.map +1 -1
  104. package/dist/connectors/registry.js +31 -8
  105. package/dist/connectors/registry.js.map +1 -1
  106. package/dist/customizer/Customizer.d.ts +57 -0
  107. package/dist/customizer/Customizer.d.ts.map +1 -0
  108. package/dist/customizer/Customizer.js +124 -0
  109. package/dist/customizer/Customizer.js.map +1 -0
  110. package/dist/customizer/index.d.ts.map +1 -0
  111. package/dist/customizer/index.js +9 -0
  112. package/dist/customizer/index.js.map +1 -0
  113. package/dist/files.d.ts +16 -0
  114. package/dist/files.d.ts.map +1 -1
  115. package/dist/files.js +60 -1
  116. package/dist/files.js.map +1 -1
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +1 -0
  119. package/dist/index.js.map +1 -1
  120. package/dist/init.d.ts +10 -6
  121. package/dist/init.d.ts.map +1 -1
  122. package/dist/init.js +96 -113
  123. package/dist/init.js.map +1 -1
  124. package/dist/migrations.d.ts.map +1 -1
  125. package/dist/migrations.js +23 -10
  126. package/dist/migrations.js.map +1 -1
  127. package/dist/models/anthropic.d.ts +4 -2
  128. package/dist/models/anthropic.d.ts.map +1 -1
  129. package/dist/models/anthropic.js +33 -6
  130. package/dist/models/anthropic.js.map +1 -1
  131. package/dist/models/fireworksai.d.ts.map +1 -1
  132. package/dist/models/fireworksai.js +9 -1
  133. package/dist/models/fireworksai.js.map +1 -1
  134. package/dist/models/index.d.ts +1 -1
  135. package/dist/models/index.d.ts.map +1 -1
  136. package/dist/models/index.js +2 -1
  137. package/dist/models/index.js.map +1 -1
  138. package/dist/models/openai.d.ts +1 -1
  139. package/dist/models/openai.d.ts.map +1 -1
  140. package/dist/models/openai.js +24 -3
  141. package/dist/models/openai.js.map +1 -1
  142. package/dist/models/types.d.ts +20 -1
  143. package/dist/models/types.d.ts.map +1 -1
  144. package/dist/models/types.js +6 -1
  145. package/dist/models/types.js.map +1 -1
  146. package/dist/pages.d.ts +30 -7
  147. package/dist/pages.d.ts.map +1 -1
  148. package/dist/pages.js +177 -55
  149. package/dist/pages.js.map +1 -1
  150. package/dist/service/server.d.ts.map +1 -1
  151. package/dist/service/server.js +37 -8
  152. package/dist/service/server.js.map +1 -1
  153. package/dist/service/transformPage.d.ts +47 -20
  154. package/dist/service/transformPage.d.ts.map +1 -1
  155. package/dist/service/transformPage.js +514 -293
  156. package/dist/service/transformPage.js.map +1 -1
  157. package/dist/service/useAgentRoutes.d.ts +2 -1
  158. package/dist/service/useAgentRoutes.d.ts.map +1 -1
  159. package/dist/service/useAgentRoutes.js +5 -2
  160. package/dist/service/useAgentRoutes.js.map +1 -1
  161. package/dist/service/useApiRoutes.d.ts.map +1 -1
  162. package/dist/service/useApiRoutes.js +237 -136
  163. package/dist/service/useApiRoutes.js.map +1 -1
  164. package/dist/service/useConnectorRoutes.js +6 -6
  165. package/dist/service/useConnectorRoutes.js.map +1 -1
  166. package/dist/service/useFileRoutes.d.ts +4 -0
  167. package/dist/service/useFileRoutes.d.ts.map +1 -0
  168. package/dist/service/useFileRoutes.js +122 -0
  169. package/dist/service/useFileRoutes.js.map +1 -0
  170. package/dist/service/usePageRoutes.d.ts.map +1 -1
  171. package/dist/service/usePageRoutes.js +648 -67
  172. package/dist/service/usePageRoutes.js.map +1 -1
  173. package/dist/service/useSharedDataRoutes.d.ts +4 -0
  174. package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
  175. package/dist/service/useSharedDataRoutes.js +104 -0
  176. package/dist/service/useSharedDataRoutes.js.map +1 -0
  177. package/dist/service/useSharedFileRoutes.d.ts +4 -0
  178. package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
  179. package/dist/service/useSharedFileRoutes.js +121 -0
  180. package/dist/service/useSharedFileRoutes.js.map +1 -0
  181. package/dist/settings.d.ts +1 -0
  182. package/dist/settings.d.ts.map +1 -1
  183. package/dist/settings.js +1 -0
  184. package/dist/settings.js.map +1 -1
  185. package/dist/synthos-cli.d.ts.map +1 -1
  186. package/dist/synthos-cli.js +4 -3
  187. package/dist/synthos-cli.js.map +1 -1
  188. package/dist/themes.d.ts +1 -0
  189. package/dist/themes.d.ts.map +1 -1
  190. package/dist/themes.js +28 -15
  191. package/dist/themes.js.map +1 -1
  192. package/migration-rules/v1-to-v2.md +193 -0
  193. package/migration-rules/v2-to-v3.md +481 -0
  194. package/package.json +11 -10
  195. package/required-pages/builder/page.html +43 -0
  196. package/required-pages/builder/page.json +10 -0
  197. package/required-pages/{pages.html → pages/page.html} +238 -233
  198. package/required-pages/pages/page.json +10 -0
  199. package/required-pages/{settings.html → settings/page.html} +389 -275
  200. package/required-pages/settings/page.json +10 -0
  201. package/required-pages/synthos_apis/page.html +846 -0
  202. package/required-pages/synthos_apis/page.json +10 -0
  203. package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
  204. package/required-pages/synthos_scripts/page.json +10 -0
  205. package/src/agents/index.ts +1 -1
  206. package/src/agents/openclaw/gatewayManager.ts +22 -11
  207. package/src/agents/openclaw/openclawProvider.ts +2 -4
  208. package/src/agents/openclaw/sshTunnelManager.ts +19 -11
  209. package/src/builders/anthropic.ts +283 -0
  210. package/src/builders/fireworksai.ts +59 -0
  211. package/src/builders/index.ts +33 -0
  212. package/src/builders/openai.ts +89 -0
  213. package/src/builders/types.ts +261 -0
  214. package/src/connectors/index.ts +1 -1
  215. package/src/connectors/registry.ts +28 -8
  216. package/src/customizer/Customizer.ts +151 -0
  217. package/src/customizer/index.ts +5 -0
  218. package/src/files.ts +57 -0
  219. package/src/index.ts +2 -1
  220. package/src/init.ts +137 -123
  221. package/src/migrations.ts +30 -10
  222. package/src/models/anthropic.ts +40 -10
  223. package/src/models/fireworksai.ts +9 -2
  224. package/src/models/index.ts +1 -1
  225. package/src/models/openai.ts +26 -6
  226. package/src/models/types.ts +31 -1
  227. package/src/pages.ts +176 -54
  228. package/src/service/server.ts +36 -9
  229. package/src/service/transformPage.ts +557 -326
  230. package/src/service/useAgentRoutes.ts +7 -2
  231. package/src/service/useApiRoutes.ts +150 -41
  232. package/src/service/useConnectorRoutes.ts +7 -7
  233. package/src/service/useFileRoutes.ts +127 -0
  234. package/src/service/usePageRoutes.ts +720 -73
  235. package/src/service/useSharedDataRoutes.ts +106 -0
  236. package/src/service/useSharedFileRoutes.ts +126 -0
  237. package/src/settings.ts +2 -0
  238. package/src/synthos-cli.ts +4 -3
  239. package/src/themes.ts +25 -14
  240. package/static-files/favicon.svg +12 -0
  241. package/static-files/fluentlm-instructions.llmd +868 -0
  242. package/static-files/fluentlm-instructions.md +1595 -0
  243. package/static-files/fluentlm.css +4844 -0
  244. package/static-files/fluentlm.js +3602 -0
  245. package/static-files/fluentlm.min.css +1 -0
  246. package/static-files/fluentlm.min.js +1 -0
  247. package/{page-scripts/helpers-v2.js → static-files/helpers.v3.js} +82 -0
  248. package/static-files/page.v3.js +1290 -0
  249. package/static-files/recommended-frameworks.llmd +81 -0
  250. package/static-files/recommended-frameworks.md +137 -0
  251. package/static-files/retro-game.js +877 -0
  252. package/static-files/shell.css +797 -0
  253. package/static-files/theme-dark.css +169 -0
  254. package/static-files/theme-light.css +169 -0
  255. package/tests/builders.spec.ts +139 -0
  256. package/tests/pages.spec.ts +8 -8
  257. package/tests/transformPage.spec.ts +299 -360
  258. package/default-pages/application.html +0 -40
  259. package/default-pages/application.json +0 -1
  260. package/default-pages/json_tools.json +0 -1
  261. package/default-pages/my_notes.html +0 -33
  262. package/default-pages/neon_asteroids.html +0 -77
  263. package/default-pages/sidebar_page.json +0 -1
  264. package/default-pages/solar_tutorial.json +0 -1
  265. package/default-pages/two-panel_page.json +0 -1
  266. package/dist/agents/a2a/a2aProvider.d.ts +0 -3
  267. package/dist/agents/discovery.d.ts +0 -30
  268. package/dist/agents/openclaw/openclawProvider.d.ts +0 -3
  269. package/dist/agents/types.d.ts +0 -64
  270. package/dist/connectors/index.d.ts +0 -3
  271. package/dist/connectors/types.d.ts +0 -84
  272. package/dist/index.d.ts +0 -7
  273. package/dist/migrations.d.ts +0 -12
  274. package/dist/models/chainOfThought.d.ts +0 -12
  275. package/dist/models/fireworksai.d.ts +0 -30
  276. package/dist/models/logCompletePrompt.d.ts +0 -3
  277. package/dist/models/providers.d.ts +0 -8
  278. package/dist/models/utils.d.ts +0 -6
  279. package/dist/scripts.d.ts +0 -15
  280. package/dist/service/createCompletePrompt.d.ts +0 -5
  281. package/dist/service/debugLog.d.ts +0 -11
  282. package/dist/service/generateImage.d.ts +0 -32
  283. package/dist/service/index.d.ts +0 -8
  284. package/dist/service/modelInstructions.d.ts +0 -7
  285. package/dist/service/requiresSettings.d.ts +0 -3
  286. package/dist/service/server.d.ts +0 -4
  287. package/dist/service/useApiRoutes.d.ts +0 -4
  288. package/dist/service/useConnectorRoutes.d.ts +0 -4
  289. package/dist/service/useDataRoutes.d.ts +0 -4
  290. package/dist/service/useGatewayRoutes.d.ts +0 -4
  291. package/dist/service/useGatewayRoutes.d.ts.map +0 -1
  292. package/dist/service/useGatewayRoutes.js +0 -168
  293. package/dist/service/useGatewayRoutes.js.map +0 -1
  294. package/dist/service/usePageRoutes.d.ts +0 -5
  295. package/dist/synthos-cli.d.ts +0 -2
  296. package/page-scripts/page-v2.js +0 -656
  297. package/required-pages/builder.html +0 -48
  298. package/required-pages/builder.json +0 -1
  299. package/required-pages/pages.json +0 -1
  300. package/required-pages/settings.json +0 -1
  301. package/required-pages/synthos_apis.html +0 -327
  302. package/required-pages/synthos_apis.json +0 -1
  303. package/required-pages/synthos_scripts.json +0 -1
  304. package/src/connectors/airtable/connector.json +0 -27
  305. package/src/connectors/alpha-vantage/connector.json +0 -26
  306. package/src/connectors/brave-search/connector.json +0 -26
  307. package/src/connectors/cloudinary/connector.json +0 -27
  308. package/src/connectors/deepl/connector.json +0 -28
  309. package/src/connectors/elevenlabs/connector.json +0 -30
  310. package/src/connectors/giphy/connector.json +0 -27
  311. package/src/connectors/github/connector.json +0 -29
  312. package/src/connectors/huggingface/connector.json +0 -27
  313. package/src/connectors/imgur/connector.json +0 -29
  314. package/src/connectors/instagram/connector.json +0 -43
  315. package/src/connectors/jira/connector.json +0 -28
  316. package/src/connectors/mapbox/connector.json +0 -26
  317. package/src/connectors/nasa/connector.json +0 -27
  318. package/src/connectors/newsapi/connector.json +0 -27
  319. package/src/connectors/notion/connector.json +0 -28
  320. package/src/connectors/open-exchange-rates/connector.json +0 -27
  321. package/src/connectors/openweathermap/connector.json +0 -26
  322. package/src/connectors/pexels/connector.json +0 -27
  323. package/src/connectors/resend/connector.json +0 -29
  324. package/src/connectors/rss2json/connector.json +0 -27
  325. package/src/connectors/sendgrid/connector.json +0 -27
  326. package/src/connectors/spoonacular/connector.json +0 -28
  327. package/src/connectors/stability-ai/connector.json +0 -27
  328. package/src/connectors/twilio/connector.json +0 -28
  329. package/src/connectors/unsplash/connector.json +0 -27
  330. package/src/connectors/wolfram-alpha/connector.json +0 -26
  331. package/src/connectors/youtube-data/connector.json +0 -30
  332. /package/{dist/connectors → service-connectors}/airtable/connector.json +0 -0
  333. /package/{dist/connectors → service-connectors}/alpha-vantage/connector.json +0 -0
  334. /package/{dist/connectors → service-connectors}/brave-search/connector.json +0 -0
  335. /package/{dist/connectors → service-connectors}/cloudinary/connector.json +0 -0
  336. /package/{dist/connectors → service-connectors}/deepl/connector.json +0 -0
  337. /package/{dist/connectors → service-connectors}/elevenlabs/connector.json +0 -0
  338. /package/{dist/connectors → service-connectors}/giphy/connector.json +0 -0
  339. /package/{dist/connectors → service-connectors}/github/connector.json +0 -0
  340. /package/{dist/connectors → service-connectors}/huggingface/connector.json +0 -0
  341. /package/{dist/connectors → service-connectors}/imgur/connector.json +0 -0
  342. /package/{dist/connectors → service-connectors}/instagram/connector.json +0 -0
  343. /package/{dist/connectors → service-connectors}/jira/connector.json +0 -0
  344. /package/{dist/connectors → service-connectors}/mapbox/connector.json +0 -0
  345. /package/{dist/connectors → service-connectors}/nasa/connector.json +0 -0
  346. /package/{dist/connectors → service-connectors}/newsapi/connector.json +0 -0
  347. /package/{dist/connectors → service-connectors}/notion/connector.json +0 -0
  348. /package/{dist/connectors → service-connectors}/open-exchange-rates/connector.json +0 -0
  349. /package/{dist/connectors → service-connectors}/openweathermap/connector.json +0 -0
  350. /package/{dist/connectors → service-connectors}/pexels/connector.json +0 -0
  351. /package/{dist/connectors → service-connectors}/resend/connector.json +0 -0
  352. /package/{dist/connectors → service-connectors}/rss2json/connector.json +0 -0
  353. /package/{dist/connectors → service-connectors}/sendgrid/connector.json +0 -0
  354. /package/{dist/connectors → service-connectors}/spoonacular/connector.json +0 -0
  355. /package/{dist/connectors → service-connectors}/stability-ai/connector.json +0 -0
  356. /package/{dist/connectors → service-connectors}/twilio/connector.json +0 -0
  357. /package/{dist/connectors → service-connectors}/unsplash/connector.json +0 -0
  358. /package/{dist/connectors → service-connectors}/wolfram-alpha/connector.json +0 -0
  359. /package/{dist/connectors → service-connectors}/youtube-data/connector.json +0 -0
@@ -1,5 +1,5 @@
1
1
  import OpenAI from 'openai';
2
- import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError } from './types';
2
+ import { AgentCompletion, completePrompt, PromptCompletionArgs, RequestError, isMultimodalContent } from './types';
3
3
 
4
4
  export interface OpenaiArgs {
5
5
  apiKey: string;
@@ -15,23 +15,43 @@ export interface OpenaiArgs {
15
15
  * Pure function — no SDK dependency.
16
16
  */
17
17
  export function buildOpenAIRequest(args: PromptCompletionArgs): {
18
- input: { role: string; content: string }[];
18
+ input: { role: string; content: string | any[] }[];
19
19
  text?: { format: any };
20
20
  } {
21
- const input: { role: string; content: string }[] = [];
21
+ const input: { role: string; content: string | any[] }[] = [];
22
22
  if (args.history) {
23
23
  for (const msg of args.history) {
24
24
  input.push({ role: msg.role, content: msg.content });
25
25
  }
26
26
  }
27
- input.push({ role: 'user', content: args.prompt.content });
27
+
28
+ // Build user content — multimodal when ContentBlock[] is provided
29
+ const promptContent = args.prompt.content;
30
+ if (isMultimodalContent(promptContent)) {
31
+ const parts: any[] = promptContent.map(block => {
32
+ if (block.type === 'text') {
33
+ return { type: 'input_text', text: block.text };
34
+ }
35
+ return {
36
+ type: 'input_image',
37
+ image_url: `data:${block.mediaType};base64,${block.data}`,
38
+ };
39
+ });
40
+ input.push({ role: 'user', content: parts });
41
+ } else {
42
+ input.push({ role: 'user', content: promptContent });
43
+ }
28
44
 
29
45
  if (args.jsonMode || args.jsonSchema) {
30
- const inputText = input.map(m => m.content).join(' ');
46
+ const inputText = input.map(m => typeof m.content === 'string' ? m.content : '').join(' ');
31
47
  if (!/json/i.test(inputText)) {
32
48
  const last = input[input.length - 1];
33
49
  if (last) {
34
- last.content += '\nReturn JSON.';
50
+ if (typeof last.content === 'string') {
51
+ last.content += '\nReturn JSON.';
52
+ } else if (Array.isArray(last.content)) {
53
+ last.content.push({ type: 'input_text', text: '\nReturn JSON.' });
54
+ }
35
55
  }
36
56
  }
37
57
  }
@@ -25,6 +25,30 @@ export interface Provider {
25
25
  detectModel(model: string): boolean;
26
26
  }
27
27
 
28
+ // ---------------------------------------------------------------------------
29
+ // Multimodal content
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export interface TextBlock {
33
+ type: 'text';
34
+ text: string;
35
+ }
36
+
37
+ export interface ImageBlock {
38
+ type: 'image';
39
+ mediaType: string;
40
+ data: string;
41
+ }
42
+
43
+ export type ContentBlock = TextBlock | ImageBlock;
44
+
45
+ export type MessageContent = string | ContentBlock[];
46
+
47
+ /** Type guard: returns true when content is a multimodal ContentBlock array. */
48
+ export function isMultimodalContent(content: MessageContent): content is ContentBlock[] {
49
+ return Array.isArray(content);
50
+ }
51
+
28
52
  // ---------------------------------------------------------------------------
29
53
  // Messages
30
54
  // ---------------------------------------------------------------------------
@@ -39,8 +63,10 @@ export interface SystemMessage extends Message {
39
63
  role: 'system';
40
64
  }
41
65
 
42
- export interface UserMessage extends Message {
66
+ export interface UserMessage {
43
67
  role: 'user';
68
+ content: MessageContent;
69
+ name?: string;
44
70
  }
45
71
 
46
72
  // ---------------------------------------------------------------------------
@@ -61,6 +87,10 @@ export interface PromptCompletionArgs {
61
87
  jsonMode?: boolean;
62
88
  /** JSON schema for structured output. When provided, the model is asked to return JSON conforming to this schema. */
63
89
  jsonSchema?: Record<string, unknown>;
90
+ /** When true, system content is wrapped with cache_control for Anthropic prompt caching. */
91
+ cacheSystem?: boolean;
92
+ /** JSON schema for structured output via constrained decoding (Anthropic output_config). */
93
+ outputSchema?: Record<string, unknown>;
64
94
  }
65
95
 
66
96
  export type completePrompt<TValue = string> = (args: PromptCompletionArgs) => Promise<AgentCompletion<TValue>>;
package/src/pages.ts CHANGED
@@ -1,12 +1,28 @@
1
- import {checkIfExists, deleteFile, deleteFolder, ensureFolderExists, listFiles, listFolders, loadFile, saveFile} from './files';
1
+ import {checkIfExists, copyFolderRecursive, deleteFile, deleteFolder, ensureFolderExists, listFiles, listFolders, loadFile, saveFile} from './files';
2
2
  import path from 'path';
3
3
 
4
- // Page State Cache
5
- const _pages: { [name: string]: string } = {};
6
-
7
- export const REQUIRED_PAGES = ['builder', 'pages', 'settings', 'apis', 'scripts'];
4
+ /**
5
+ * Derive the list of required page names by scanning *.html files
6
+ * across one or more requiredPages folders.
7
+ */
8
+ export async function getRequiredPages(requiredPagesFolders: string[]): Promise<string[]> {
9
+ const result: string[] = [];
10
+ const seen = new Set<string>();
11
+ for (const folder of requiredPagesFolders) {
12
+ if (!await checkIfExists(folder)) continue;
13
+ const entries = await listFolders(folder);
14
+ for (const entry of entries) {
15
+ if (seen.has(entry)) continue;
16
+ if (await checkIfExists(path.join(folder, entry, 'page.html'))) {
17
+ seen.add(entry);
18
+ result.push(entry);
19
+ }
20
+ }
21
+ }
22
+ return result;
23
+ }
8
24
 
9
- export const PAGE_VERSION = 2;
25
+ export const PAGE_VERSION = 3;
10
26
 
11
27
  export interface PageInfo {
12
28
  name: string;
@@ -22,8 +38,8 @@ export interface PageInfo {
22
38
 
23
39
  export type PageMetadata = Omit<PageInfo, 'name'>;
24
40
 
25
- export async function loadPageMetadata(pagesFolder: string, name: string, fallbackFolder?: string): Promise<PageMetadata | undefined> {
26
- // 1. Try user override: .synthos/pages/<name>/page.json
41
+ export async function loadPageMetadata(pagesFolder: string, name: string, fallbackFolders?: string[]): Promise<PageMetadata | undefined> {
42
+ // 1. Try user override: <localFolder>/pages/<name>/page.json
27
43
  const metadataPath = path.join(pagesFolder, 'pages', name, 'page.json');
28
44
  if (await checkIfExists(metadataPath)) {
29
45
  try {
@@ -35,16 +51,18 @@ export async function loadPageMetadata(pagesFolder: string, name: string, fallba
35
51
  }
36
52
  }
37
53
 
38
- // 2. Try fallback: fallbackFolder/<name>.json
39
- if (fallbackFolder) {
40
- const fallbackPath = path.join(fallbackFolder, `${name}.json`);
41
- if (await checkIfExists(fallbackPath)) {
42
- try {
43
- const raw = await loadFile(fallbackPath);
44
- const parsed = JSON.parse(raw);
45
- return parseMetadata(parsed);
46
- } catch {
47
- // fall through
54
+ // 2. Try fallback folders: fallbackFolder/<name>/page.json
55
+ if (fallbackFolders) {
56
+ for (const folder of fallbackFolders) {
57
+ const candidate = path.join(folder, name, 'page.json');
58
+ if (await checkIfExists(candidate)) {
59
+ try {
60
+ const raw = await loadFile(candidate);
61
+ const parsed = JSON.parse(raw);
62
+ return parseMetadata(parsed);
63
+ } catch {
64
+ // fall through
65
+ }
48
66
  }
49
67
  }
50
68
  }
@@ -83,7 +101,7 @@ const DEFAULT_METADATA: PageMetadata = {
83
101
  mode: 'unlocked',
84
102
  };
85
103
 
86
- export async function listPages(pagesFolder: string, fallbackPagesFolder: string): Promise<PageInfo[]> {
104
+ export async function listPages(pagesFolder: string, fallbackPagesFolders: string[]): Promise<PageInfo[]> {
87
105
  const pageMap = new Map<string, PageInfo>();
88
106
 
89
107
  // Folder-based pages under pages/ subdirectory
@@ -128,13 +146,15 @@ export async function listPages(pagesFolder: string, fallbackPagesFolder: string
128
146
  }
129
147
  }
130
148
 
131
- // Add pages from fallback (required) pages folder
132
- const fallbackFiles = (await listFiles(fallbackPagesFolder)).filter(file => file.endsWith('.html'));
133
- for (const file of fallbackFiles) {
134
- const name = file.replace(/\.html$/, '');
135
- if (!pageMap.has(name)) {
136
- // System page not yet in map — check for user override, then fallback .json
137
- const metadata = await loadPageMetadata(pagesFolder, name, fallbackPagesFolder);
149
+ // Add pages from fallback (required) pages folders
150
+ for (const folder of fallbackPagesFolders) {
151
+ if (!await checkIfExists(folder)) continue;
152
+ const dirs = await listFolders(folder);
153
+ for (const name of dirs) {
154
+ if (pageMap.has(name)) continue;
155
+ if (!await checkIfExists(path.join(folder, name, 'page.html'))) continue;
156
+ // System page not yet in map — check for user override, then fallback page.json
157
+ const metadata = await loadPageMetadata(pagesFolder, name, fallbackPagesFolders);
138
158
  pageMap.set(name, {
139
159
  name,
140
160
  title: metadata?.title ?? '',
@@ -156,22 +176,27 @@ export async function listPages(pagesFolder: string, fallbackPagesFolder: string
156
176
  return entries;
157
177
  }
158
178
 
159
- export async function loadPageState(pagesFolder: string, name: string, reset: boolean): Promise<string|undefined> {
160
- if (!_pages[name] || reset) {
161
- // Try folder-based path under pages/ first, then fall back to flat file
162
- const folderPath = path.join(pagesFolder, 'pages', name, 'page.html');
163
- const flatPath = path.join(pagesFolder, `${name}.html`);
164
-
165
- if (await checkIfExists(folderPath)) {
166
- _pages[name] = await loadFile(folderPath);
167
- } else if (await checkIfExists(flatPath)) {
168
- _pages[name] = await loadFile(flatPath);
169
- } else {
170
- return undefined;
171
- }
179
+ export async function loadPageState(pagesFolder: string, name: string): Promise<string|undefined> {
180
+ // Check for working-state version files first
181
+ const latestVersion = await getLatestVersion(pagesFolder, name);
182
+ if (latestVersion > 0) {
183
+ const versionHtml = await loadPageVersion(pagesFolder, name, latestVersion);
184
+ if (versionHtml) return versionHtml;
172
185
  }
173
186
 
174
- return _pages[name];
187
+ // Fall back to saved baseline
188
+ const folderPath = path.join(pagesFolder, 'pages', name, 'page.html');
189
+ const directFolderPath = path.join(pagesFolder, name, 'page.html');
190
+ const flatPath = path.join(pagesFolder, `${name}.html`);
191
+
192
+ if (await checkIfExists(folderPath)) {
193
+ return loadFile(folderPath);
194
+ } else if (await checkIfExists(directFolderPath)) {
195
+ return loadFile(directFolderPath);
196
+ } else if (await checkIfExists(flatPath)) {
197
+ return loadFile(flatPath);
198
+ }
199
+ return undefined;
175
200
  }
176
201
 
177
202
  export function normalizePageName(name: string|undefined): string|undefined {
@@ -179,7 +204,6 @@ export function normalizePageName(name: string|undefined): string|undefined {
179
204
  }
180
205
 
181
206
  export async function savePageState(pagesFolder: string, name: string, content: string, title?: string, categories?: string[]): Promise<void> {
182
- _pages[name] = content;
183
207
  const pageFolder = path.join(pagesFolder, 'pages', name);
184
208
  await ensureFolderExists(pageFolder);
185
209
  await saveFile(path.join(pageFolder, 'page.html'), content);
@@ -202,10 +226,6 @@ export async function savePageState(pagesFolder: string, name: string, content:
202
226
  }
203
227
  }
204
228
 
205
- export function updatePageState(name: string, content: string): void {
206
- _pages[name] = content;
207
- }
208
-
209
229
  export async function deletePage(pagesFolder: string, name: string): Promise<void> {
210
230
  // Delete folder-based page: <pagesFolder>/pages/<name>/
211
231
  const folderPath = path.join(pagesFolder, 'pages', name);
@@ -219,8 +239,65 @@ export async function deletePage(pagesFolder: string, name: string): Promise<voi
219
239
  await deleteFile(flatPath);
220
240
  }
221
241
 
222
- // Clear in-memory cache
223
- delete _pages[name];
242
+ }
243
+
244
+ // ---------------------------------------------------------------------------
245
+ // Page versioning — per-edit version snapshots for undo support
246
+ // ---------------------------------------------------------------------------
247
+
248
+ /**
249
+ * Save a version snapshot: <pagesFolder>/pages/<name>/page.v<version>.html
250
+ */
251
+ export async function savePageVersion(pagesFolder: string, name: string, version: number, html: string): Promise<void> {
252
+ const pageFolder = path.join(pagesFolder, 'pages', name);
253
+ await ensureFolderExists(pageFolder);
254
+ await saveFile(path.join(pageFolder, `page.v${version}.html`), html);
255
+ }
256
+
257
+ /**
258
+ * Load a version snapshot (returns undefined if the file doesn't exist).
259
+ */
260
+ export async function loadPageVersion(pagesFolder: string, name: string, version: number): Promise<string | undefined> {
261
+ const filePath = path.join(pagesFolder, 'pages', name, `page.v${version}.html`);
262
+ if (!await checkIfExists(filePath)) return undefined;
263
+ return loadFile(filePath);
264
+ }
265
+
266
+ /**
267
+ * Scan page.v*.html files and return the highest version number (0 if none).
268
+ */
269
+ export async function getLatestVersion(pagesFolder: string, name: string): Promise<number> {
270
+ const pageFolder = path.join(pagesFolder, 'pages', name);
271
+ if (!await checkIfExists(pageFolder)) return 0;
272
+ const files = await listFiles(pageFolder);
273
+ let max = 0;
274
+ for (const file of files) {
275
+ const match = file.match(/^page\.v(\d+)\.html$/);
276
+ if (match) {
277
+ const v = parseInt(match[1], 10);
278
+ if (v > max) max = v;
279
+ }
280
+ }
281
+ return max;
282
+ }
283
+
284
+ /**
285
+ * Delete all page.v*.html version files for a page.
286
+ */
287
+ export async function clearVersions(pagesFolder: string, name: string): Promise<void> {
288
+ const pageFolder = path.join(pagesFolder, 'pages', name);
289
+ if (!await checkIfExists(pageFolder)) return;
290
+ const files = await listFiles(pageFolder);
291
+ for (const file of files) {
292
+ if (/^page\.v\d+\.html$/.test(file)) {
293
+ await deleteFile(path.join(pageFolder, file));
294
+ }
295
+ }
296
+ }
297
+
298
+ export interface CopyPageOptions {
299
+ copyTables?: boolean; // default false
300
+ copyFiles?: boolean; // default true
224
301
  }
225
302
 
226
303
  export async function copyPage(
@@ -229,14 +306,36 @@ export async function copyPage(
229
306
  targetName: string,
230
307
  title: string,
231
308
  categories: string[],
232
- requiredPagesFolder: string
309
+ requiredPagesFolders: string[],
310
+ options?: CopyPageOptions
233
311
  ): Promise<void> {
234
- // Load source HTML from user folder, then try required folder as fallback
235
- let html = await loadPageState(pagesFolder, sourceName, true);
236
- if (!html) {
237
- const requiredPath = path.join(requiredPagesFolder, `${sourceName}.html`);
238
- if (await checkIfExists(requiredPath)) {
239
- html = await loadFile(requiredPath);
312
+ const copyTables = options?.copyTables ?? false;
313
+ const copyFiles = options?.copyFiles ?? true;
314
+
315
+ // Resolve source page folder: user pages first, then required pages
316
+ let sourceFolder: string | undefined;
317
+ const userSourceFolder = path.join(pagesFolder, 'pages', sourceName);
318
+ if (await checkIfExists(path.join(userSourceFolder, 'page.html'))) {
319
+ sourceFolder = userSourceFolder;
320
+ } else {
321
+ for (const folder of requiredPagesFolders) {
322
+ const candidate = path.join(folder, sourceName);
323
+ if (await checkIfExists(path.join(candidate, 'page.html'))) {
324
+ sourceFolder = candidate;
325
+ break;
326
+ }
327
+ }
328
+ }
329
+
330
+ // Load source HTML
331
+ let html: string | undefined;
332
+ if (sourceFolder) {
333
+ html = await loadFile(path.join(sourceFolder, 'page.html'));
334
+ } else {
335
+ // Try legacy flat file
336
+ const flatPath = path.join(pagesFolder, `${sourceName}.html`);
337
+ if (await checkIfExists(flatPath)) {
338
+ html = await loadFile(flatPath);
240
339
  }
241
340
  }
242
341
 
@@ -260,4 +359,27 @@ export async function copyPage(
260
359
  mode: 'unlocked',
261
360
  };
262
361
  await savePageMetadata(pagesFolder, targetName, metadata);
362
+
363
+ // Copy additional content from source if a folder was resolved
364
+ if (sourceFolder) {
365
+ const targetFolder = path.join(pagesFolder, 'pages', targetName);
366
+
367
+ if (copyTables) {
368
+ const entries = await listFolders(sourceFolder);
369
+ for (const entry of entries) {
370
+ if (entry === 'files') continue; // handled separately
371
+ await copyFolderRecursive(
372
+ path.join(sourceFolder, entry),
373
+ path.join(targetFolder, entry)
374
+ );
375
+ }
376
+ }
377
+
378
+ if (copyFiles) {
379
+ const filesDir = path.join(sourceFolder, 'files');
380
+ if (await checkIfExists(filesDir)) {
381
+ await copyFolderRecursive(filesDir, path.join(targetFolder, 'files'));
382
+ }
383
+ }
384
+ }
263
385
  }
@@ -3,11 +3,15 @@ import { usePageRoutes } from './usePageRoutes';
3
3
  import { useApiRoutes } from './useApiRoutes';
4
4
  import { SynthOSConfig } from '../init';
5
5
  import { useDataRoutes } from './useDataRoutes';
6
+ import { useFileRoutes } from './useFileRoutes';
7
+ import { useSharedDataRoutes } from './useSharedDataRoutes';
8
+ import { useSharedFileRoutes } from './useSharedFileRoutes';
6
9
  import { useConnectorRoutes } from './useConnectorRoutes';
7
10
  import { useAgentRoutes } from './useAgentRoutes';
8
11
  import { cyan, yellow, formatTime } from './debugLog';
12
+ import { customizer as defaultCustomizer, Customizer } from '../customizer';
9
13
 
10
- export function server(config: SynthOSConfig): Application {
14
+ export function server(config: SynthOSConfig, customizer: Customizer = defaultCustomizer): Application {
11
15
  const app = express();
12
16
 
13
17
  // Debug request-logging middleware
@@ -25,23 +29,46 @@ export function server(config: SynthOSConfig): Application {
25
29
  // Middleware to parse URL-encoded data (form data)
26
30
  app.use(express.urlencoded({ extended: true }));
27
31
 
28
- // Middleware to parse JSON data
29
- app.use(express.json());
32
+ // Middleware to parse JSON data (10 MB limit for image attachments)
33
+ app.use(express.json({ limit: '10mb' }));
34
+
35
+ // Favicon — serve from static files
36
+ app.get('/favicon.ico', (_req, res) => res.redirect('/static/favicon.svg'));
37
+ app.get('/favicon.svg', (_req, res) => res.redirect('/static/favicon.svg'));
38
+
39
+ // Serve static files from staticFilesFolders (first folder wins)
40
+ for (const folder of config.staticFilesFolders) {
41
+ app.use('/static', express.static(folder, { maxAge: '1h' }));
42
+ }
30
43
 
31
44
  // Page handling routes
32
- usePageRoutes(config, app);
45
+ if (customizer.isEnabled('pages')) usePageRoutes(config, app, customizer);
33
46
 
34
47
  // API routes
35
- useApiRoutes(config, app);
48
+ if (customizer.isEnabled('api')) useApiRoutes(config, app, customizer);
36
49
 
37
50
  // Connector routes
38
- useConnectorRoutes(config, app);
51
+ if (customizer.isEnabled('connectors')) useConnectorRoutes(config, app);
39
52
 
40
53
  // Agent routes
41
- useAgentRoutes(config, app);
54
+ if (customizer.isEnabled('agents')) useAgentRoutes(config, app, customizer);
42
55
 
43
56
  // Data routes
44
- useDataRoutes(config, app);
57
+ if (customizer.isEnabled('data')) useDataRoutes(config, app);
58
+
59
+ // File routes
60
+ if (customizer.isEnabled('files')) useFileRoutes(config, app);
61
+
62
+ // Shared data routes
63
+ if (customizer.isEnabled('shared-data')) useSharedDataRoutes(config, app);
64
+
65
+ // Shared file routes
66
+ if (customizer.isEnabled('shared-files')) useSharedFileRoutes(config, app);
67
+
68
+ // Custom routes from the Customizer
69
+ for (const installer of customizer.getExtraRoutes()) {
70
+ installer(config, app);
71
+ }
45
72
 
46
73
  return app;
47
- }
74
+ }