@imgly/codesign-mcp 0.1.6 → 0.1.7

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 (360) hide show
  1. package/README.md +5 -10
  2. package/dist/cli/http.d.ts.map +1 -1
  3. package/dist/cli/http.js +12 -2
  4. package/dist/cli/http.js.map +1 -1
  5. package/dist/cli/stdio.d.ts.map +1 -1
  6. package/dist/cli/stdio.js +103 -35
  7. package/dist/cli/stdio.js.map +1 -1
  8. package/dist/config.d.ts +11 -2
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +12 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/diagnostics/events.d.ts +6 -0
  13. package/dist/diagnostics/events.d.ts.map +1 -1
  14. package/dist/diagnostics/events.js.map +1 -1
  15. package/dist/engine/cesdk-adapter.d.ts +5 -1
  16. package/dist/engine/cesdk-adapter.d.ts.map +1 -1
  17. package/dist/engine/cesdk-adapter.js +8 -1
  18. package/dist/engine/cesdk-adapter.js.map +1 -1
  19. package/dist/engine/engine-capacity.d.ts +16 -0
  20. package/dist/engine/engine-capacity.d.ts.map +1 -0
  21. package/dist/engine/engine-capacity.js +48 -0
  22. package/dist/engine/engine-capacity.js.map +1 -0
  23. package/dist/engine/engine-slots.d.ts +48 -0
  24. package/dist/engine/engine-slots.d.ts.map +1 -0
  25. package/dist/engine/engine-slots.js +182 -0
  26. package/dist/engine/engine-slots.js.map +1 -0
  27. package/dist/tools/engine.d.ts +13 -0
  28. package/dist/tools/engine.d.ts.map +1 -1
  29. package/dist/tools/engine.js +28 -0
  30. package/dist/tools/engine.js.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/dashboard/router.d.ts +0 -3
  33. package/dist/dashboard/router.d.ts.map +0 -1
  34. package/dist/dashboard/router.js +0 -19
  35. package/dist/dashboard/router.js.map +0 -1
  36. package/dist/dashboard/spa-assets.generated.d.ts +0 -2
  37. package/dist/dashboard/spa-assets.generated.d.ts.map +0 -1
  38. package/dist/dashboard/spa-assets.generated.js +0 -220
  39. package/dist/dashboard/spa-assets.generated.js.map +0 -1
  40. package/dist/metrics/mount.d.ts +0 -11
  41. package/dist/metrics/mount.d.ts.map +0 -1
  42. package/dist/metrics/mount.js +0 -13
  43. package/dist/metrics/mount.js.map +0 -1
  44. package/dist/metrics/recorder.d.ts +0 -55
  45. package/dist/metrics/recorder.d.ts.map +0 -1
  46. package/dist/metrics/recorder.js +0 -97
  47. package/dist/metrics/recorder.js.map +0 -1
  48. package/dist/test/scripts/changelog-add.d.ts +0 -8
  49. package/dist/test/scripts/changelog-add.d.ts.map +0 -1
  50. package/dist/test/scripts/changelog-add.js +0 -50
  51. package/dist/test/scripts/changelog-add.js.map +0 -1
  52. package/dist/test/scripts/changelog-collate.d.ts +0 -38
  53. package/dist/test/scripts/changelog-collate.d.ts.map +0 -1
  54. package/dist/test/scripts/changelog-collate.js +0 -171
  55. package/dist/test/scripts/changelog-collate.js.map +0 -1
  56. package/dist/test/test/changelog.test.d.ts +0 -2
  57. package/dist/test/test/changelog.test.d.ts.map +0 -1
  58. package/dist/test/test/changelog.test.js +0 -293
  59. package/dist/test/test/changelog.test.js.map +0 -1
  60. package/dist/test/test/cli/dispatcher.test.d.ts +0 -2
  61. package/dist/test/test/cli/dispatcher.test.d.ts.map +0 -1
  62. package/dist/test/test/cli/dispatcher.test.js +0 -54
  63. package/dist/test/test/cli/dispatcher.test.js.map +0 -1
  64. package/dist/test/test/cli/redirect-console.test.d.ts +0 -2
  65. package/dist/test/test/cli/redirect-console.test.d.ts.map +0 -1
  66. package/dist/test/test/cli/redirect-console.test.js +0 -78
  67. package/dist/test/test/cli/redirect-console.test.js.map +0 -1
  68. package/dist/test/test/cli/resolve-license.test.d.ts +0 -2
  69. package/dist/test/test/cli/resolve-license.test.d.ts.map +0 -1
  70. package/dist/test/test/cli/resolve-license.test.js +0 -29
  71. package/dist/test/test/cli/resolve-license.test.js.map +0 -1
  72. package/dist/test/test/cli/stdio-diagnostics.test.d.ts +0 -2
  73. package/dist/test/test/cli/stdio-diagnostics.test.d.ts.map +0 -1
  74. package/dist/test/test/cli/stdio-diagnostics.test.js +0 -31
  75. package/dist/test/test/cli/stdio-diagnostics.test.js.map +0 -1
  76. package/dist/test/test/cli/stdio-license.test.d.ts +0 -2
  77. package/dist/test/test/cli/stdio-license.test.d.ts.map +0 -1
  78. package/dist/test/test/cli/stdio-license.test.js +0 -19
  79. package/dist/test/test/cli/stdio-license.test.js.map +0 -1
  80. package/dist/test/test/config.test.d.ts +0 -2
  81. package/dist/test/test/config.test.d.ts.map +0 -1
  82. package/dist/test/test/config.test.js +0 -95
  83. package/dist/test/test/config.test.js.map +0 -1
  84. package/dist/test/test/cross-ref.test.d.ts +0 -2
  85. package/dist/test/test/cross-ref.test.d.ts.map +0 -1
  86. package/dist/test/test/cross-ref.test.js +0 -39
  87. package/dist/test/test/cross-ref.test.js.map +0 -1
  88. package/dist/test/test/diagnostics/bundle.test.d.ts +0 -2
  89. package/dist/test/test/diagnostics/bundle.test.d.ts.map +0 -1
  90. package/dist/test/test/diagnostics/bundle.test.js +0 -53
  91. package/dist/test/test/diagnostics/bundle.test.js.map +0 -1
  92. package/dist/test/test/diagnostics/compose.test.d.ts +0 -2
  93. package/dist/test/test/diagnostics/compose.test.d.ts.map +0 -1
  94. package/dist/test/test/diagnostics/compose.test.js +0 -31
  95. package/dist/test/test/diagnostics/compose.test.js.map +0 -1
  96. package/dist/test/test/diagnostics/file-sink.test.d.ts +0 -2
  97. package/dist/test/test/diagnostics/file-sink.test.d.ts.map +0 -1
  98. package/dist/test/test/diagnostics/file-sink.test.js +0 -113
  99. package/dist/test/test/diagnostics/file-sink.test.js.map +0 -1
  100. package/dist/test/test/diagnostics/redact.test.d.ts +0 -2
  101. package/dist/test/test/diagnostics/redact.test.d.ts.map +0 -1
  102. package/dist/test/test/diagnostics/redact.test.js +0 -59
  103. package/dist/test/test/diagnostics/redact.test.js.map +0 -1
  104. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts +0 -2
  105. package/dist/test/test/diagnostics/tool-call-adapter.test.d.ts.map +0 -1
  106. package/dist/test/test/diagnostics/tool-call-adapter.test.js +0 -34
  107. package/dist/test/test/diagnostics/tool-call-adapter.test.js.map +0 -1
  108. package/dist/test/test/engine/asset-sources.test.d.ts +0 -2
  109. package/dist/test/test/engine/asset-sources.test.d.ts.map +0 -1
  110. package/dist/test/test/engine/asset-sources.test.js +0 -100
  111. package/dist/test/test/engine/asset-sources.test.js.map +0 -1
  112. package/dist/test/test/engine/cesdk-adapter.test.d.ts +0 -2
  113. package/dist/test/test/engine/cesdk-adapter.test.d.ts.map +0 -1
  114. package/dist/test/test/engine/cesdk-adapter.test.js +0 -32
  115. package/dist/test/test/engine/cesdk-adapter.test.js.map +0 -1
  116. package/dist/test/test/engine/mutex.test.d.ts +0 -2
  117. package/dist/test/test/engine/mutex.test.d.ts.map +0 -1
  118. package/dist/test/test/engine/mutex.test.js +0 -63
  119. package/dist/test/test/engine/mutex.test.js.map +0 -1
  120. package/dist/test/test/engine/session.test.d.ts +0 -2
  121. package/dist/test/test/engine/session.test.d.ts.map +0 -1
  122. package/dist/test/test/engine/session.test.js +0 -159
  123. package/dist/test/test/engine/session.test.js.map +0 -1
  124. package/dist/test/test/engine/unsplash-source.test.d.ts +0 -2
  125. package/dist/test/test/engine/unsplash-source.test.d.ts.map +0 -1
  126. package/dist/test/test/engine/unsplash-source.test.js +0 -129
  127. package/dist/test/test/engine/unsplash-source.test.js.map +0 -1
  128. package/dist/test/test/engine/uri-resolver.test.d.ts +0 -2
  129. package/dist/test/test/engine/uri-resolver.test.d.ts.map +0 -1
  130. package/dist/test/test/engine/uri-resolver.test.js +0 -38
  131. package/dist/test/test/engine/uri-resolver.test.js.map +0 -1
  132. package/dist/test/test/gateway/hosted-stub.test.d.ts +0 -2
  133. package/dist/test/test/gateway/hosted-stub.test.d.ts.map +0 -1
  134. package/dist/test/test/gateway/hosted-stub.test.js +0 -11
  135. package/dist/test/test/gateway/hosted-stub.test.js.map +0 -1
  136. package/dist/test/test/gateway/local.test.d.ts +0 -2
  137. package/dist/test/test/gateway/local.test.d.ts.map +0 -1
  138. package/dist/test/test/gateway/local.test.js +0 -29
  139. package/dist/test/test/gateway/local.test.js.map +0 -1
  140. package/dist/test/test/handbook-contract.test.d.ts +0 -2
  141. package/dist/test/test/handbook-contract.test.d.ts.map +0 -1
  142. package/dist/test/test/handbook-contract.test.js +0 -79
  143. package/dist/test/test/handbook-contract.test.js.map +0 -1
  144. package/dist/test/test/http/server.test.d.ts +0 -2
  145. package/dist/test/test/http/server.test.d.ts.map +0 -1
  146. package/dist/test/test/http/server.test.js +0 -338
  147. package/dist/test/test/http/server.test.js.map +0 -1
  148. package/dist/test/test/http/session-id-header.test.d.ts +0 -2
  149. package/dist/test/test/http/session-id-header.test.d.ts.map +0 -1
  150. package/dist/test/test/http/session-id-header.test.js +0 -76
  151. package/dist/test/test/http/session-id-header.test.js.map +0 -1
  152. package/dist/test/test/http/session-manager.test.d.ts +0 -2
  153. package/dist/test/test/http/session-manager.test.d.ts.map +0 -1
  154. package/dist/test/test/http/session-manager.test.js +0 -97
  155. package/dist/test/test/http/session-manager.test.js.map +0 -1
  156. package/dist/test/test/http/workspace-auth.test.d.ts +0 -2
  157. package/dist/test/test/http/workspace-auth.test.d.ts.map +0 -1
  158. package/dist/test/test/http/workspace-auth.test.js +0 -40
  159. package/dist/test/test/http/workspace-auth.test.js.map +0 -1
  160. package/dist/test/test/metrics/mount.test.d.ts +0 -2
  161. package/dist/test/test/metrics/mount.test.d.ts.map +0 -1
  162. package/dist/test/test/metrics/mount.test.js +0 -59
  163. package/dist/test/test/metrics/mount.test.js.map +0 -1
  164. package/dist/test/test/metrics/recorder.test.d.ts +0 -2
  165. package/dist/test/test/metrics/recorder.test.d.ts.map +0 -1
  166. package/dist/test/test/metrics/recorder.test.js +0 -118
  167. package/dist/test/test/metrics/recorder.test.js.map +0 -1
  168. package/dist/test/test/revisions/hash.test.d.ts +0 -2
  169. package/dist/test/test/revisions/hash.test.d.ts.map +0 -1
  170. package/dist/test/test/revisions/hash.test.js +0 -33
  171. package/dist/test/test/revisions/hash.test.js.map +0 -1
  172. package/dist/test/test/revisions/store.test.d.ts +0 -2
  173. package/dist/test/test/revisions/store.test.d.ts.map +0 -1
  174. package/dist/test/test/revisions/store.test.js +0 -315
  175. package/dist/test/test/revisions/store.test.js.map +0 -1
  176. package/dist/test/test/server.metrics.test.d.ts +0 -2
  177. package/dist/test/test/server.metrics.test.d.ts.map +0 -1
  178. package/dist/test/test/server.metrics.test.js +0 -87
  179. package/dist/test/test/server.metrics.test.js.map +0 -1
  180. package/dist/test/test/server.test.d.ts +0 -2
  181. package/dist/test/test/server.test.d.ts.map +0 -1
  182. package/dist/test/test/server.test.js +0 -146
  183. package/dist/test/test/server.test.js.map +0 -1
  184. package/dist/test/test/skill-tools.test.d.ts +0 -2
  185. package/dist/test/test/skill-tools.test.d.ts.map +0 -1
  186. package/dist/test/test/skill-tools.test.js +0 -84
  187. package/dist/test/test/skill-tools.test.js.map +0 -1
  188. package/dist/test/test/smoke.test.d.ts +0 -2
  189. package/dist/test/test/smoke.test.d.ts.map +0 -1
  190. package/dist/test/test/smoke.test.js +0 -8
  191. package/dist/test/test/smoke.test.js.map +0 -1
  192. package/dist/test/test/tools/asset-generate.test.d.ts +0 -2
  193. package/dist/test/test/tools/asset-generate.test.d.ts.map +0 -1
  194. package/dist/test/test/tools/asset-generate.test.js +0 -66
  195. package/dist/test/test/tools/asset-generate.test.js.map +0 -1
  196. package/dist/test/test/tools/asset-search.test.d.ts +0 -2
  197. package/dist/test/test/tools/asset-search.test.d.ts.map +0 -1
  198. package/dist/test/test/tools/asset-search.test.js +0 -121
  199. package/dist/test/test/tools/asset-search.test.js.map +0 -1
  200. package/dist/test/test/tools/edit-harness.d.ts +0 -14
  201. package/dist/test/test/tools/edit-harness.d.ts.map +0 -1
  202. package/dist/test/test/tools/edit-harness.js +0 -30
  203. package/dist/test/test/tools/edit-harness.js.map +0 -1
  204. package/dist/test/test/tools/edit.telemetry.test.d.ts +0 -2
  205. package/dist/test/test/tools/edit.telemetry.test.d.ts.map +0 -1
  206. package/dist/test/test/tools/edit.telemetry.test.js +0 -46
  207. package/dist/test/test/tools/edit.telemetry.test.js.map +0 -1
  208. package/dist/test/test/tools/edit.test.d.ts +0 -2
  209. package/dist/test/test/tools/edit.test.d.ts.map +0 -1
  210. package/dist/test/test/tools/edit.test.js +0 -267
  211. package/dist/test/test/tools/edit.test.js.map +0 -1
  212. package/dist/test/test/tools/export-harness.d.ts +0 -13
  213. package/dist/test/test/tools/export-harness.d.ts.map +0 -1
  214. package/dist/test/test/tools/export-harness.js +0 -27
  215. package/dist/test/test/tools/export-harness.js.map +0 -1
  216. package/dist/test/test/tools/export.telemetry.test.d.ts +0 -2
  217. package/dist/test/test/tools/export.telemetry.test.d.ts.map +0 -1
  218. package/dist/test/test/tools/export.telemetry.test.js +0 -65
  219. package/dist/test/test/tools/export.telemetry.test.js.map +0 -1
  220. package/dist/test/test/tools/export.test.d.ts +0 -2
  221. package/dist/test/test/tools/export.test.d.ts.map +0 -1
  222. package/dist/test/test/tools/export.test.js +0 -174
  223. package/dist/test/test/tools/export.test.js.map +0 -1
  224. package/dist/test/test/tools/history.test.d.ts +0 -2
  225. package/dist/test/test/tools/history.test.d.ts.map +0 -1
  226. package/dist/test/test/tools/history.test.js +0 -29
  227. package/dist/test/test/tools/history.test.js.map +0 -1
  228. package/dist/test/test/tools/import.test.d.ts +0 -2
  229. package/dist/test/test/tools/import.test.d.ts.map +0 -1
  230. package/dist/test/test/tools/import.test.js +0 -122
  231. package/dist/test/test/tools/import.test.js.map +0 -1
  232. package/dist/test/test/tools/index.test.d.ts +0 -2
  233. package/dist/test/test/tools/index.test.d.ts.map +0 -1
  234. package/dist/test/test/tools/index.test.js +0 -117
  235. package/dist/test/test/tools/index.test.js.map +0 -1
  236. package/dist/test/test/tools/inspect.test.d.ts +0 -2
  237. package/dist/test/test/tools/inspect.test.d.ts.map +0 -1
  238. package/dist/test/test/tools/inspect.test.js +0 -62
  239. package/dist/test/test/tools/inspect.test.js.map +0 -1
  240. package/dist/test/test/tools/list.test.d.ts +0 -2
  241. package/dist/test/test/tools/list.test.d.ts.map +0 -1
  242. package/dist/test/test/tools/list.test.js +0 -23
  243. package/dist/test/test/tools/list.test.js.map +0 -1
  244. package/dist/test/test/tools/load.integration.test.d.ts +0 -2
  245. package/dist/test/test/tools/load.integration.test.d.ts.map +0 -1
  246. package/dist/test/test/tools/load.integration.test.js +0 -165
  247. package/dist/test/test/tools/load.integration.test.js.map +0 -1
  248. package/dist/test/test/tools/load.test.d.ts +0 -2
  249. package/dist/test/test/tools/load.test.d.ts.map +0 -1
  250. package/dist/test/test/tools/load.test.js +0 -387
  251. package/dist/test/test/tools/load.test.js.map +0 -1
  252. package/dist/test/test/tools/metrics.test.d.ts +0 -2
  253. package/dist/test/test/tools/metrics.test.d.ts.map +0 -1
  254. package/dist/test/test/tools/metrics.test.js +0 -29
  255. package/dist/test/test/tools/metrics.test.js.map +0 -1
  256. package/dist/test/test/tools/open-editor.test.d.ts +0 -2
  257. package/dist/test/test/tools/open-editor.test.d.ts.map +0 -1
  258. package/dist/test/test/tools/open-editor.test.js +0 -53
  259. package/dist/test/test/tools/open-editor.test.js.map +0 -1
  260. package/dist/test/test/tools/preview-harness.d.ts +0 -9
  261. package/dist/test/test/tools/preview-harness.d.ts.map +0 -1
  262. package/dist/test/test/tools/preview-harness.js +0 -23
  263. package/dist/test/test/tools/preview-harness.js.map +0 -1
  264. package/dist/test/test/tools/preview.telemetry.test.d.ts +0 -2
  265. package/dist/test/test/tools/preview.telemetry.test.d.ts.map +0 -1
  266. package/dist/test/test/tools/preview.telemetry.test.js +0 -36
  267. package/dist/test/test/tools/preview.telemetry.test.js.map +0 -1
  268. package/dist/test/test/tools/preview.test.d.ts +0 -2
  269. package/dist/test/test/tools/preview.test.d.ts.map +0 -1
  270. package/dist/test/test/tools/preview.test.js +0 -75
  271. package/dist/test/test/tools/preview.test.js.map +0 -1
  272. package/dist/test/test/tools/rename.test.d.ts +0 -2
  273. package/dist/test/test/tools/rename.test.d.ts.map +0 -1
  274. package/dist/test/test/tools/rename.test.js +0 -16
  275. package/dist/test/test/tools/rename.test.js.map +0 -1
  276. package/dist/test/test/tools/save.test.d.ts +0 -2
  277. package/dist/test/test/tools/save.test.d.ts.map +0 -1
  278. package/dist/test/test/tools/save.test.js +0 -137
  279. package/dist/test/test/tools/save.test.js.map +0 -1
  280. package/dist/test/test/tools/schema-best-practices.d.ts +0 -13
  281. package/dist/test/test/tools/schema-best-practices.d.ts.map +0 -1
  282. package/dist/test/test/tools/schema-best-practices.js +0 -169
  283. package/dist/test/test/tools/schema-best-practices.js.map +0 -1
  284. package/dist/test/test/tools/schema-best-practices.test.d.ts +0 -3
  285. package/dist/test/test/tools/schema-best-practices.test.d.ts.map +0 -1
  286. package/dist/test/test/tools/schema-best-practices.test.js +0 -194
  287. package/dist/test/test/tools/schema-best-practices.test.js.map +0 -1
  288. package/dist/test/test/tools/spans.test.d.ts +0 -2
  289. package/dist/test/test/tools/spans.test.d.ts.map +0 -1
  290. package/dist/test/test/tools/spans.test.js +0 -84
  291. package/dist/test/test/tools/spans.test.js.map +0 -1
  292. package/dist/test/test/viewer/broadcaster.test.d.ts +0 -2
  293. package/dist/test/test/viewer/broadcaster.test.d.ts.map +0 -1
  294. package/dist/test/test/viewer/broadcaster.test.js +0 -53
  295. package/dist/test/test/viewer/broadcaster.test.js.map +0 -1
  296. package/dist/test/test/viewer/cesdk-cdn.test.d.ts +0 -2
  297. package/dist/test/test/viewer/cesdk-cdn.test.d.ts.map +0 -1
  298. package/dist/test/test/viewer/cesdk-cdn.test.js +0 -19
  299. package/dist/test/test/viewer/cesdk-cdn.test.js.map +0 -1
  300. package/dist/test/test/viewer/config-route.test.d.ts +0 -2
  301. package/dist/test/test/viewer/config-route.test.d.ts.map +0 -1
  302. package/dist/test/test/viewer/config-route.test.js +0 -28
  303. package/dist/test/test/viewer/config-route.test.js.map +0 -1
  304. package/dist/test/test/viewer/resource-route.test.d.ts +0 -2
  305. package/dist/test/test/viewer/resource-route.test.d.ts.map +0 -1
  306. package/dist/test/test/viewer/resource-route.test.js +0 -70
  307. package/dist/test/test/viewer/resource-route.test.js.map +0 -1
  308. package/dist/test/test/viewer/router.test.d.ts +0 -2
  309. package/dist/test/test/viewer/router.test.d.ts.map +0 -1
  310. package/dist/test/test/viewer/router.test.js +0 -227
  311. package/dist/test/test/viewer/router.test.js.map +0 -1
  312. package/dist/test/test/viewer/session-mount.test.d.ts +0 -2
  313. package/dist/test/test/viewer/session-mount.test.d.ts.map +0 -1
  314. package/dist/test/test/viewer/session-mount.test.js +0 -52
  315. package/dist/test/test/viewer/session-mount.test.js.map +0 -1
  316. package/dist/test/test/viewer/spa-version.test.d.ts +0 -2
  317. package/dist/test/test/viewer/spa-version.test.d.ts.map +0 -1
  318. package/dist/test/test/viewer/spa-version.test.js +0 -14
  319. package/dist/test/test/viewer/spa-version.test.js.map +0 -1
  320. package/dist/test/test/viewer/url-builder.test.d.ts +0 -2
  321. package/dist/test/test/viewer/url-builder.test.d.ts.map +0 -1
  322. package/dist/test/test/viewer/url-builder.test.js +0 -98
  323. package/dist/test/test/viewer/url-builder.test.js.map +0 -1
  324. package/dist/test/test/workspace/content-hash.test.d.ts +0 -2
  325. package/dist/test/test/workspace/content-hash.test.d.ts.map +0 -1
  326. package/dist/test/test/workspace/content-hash.test.js +0 -17
  327. package/dist/test/test/workspace/content-hash.test.js.map +0 -1
  328. package/dist/test/test/workspace/hosted-stub.test.d.ts +0 -2
  329. package/dist/test/test/workspace/hosted-stub.test.d.ts.map +0 -1
  330. package/dist/test/test/workspace/hosted-stub.test.js +0 -30
  331. package/dist/test/test/workspace/hosted-stub.test.js.map +0 -1
  332. package/dist/test/test/workspace/in-memory.test.d.ts +0 -2
  333. package/dist/test/test/workspace/in-memory.test.d.ts.map +0 -1
  334. package/dist/test/test/workspace/in-memory.test.js +0 -4
  335. package/dist/test/test/workspace/in-memory.test.js.map +0 -1
  336. package/dist/test/test/workspace/node-fs.test.d.ts +0 -2
  337. package/dist/test/test/workspace/node-fs.test.d.ts.map +0 -1
  338. package/dist/test/test/workspace/node-fs.test.js +0 -20
  339. package/dist/test/test/workspace/node-fs.test.js.map +0 -1
  340. package/dist/test/test/workspace/s3.test.d.ts +0 -2
  341. package/dist/test/test/workspace/s3.test.d.ts.map +0 -1
  342. package/dist/test/test/workspace/s3.test.js +0 -153
  343. package/dist/test/test/workspace/s3.test.js.map +0 -1
  344. package/dist/test/test/workspace/scoped.test.d.ts +0 -2
  345. package/dist/test/test/workspace/scoped.test.d.ts.map +0 -1
  346. package/dist/test/test/workspace/scoped.test.js +0 -59
  347. package/dist/test/test/workspace/scoped.test.js.map +0 -1
  348. package/dist/test/test/workspace/shared-contract.d.ts +0 -3
  349. package/dist/test/test/workspace/shared-contract.d.ts.map +0 -1
  350. package/dist/test/test/workspace/shared-contract.js +0 -92
  351. package/dist/test/test/workspace/shared-contract.js.map +0 -1
  352. package/dist/test/tsconfig.test.tsbuildinfo +0 -1
  353. package/dist/test/vitest.config.d.ts +0 -3
  354. package/dist/test/vitest.config.d.ts.map +0 -1
  355. package/dist/test/vitest.config.js +0 -8
  356. package/dist/test/vitest.config.js.map +0 -1
  357. package/dist/tools/metrics.d.ts +0 -11
  358. package/dist/tools/metrics.d.ts.map +0 -1
  359. package/dist/tools/metrics.js +0 -28
  360. package/dist/tools/metrics.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview-harness.js","sourceRoot":"","sources":["../../../../test/tools/preview-harness.ts"],"names":[],"mappings":"AAAA,2HAA2H;AAC3H,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAsB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,GAAG,IAAI,mBAAmB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG;QACjB,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,EAAE,GAAW,EAAE,KAAa,EAAE,EAAE,CAC3C,IAAI,IAAI,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3C;KACF,CAAC;IACF,MAAM,OAAO,GAAkB;QAC7B,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU;QACxB,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;QACrB,cAAc,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;QAC9B,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;KAC7B,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,KAAK,EAAE,EAAE,CAAC;AACrD,CAAC","sourcesContent":["// Shared preview-tool test harness — a plain module (no test registrations) so importing it never re-runs preview.test.ts.\nimport { Mutex } from '../../src/engine/mutex.js';\nimport { type EngineAdapter, EngineSession } from '../../src/engine/session.js';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\nexport function makePreviewHarness() {\n const fs = new InMemoryWorkspaceFS();\n const store = new RevisionStore(fs);\n const fakeEngine = {\n block: {\n export: async (_id: number, _mime: string) =>\n new Blob([new Uint8Array([1, 2, 3, 4])])\n }\n };\n const adapter: EngineAdapter = {\n create: () => fakeEngine,\n reset: async () => {},\n loadFromString: async () => {},\n saveToString: async () => ''\n };\n const session = new EngineSession({ size: 1, adapter });\n return { store, session, engineLock: new Mutex() };\n}\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=preview.telemetry.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.telemetry.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/preview.telemetry.test.ts"],"names":[],"mappings":""}
@@ -1,36 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createPreviewTool } from '../../src/tools/preview.js';
3
- import { makePreviewHarness } from './preview-harness.js';
4
- describe('preview telemetry', () => {
5
- it('records engine_execute and no write phases when revision is supplied', async () => {
6
- const reports = [];
7
- const harness = makePreviewHarness();
8
- const root = await harness.store.write({
9
- scene: 'SCENE_FOR_PREVIEW',
10
- parent: null,
11
- title: 'T'
12
- });
13
- const tool = createPreviewTool({
14
- ...harness,
15
- telemetry: { record: (r) => reports.push(r) }
16
- });
17
- const res = await tool.call({ blockId: 1, revision: root.id });
18
- expect(res.isError).toBeUndefined();
19
- expect(reports).toHaveLength(1);
20
- const phases = reports[0].spans.map((s) => s.phase);
21
- expect(phases).toContain('engine_execute');
22
- expect(phases).not.toContain('engine_scene_save');
23
- expect(reports[0].revision).toBe(root.id);
24
- });
25
- it('records succeeded=false when blockId is missing', async () => {
26
- const reports = [];
27
- const tool = createPreviewTool({
28
- ...makePreviewHarness(),
29
- telemetry: { record: (r) => reports.push(r) }
30
- });
31
- const res = await tool.call({});
32
- expect(res.isError).toBe(true);
33
- expect(reports[0].succeeded).toBe(false);
34
- });
35
- });
36
- //# sourceMappingURL=preview.telemetry.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.telemetry.test.js","sourceRoot":"","sources":["../../../../test/tools/preview.telemetry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,OAAO,GAAqB,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;YACrC,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,iBAAiB,CAAC;YAC7B,GAAG,OAAO;YACV,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;SAC9C,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAClD,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,OAAO,GAAqB,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,iBAAiB,CAAC;YAC7B,GAAG,kBAAkB,EAAE;YACvB,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;SAC9C,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { createPreviewTool } from '../../src/tools/preview.js';\nimport type { ToolCallReport } from '../../src/tools/spans.js';\nimport { makePreviewHarness } from './preview-harness.js';\n\ndescribe('preview telemetry', () => {\n it('records engine_execute and no write phases when revision is supplied', async () => {\n const reports: ToolCallReport[] = [];\n const harness = makePreviewHarness();\n const root = await harness.store.write({\n scene: 'SCENE_FOR_PREVIEW',\n parent: null,\n title: 'T'\n });\n const tool = createPreviewTool({\n ...harness,\n telemetry: { record: (r) => reports.push(r) }\n });\n const res = await tool.call({ blockId: 1, revision: root.id });\n expect(res.isError).toBeUndefined();\n expect(reports).toHaveLength(1);\n const phases = reports[0].spans.map((s) => s.phase);\n expect(phases).toContain('engine_execute');\n expect(phases).not.toContain('engine_scene_save');\n expect(reports[0].revision).toBe(root.id);\n });\n\n it('records succeeded=false when blockId is missing', async () => {\n const reports: ToolCallReport[] = [];\n const tool = createPreviewTool({\n ...makePreviewHarness(),\n telemetry: { record: (r) => reports.push(r) }\n });\n const res = await tool.call({});\n expect(res.isError).toBe(true);\n expect(reports[0].succeeded).toBe(false);\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=preview.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/preview.test.ts"],"names":[],"mappings":""}
@@ -1,75 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { Mutex } from '../../src/engine/mutex.js';
3
- import { EngineSession } from '../../src/engine/session.js';
4
- import { RevisionStore } from '../../src/revisions/store.js';
5
- import { createPreviewTool } from '../../src/tools/preview.js';
6
- import { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';
7
- const engineLock = new Mutex();
8
- function makeDeps() {
9
- const fs = new InMemoryWorkspaceFS();
10
- const store = new RevisionStore(fs);
11
- const fakeEngine = {
12
- _scene: '',
13
- block: {
14
- export: vi.fn(async (_id, _mime) => new Blob([new Uint8Array([1, 2, 3, 4])]))
15
- }
16
- };
17
- const adapter = {
18
- create: () => fakeEngine,
19
- reset: async (e) => {
20
- e._scene = 'EMPTY';
21
- },
22
- loadFromString: async (e, s) => {
23
- e._scene = s;
24
- },
25
- saveToString: async (e) => e._scene
26
- };
27
- const session = new EngineSession({ size: 1, adapter });
28
- return { fs, store, session, fakeEngine, adapter };
29
- }
30
- describe('preview tool', () => {
31
- it('renders the active engine state when no revision is supplied', async () => {
32
- const { store, session, fakeEngine } = makeDeps();
33
- const slot = await session.acquire(null);
34
- session.commit(slot, 'rev_warm');
35
- fakeEngine._scene = 'WARM';
36
- const tool = createPreviewTool({ store, session, engineLock });
37
- const result = await tool.call({ blockId: 42 });
38
- expect(result.isError).toBeFalsy();
39
- expect(result.content[0].type).toBe('image');
40
- expect(fakeEngine.block.export).toHaveBeenCalledWith(42, 'image/webp', expect.any(Object));
41
- // Engine state is preserved (NOT reset) when no revision is supplied.
42
- expect(fakeEngine._scene).toBe('WARM');
43
- });
44
- it('returns isError when no revision supplied AND no warm slot exists', async () => {
45
- const { store, session } = makeDeps();
46
- const tool = createPreviewTool({ store, session, engineLock });
47
- const result = await tool.call({ blockId: 42 });
48
- expect(result.isError).toBe(true);
49
- expect(result.content[0].text).toMatch(/no.*engine.*state/i);
50
- });
51
- it('loads a revision into the engine when `revision` is supplied', async () => {
52
- const { store, session, adapter } = makeDeps();
53
- const root = await store.write({
54
- scene: 'SCENE_FOR_PREVIEW',
55
- parent: null,
56
- title: 'T'
57
- });
58
- const tool = createPreviewTool({ store, session, engineLock });
59
- // adapter.loadFromString is set as a plain async fn (not a vi.fn) above, so wrap it
60
- const loadSpy = vi.spyOn(adapter, 'loadFromString');
61
- await tool.call({ blockId: 42, revision: root.id });
62
- expect(loadSpy).toHaveBeenCalledWith(expect.anything(), 'SCENE_FOR_PREVIEW');
63
- });
64
- it('clamps width/height to 1800 px', async () => {
65
- const { store, session, fakeEngine } = makeDeps();
66
- const slot = await session.acquire(null);
67
- session.commit(slot, 'rev_warm');
68
- const tool = createPreviewTool({ store, session, engineLock });
69
- await tool.call({ blockId: 42, width: 9999, height: 9999 });
70
- const call = fakeEngine.block.export.mock
71
- .calls[0];
72
- expect(call[2]).toMatchObject({ targetWidth: 1800, targetHeight: 1800 });
73
- });
74
- });
75
- //# sourceMappingURL=preview.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.test.js","sourceRoot":"","sources":["../../../../test/tools/preview.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport { Mutex } from '../../src/engine/mutex.js';\nimport { type EngineAdapter, EngineSession } from '../../src/engine/session.js';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { createPreviewTool } from '../../src/tools/preview.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\nconst engineLock = new Mutex();\n\ninterface FakeEngine {\n _scene: string;\n block: { export: ReturnType<typeof vi.fn> };\n}\n\nfunction makeDeps() {\n const fs = new InMemoryWorkspaceFS();\n const store = new RevisionStore(fs);\n const fakeEngine: FakeEngine = {\n _scene: '',\n block: {\n export: vi.fn(\n async (_id: number, _mime: string) =>\n new Blob([new Uint8Array([1, 2, 3, 4])])\n )\n }\n };\n const adapter: EngineAdapter<FakeEngine> = {\n create: () => fakeEngine,\n reset: async (e) => {\n e._scene = 'EMPTY';\n },\n loadFromString: async (e, s) => {\n e._scene = s;\n },\n saveToString: async (e) => e._scene\n };\n const session = new EngineSession<FakeEngine>({ size: 1, adapter });\n return { fs, store, session, fakeEngine, adapter };\n}\n\ndescribe('preview tool', () => {\n it('renders the active engine state when no revision is supplied', async () => {\n const { store, session, fakeEngine } = makeDeps();\n const slot = await session.acquire(null);\n session.commit(slot, 'rev_warm');\n fakeEngine._scene = 'WARM';\n const tool = createPreviewTool({ store, session, engineLock });\n const result = await tool.call({ blockId: 42 });\n expect(result.isError).toBeFalsy();\n expect((result.content[0] as { type: string }).type).toBe('image');\n expect(fakeEngine.block.export).toHaveBeenCalledWith(\n 42,\n 'image/webp',\n expect.any(Object)\n );\n // Engine state is preserved (NOT reset) when no revision is supplied.\n expect(fakeEngine._scene).toBe('WARM');\n });\n\n it('returns isError when no revision supplied AND no warm slot exists', async () => {\n const { store, session } = makeDeps();\n const tool = createPreviewTool({ store, session, engineLock });\n const result = await tool.call({ blockId: 42 });\n expect(result.isError).toBe(true);\n expect((result.content[0] as { text: string }).text).toMatch(\n /no.*engine.*state/i\n );\n });\n\n it('loads a revision into the engine when `revision` is supplied', async () => {\n const { store, session, adapter } = makeDeps();\n const root = await store.write({\n scene: 'SCENE_FOR_PREVIEW',\n parent: null,\n title: 'T'\n });\n const tool = createPreviewTool({ store, session, engineLock });\n // adapter.loadFromString is set as a plain async fn (not a vi.fn) above, so wrap it\n const loadSpy = vi.spyOn(adapter, 'loadFromString');\n await tool.call({ blockId: 42, revision: root.id });\n expect(loadSpy).toHaveBeenCalledWith(\n expect.anything(),\n 'SCENE_FOR_PREVIEW'\n );\n });\n\n it('clamps width/height to 1800 px', async () => {\n const { store, session, fakeEngine } = makeDeps();\n const slot = await session.acquire(null);\n session.commit(slot, 'rev_warm');\n const tool = createPreviewTool({ store, session, engineLock });\n await tool.call({ blockId: 42, width: 9999, height: 9999 });\n const call = (fakeEngine.block.export as ReturnType<typeof vi.fn>).mock\n .calls[0];\n expect(call[2]).toMatchObject({ targetWidth: 1800, targetHeight: 1800 });\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=rename.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rename.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/rename.test.ts"],"names":[],"mappings":""}
@@ -1,16 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { RevisionStore } from '../../src/revisions/store.js';
3
- import { createRenameTool } from '../../src/tools/rename.js';
4
- import { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';
5
- describe('rename tool', () => {
6
- it('renames the root reachable from the given revision', async () => {
7
- const store = new RevisionStore(new InMemoryWorkspaceFS());
8
- const r1 = await store.write({ scene: 'a', parent: null, title: 'Old' });
9
- const r2 = await store.write({ scene: 'b', parent: r1.id });
10
- const tool = createRenameTool({ store });
11
- const result = await tool.call({ revision: r2.id, title: 'New' });
12
- expect(result.content[0].text).toContain('ok');
13
- expect(await store.currentTitle(r1.id)).toBe('New');
14
- });
15
- });
16
- //# sourceMappingURL=rename.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rename.test.js","sourceRoot":"","sources":["../../../../test/tools/rename.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC;QAC3D,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,MAAM,CAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAsB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { createRenameTool } from '../../src/tools/rename.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\ndescribe('rename tool', () => {\n it('renames the root reachable from the given revision', async () => {\n const store = new RevisionStore(new InMemoryWorkspaceFS());\n const r1 = await store.write({ scene: 'a', parent: null, title: 'Old' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const tool = createRenameTool({ store });\n const result = await tool.call({ revision: r2.id, title: 'New' });\n expect((result.content[0] as { text: string }).text).toContain('ok');\n expect(await store.currentTitle(r1.id)).toBe('New');\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=save.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"save.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/save.test.ts"],"names":[],"mappings":""}
@@ -1,137 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { Mutex } from '../../src/engine/mutex.js';
3
- import { EngineSession } from '../../src/engine/session.js';
4
- import { RevisionStore } from '../../src/revisions/store.js';
5
- import { createSaveTool } from '../../src/tools/save.js';
6
- import { LocalViewerUrlBuilder } from '../../src/viewer/url-builder.js';
7
- import { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';
8
- const engineLock = new Mutex();
9
- const urlBuilder = new LocalViewerUrlBuilder({ base: 'http://127.0.0.1:9000' });
10
- function makeDeps() {
11
- const fs = new InMemoryWorkspaceFS();
12
- const store = new RevisionStore(fs);
13
- const fakeEngine = {
14
- _scene: '',
15
- _resourcesLoaded: false,
16
- scene: {
17
- getPages: vi.fn(() => [10]),
18
- // The archive bundles the asset marker ONLY if resources were loaded
19
- // first — mirrors saveToArchive's "assets accessible when called" contract.
20
- saveToArchive: vi.fn(async () => {
21
- const body = fakeEngine._resourcesLoaded ? 'ZIP+ASSET' : 'ZIP';
22
- return new Blob([new TextEncoder().encode(body)]);
23
- })
24
- },
25
- block: {
26
- forceLoadResources: vi.fn(async () => {
27
- fakeEngine._resourcesLoaded = true;
28
- })
29
- }
30
- };
31
- const adapter = {
32
- create: () => fakeEngine,
33
- reset: async (e) => {
34
- e._scene = 'EMPTY';
35
- },
36
- loadFromString: async (e, s) => {
37
- e._scene = s;
38
- },
39
- saveToString: async (e) => e._scene
40
- };
41
- const session = new EngineSession({ size: 1, adapter });
42
- return { fs, store, session, fakeEngine };
43
- }
44
- describe('save tool', () => {
45
- it('writes archive.zip and returns uri + httpUrl, no path', async () => {
46
- const { fs, store, session } = makeDeps();
47
- const root = await store.write({
48
- scene: 'SCENE_ABC',
49
- parent: null,
50
- title: 'T'
51
- });
52
- const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });
53
- const result = await tool.call({ revision: root.id });
54
- const body = JSON.parse(result.content[0].text);
55
- const shard = root.id.slice(0, 2);
56
- const rest = root.id.slice(2);
57
- expect(body.uri).toBe(`workspace://exports/${shard}/${rest}/archive.zip`);
58
- expect(body.httpUrl).toBe(`http://127.0.0.1:9000/r/exports/${shard}/${rest}/archive.zip`);
59
- expect(body.path).toBeUndefined();
60
- expect(body.format).toBe('archive');
61
- expect(body.revision).toBe(root.id);
62
- expect(await fs.exists(`exports/${shard}/${rest}/archive.zip`)).toBe(true);
63
- });
64
- it('respects local `outPath` escape hatch — returns path, not httpUrl', async () => {
65
- const { fs, store, session } = makeDeps();
66
- const root = await store.write({ scene: 'S', parent: null, title: 'T' });
67
- const { mkdtemp, rm } = await import('node:fs/promises');
68
- const { tmpdir } = await import('node:os');
69
- const { join } = await import('node:path');
70
- const dir = await mkdtemp(join(tmpdir(), 'save-out-'));
71
- const outPath = join(dir, 'design.zip');
72
- const tool = createSaveTool({
73
- fs,
74
- store,
75
- session,
76
- engineLock,
77
- urlBuilder,
78
- allowOutPath: true
79
- });
80
- const result = await tool.call({ revision: root.id, outPath });
81
- const body = JSON.parse(result.content[0].text);
82
- expect(body.path).toBe(outPath);
83
- expect(body.httpUrl).toBeUndefined();
84
- await rm(dir, { recursive: true });
85
- });
86
- it('rejects `outPath` when not allowed (Hosted/sandbox)', async () => {
87
- const { fs, store, session } = makeDeps();
88
- const root = await store.write({ scene: 'S', parent: null, title: 'T' });
89
- const tool = createSaveTool({
90
- fs,
91
- store,
92
- session,
93
- engineLock,
94
- urlBuilder,
95
- allowOutPath: false
96
- });
97
- const result = await tool.call({
98
- revision: root.id,
99
- outPath: '/tmp/x.zip'
100
- });
101
- expect(result.isError).toBe(true);
102
- expect(result.content[0].text).toMatch(/outPath.*not.*supported/i);
103
- });
104
- it('omits `outPath` from the schema when not allowed (Hosted), advertises it when allowed (Local)', () => {
105
- const { fs, store, session } = makeDeps();
106
- const base = { fs, store, session, engineLock, urlBuilder };
107
- const hosted = createSaveTool({ ...base, allowOutPath: false });
108
- const local = createSaveTool({ ...base, allowOutPath: true });
109
- const props = (s) => s.properties;
110
- expect(props(hosted.inputSchema)).not.toHaveProperty('outPath');
111
- expect(props(local.inputSchema)).toHaveProperty('outPath');
112
- });
113
- it('errors when revision is missing', async () => {
114
- const { fs, store, session } = makeDeps();
115
- const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });
116
- const result = await tool.call({});
117
- expect(result.isError).toBe(true);
118
- expect(result.content[0].text).toMatch(/revision/i);
119
- });
120
- it('force-loads resources before archiving so assets are bundled', async () => {
121
- const { fs, store, session, fakeEngine } = makeDeps();
122
- const root = await store.write({ scene: 'S', parent: null, title: 'T' });
123
- const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });
124
- await tool.call({ revision: root.id });
125
- // forceLoadResources must run with the scene's pages...
126
- expect(fakeEngine.block.forceLoadResources).toHaveBeenCalledWith([10]);
127
- // ...and BEFORE saveToArchive.
128
- const callOrder = (m) => m.mock.invocationCallOrder[0];
129
- expect(callOrder(fakeEngine.block.forceLoadResources)).toBeLessThan(callOrder(fakeEngine.scene.saveToArchive));
130
- // ...so the produced archive actually contains the asset bytes.
131
- const shard = root.id.slice(0, 2);
132
- const rest = root.id.slice(2);
133
- const zip = await fs.readFile(`exports/${shard}/${rest}/archive.zip`, 'utf8');
134
- expect(zip).toContain('ASSET');
135
- });
136
- });
137
- //# sourceMappingURL=save.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"save.test.js","sourceRoot":"","sources":["../../../../test/tools/save.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport { Mutex } from '../../src/engine/mutex.js';\nimport { type EngineAdapter, EngineSession } from '../../src/engine/session.js';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { createSaveTool } from '../../src/tools/save.js';\nimport { LocalViewerUrlBuilder } from '../../src/viewer/url-builder.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\nconst engineLock = new Mutex();\nconst urlBuilder = new LocalViewerUrlBuilder({ base: 'http://127.0.0.1:9000' });\n\ninterface FakeEngine {\n _scene: string;\n _resourcesLoaded: boolean;\n scene: {\n getPages: ReturnType<typeof vi.fn>;\n saveToArchive: ReturnType<typeof vi.fn>;\n };\n block: { forceLoadResources: ReturnType<typeof vi.fn> };\n}\n\nfunction makeDeps() {\n const fs = new InMemoryWorkspaceFS();\n const store = new RevisionStore(fs);\n const fakeEngine: FakeEngine = {\n _scene: '',\n _resourcesLoaded: false,\n scene: {\n getPages: vi.fn(() => [10]),\n // The archive bundles the asset marker ONLY if resources were loaded\n // first — mirrors saveToArchive's \"assets accessible when called\" contract.\n saveToArchive: vi.fn(async () => {\n const body = fakeEngine._resourcesLoaded ? 'ZIP+ASSET' : 'ZIP';\n return new Blob([new TextEncoder().encode(body)]);\n })\n },\n block: {\n forceLoadResources: vi.fn(async () => {\n fakeEngine._resourcesLoaded = true;\n })\n }\n };\n const adapter: EngineAdapter<FakeEngine> = {\n create: () => fakeEngine,\n reset: async (e) => {\n e._scene = 'EMPTY';\n },\n loadFromString: async (e, s) => {\n e._scene = s;\n },\n saveToString: async (e) => e._scene\n };\n const session = new EngineSession<FakeEngine>({ size: 1, adapter });\n return { fs, store, session, fakeEngine };\n}\n\ndescribe('save tool', () => {\n it('writes archive.zip and returns uri + httpUrl, no path', async () => {\n const { fs, store, session } = makeDeps();\n const root = await store.write({\n scene: 'SCENE_ABC',\n parent: null,\n title: 'T'\n });\n const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });\n const result = await tool.call({ revision: root.id });\n const body = JSON.parse((result.content[0] as { text: string }).text) as {\n uri: string;\n httpUrl?: string;\n path?: string;\n bytes: number;\n format: string;\n revision: string;\n };\n const shard = root.id.slice(0, 2);\n const rest = root.id.slice(2);\n expect(body.uri).toBe(`workspace://exports/${shard}/${rest}/archive.zip`);\n expect(body.httpUrl).toBe(\n `http://127.0.0.1:9000/r/exports/${shard}/${rest}/archive.zip`\n );\n expect(body.path).toBeUndefined();\n expect(body.format).toBe('archive');\n expect(body.revision).toBe(root.id);\n expect(await fs.exists(`exports/${shard}/${rest}/archive.zip`)).toBe(true);\n });\n\n it('respects local `outPath` escape hatch — returns path, not httpUrl', async () => {\n const { fs, store, session } = makeDeps();\n const root = await store.write({ scene: 'S', parent: null, title: 'T' });\n const { mkdtemp, rm } = await import('node:fs/promises');\n const { tmpdir } = await import('node:os');\n const { join } = await import('node:path');\n const dir = await mkdtemp(join(tmpdir(), 'save-out-'));\n const outPath = join(dir, 'design.zip');\n const tool = createSaveTool({\n fs,\n store,\n session,\n engineLock,\n urlBuilder,\n allowOutPath: true\n });\n const result = await tool.call({ revision: root.id, outPath });\n const body = JSON.parse((result.content[0] as { text: string }).text) as {\n path?: string;\n httpUrl?: string;\n };\n expect(body.path).toBe(outPath);\n expect(body.httpUrl).toBeUndefined();\n await rm(dir, { recursive: true });\n });\n\n it('rejects `outPath` when not allowed (Hosted/sandbox)', async () => {\n const { fs, store, session } = makeDeps();\n const root = await store.write({ scene: 'S', parent: null, title: 'T' });\n const tool = createSaveTool({\n fs,\n store,\n session,\n engineLock,\n urlBuilder,\n allowOutPath: false\n });\n const result = await tool.call({\n revision: root.id,\n outPath: '/tmp/x.zip'\n });\n expect(result.isError).toBe(true);\n expect((result.content[0] as { text: string }).text).toMatch(\n /outPath.*not.*supported/i\n );\n });\n\n it('omits `outPath` from the schema when not allowed (Hosted), advertises it when allowed (Local)', () => {\n const { fs, store, session } = makeDeps();\n const base = { fs, store, session, engineLock, urlBuilder };\n const hosted = createSaveTool({ ...base, allowOutPath: false });\n const local = createSaveTool({ ...base, allowOutPath: true });\n const props = (s: unknown) =>\n (s as { properties: Record<string, unknown> }).properties;\n expect(props(hosted.inputSchema)).not.toHaveProperty('outPath');\n expect(props(local.inputSchema)).toHaveProperty('outPath');\n });\n\n it('errors when revision is missing', async () => {\n const { fs, store, session } = makeDeps();\n const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });\n const result = await tool.call({});\n expect(result.isError).toBe(true);\n expect((result.content[0] as { text: string }).text).toMatch(/revision/i);\n });\n\n it('force-loads resources before archiving so assets are bundled', async () => {\n const { fs, store, session, fakeEngine } = makeDeps();\n const root = await store.write({ scene: 'S', parent: null, title: 'T' });\n const tool = createSaveTool({ fs, store, session, engineLock, urlBuilder });\n await tool.call({ revision: root.id });\n\n // forceLoadResources must run with the scene's pages...\n expect(fakeEngine.block.forceLoadResources).toHaveBeenCalledWith([10]);\n // ...and BEFORE saveToArchive.\n const callOrder = (m: ReturnType<typeof vi.fn>): number =>\n m.mock.invocationCallOrder[0];\n expect(callOrder(fakeEngine.block.forceLoadResources)).toBeLessThan(\n callOrder(fakeEngine.scene.saveToArchive)\n );\n\n // ...so the produced archive actually contains the asset bytes.\n const shard = root.id.slice(0, 2);\n const rest = root.id.slice(2);\n const zip = await fs.readFile(\n `exports/${shard}/${rest}/archive.zip`,\n 'utf8'\n );\n expect(zip).toContain('ASSET');\n });\n});\n"]}
@@ -1,13 +0,0 @@
1
- export type Severity = 'error' | 'warn';
2
- export interface SchemaFinding {
3
- /** Human-readable location inside the schema, e.g. `properties.source`. */
4
- path: string;
5
- /** Stable rule id (see RULES below). */
6
- rule: string;
7
- severity: Severity;
8
- message: string;
9
- }
10
- /** Lint one tool's input schema. Returns all findings (errors + warnings). */
11
- export declare function lintToolSchema(schema: unknown): SchemaFinding[];
12
- export declare const errorsOnly: (findings: SchemaFinding[]) => SchemaFinding[];
13
- //# sourceMappingURL=schema-best-practices.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-best-practices.d.ts","sourceRoot":"","sources":["../../../../test/tools/schema-best-practices.ts"],"names":[],"mappings":"AA2BA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AA8JD,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,EAAE,CAI/D;AAED,eAAO,MAAM,UAAU,GAAI,UAAU,aAAa,EAAE,KAAG,aAAa,EACpB,CAAC"}
@@ -1,169 +0,0 @@
1
- // packages/codesign-mcp/test/tools/schema-best-practices.ts
2
- //
3
- // A tiny JSON-Schema linter for MCP *tool input schemas*, encoding the
4
- // cross-vendor best practices for reliable LLM tool calling. It exists because
5
- // a real eval regression traced back to a schema shape, not a model bug:
6
- // `load`'s `source` used a `oneOf` union, and a weaker model
7
- // (openrouter:xiaomi/mimo-v2.5-pro) serialized the whole object as an escaped
8
- // JSON string on every attempt — the exact `oneOf` failure documented against
9
- // Claude too (samchon/openapi#88). OpenAI sidesteps it by banning `oneOf` in
10
- // strict mode outright.
11
- //
12
- // Sources for the rules below:
13
- // - samchon/openapi#88 — Claude fills `oneOf` unions as a JSON string.
14
- // - OpenAI structured outputs: `oneOf`/`allOf`/`$ref`(recursive) unsupported
15
- // in strict mode; root must be `object`; `additionalProperties:false`
16
- // required; only a subset of JSON Schema is honoured.
17
- // - Cohere structured outputs: `anyOf`/`allOf`/`oneOf`/`dependencies` not
18
- // enforced.
19
- // - Vendor + practitioner guidance (Anyscale, agenta, Cohere, LangChain):
20
- // "keep tool schemas as flat as possible; deep nesting lowers generation
21
- // quality"; "invest in descriptions/enums — +10-20% accuracy".
22
- //
23
- // The linter is deliberately conservative: HARD ERRORS are the constructs
24
- // documented to break or degrade tool calls across providers; WARNINGS are
25
- // quality nudges that shouldn't fail CI. `schema-best-practices.test.ts`
26
- // asserts zero errors over every registered tool and prints warnings.
27
- // Object-nesting depth at which we start nudging toward a flatter schema.
28
- // Root object = depth 1; one nested object (e.g. `source`) = depth 2 (fine).
29
- // Depth 3+ is where the "keep it flat" guidance starts to bite, so we warn.
30
- const MAX_OBJECT_DEPTH_BEFORE_WARN = 2;
31
- // Composition / reference keywords that are unsupported or unenforced by at
32
- // least one major provider's strict tool-calling path — each is a hard error.
33
- const BANNED_COMPOSITION_KEYS = [
34
- 'oneOf', // OpenAI: banned in strict mode; Claude/mimo: stringify the value
35
- 'allOf', // OpenAI/Cohere: unsupported/unenforced
36
- 'not', // unsupported in strict schemas
37
- '$ref', // no external/recursive refs in strict mode — inline instead
38
- '$defs',
39
- 'definitions',
40
- 'prefixItems', // tuple validation unsupported
41
- 'dependencies',
42
- 'dependentSchemas',
43
- 'dependentRequired'
44
- ];
45
- const SCALAR_TYPES = new Set([
46
- 'string',
47
- 'number',
48
- 'integer',
49
- 'boolean',
50
- 'null'
51
- ]);
52
- function isRecord(value) {
53
- return typeof value === 'object' && value !== null && !Array.isArray(value);
54
- }
55
- /** True when a schema node only ever produces a scalar (safe `anyOf` arm). */
56
- function isScalarSchema(node) {
57
- if ('properties' in node || 'items' in node)
58
- return false;
59
- const t = node.type;
60
- if (typeof t === 'string')
61
- return SCALAR_TYPES.has(t);
62
- if (Array.isArray(t))
63
- return t.every((x) => SCALAR_TYPES.has(String(x)));
64
- // No `type` at all → can't prove it's scalar; treat as complex.
65
- return false;
66
- }
67
- /**
68
- * Lint a single JSON-Schema node (recursively). `path` is the location label,
69
- * `depth` counts object nesting, `isRoot` gates the "root must be object" rule.
70
- */
71
- function lintNode(node, path, depth, isRoot, out) {
72
- if (!isRecord(node))
73
- return;
74
- // Root of a tool input schema MUST be an object — every tool-call API
75
- // requires it, and a non-object root cannot carry named arguments.
76
- if (isRoot && node.type !== 'object') {
77
- out.push({
78
- path,
79
- rule: 'root-must-be-object',
80
- severity: 'error',
81
- message: `tool input schema root must be { type: "object" }, got ${JSON.stringify(node.type)}`
82
- });
83
- }
84
- // Banned composition/reference keywords anywhere in the tree.
85
- for (const key of BANNED_COMPOSITION_KEYS) {
86
- if (key in node) {
87
- out.push({
88
- path: `${path}.${key}`,
89
- rule: `no-${key.replace('$', '')}`,
90
- severity: 'error',
91
- message: `\`${key}\` is unsupported/unenforced by strict tool-calling on at least one major provider — flatten it (prefer sibling optional fields + runtime validation, or \`type: [...]\` for nullability).`
92
- });
93
- }
94
- }
95
- // `anyOf` is the one union OpenAI keeps in strict mode, but only scalar arms
96
- // are safe. An object/array arm reintroduces the discriminated-union shape
97
- // that models stringify — so allow scalar unions (incl. the `["string",
98
- // "null"]` nullable idiom) and flag complex arms.
99
- if (Array.isArray(node.anyOf)) {
100
- node.anyOf.forEach((arm, i) => {
101
- if (isRecord(arm) && !isScalarSchema(arm)) {
102
- out.push({
103
- path: `${path}.anyOf[${i}]`,
104
- rule: 'anyof-scalar-arms-only',
105
- severity: 'error',
106
- message: 'anyOf arms must be scalar types; an object/array arm is a discriminated union that weaker models serialize as a JSON string. Flatten to sibling fields.'
107
- });
108
- }
109
- lintNode(arm, `${path}.anyOf[${i}]`, depth, false, out);
110
- });
111
- }
112
- if (node.type === 'object') {
113
- // Strict tool-calling (OpenAI et al.) requires closed objects.
114
- if (node.additionalProperties !== false) {
115
- out.push({
116
- path,
117
- rule: 'object-additional-properties-false',
118
- severity: 'error',
119
- message: 'object schemas must set `additionalProperties: false` for strict-mode adherence.'
120
- });
121
- }
122
- if (depth > MAX_OBJECT_DEPTH_BEFORE_WARN) {
123
- out.push({
124
- path,
125
- rule: 'keep-schema-flat',
126
- severity: 'warn',
127
- message: `object nesting depth ${depth} — deep nesting lowers generation quality; keep tool schemas as flat as the domain allows.`
128
- });
129
- }
130
- const props = node.properties;
131
- if (isRecord(props)) {
132
- for (const [name, sub] of Object.entries(props)) {
133
- const subPath = `${path}.properties.${name}`;
134
- // Descriptions measurably improve tool-call accuracy (+10-20%).
135
- if (isRecord(sub) &&
136
- (typeof sub.description !== 'string' || sub.description.trim() === '')) {
137
- out.push({
138
- path: subPath,
139
- rule: 'property-needs-description',
140
- severity: 'warn',
141
- message: `property \`${name}\` has no description — add one (+accuracy).`
142
- });
143
- }
144
- lintNode(sub, subPath, depth + 1, false, out);
145
- }
146
- }
147
- }
148
- if (node.type === 'array') {
149
- if (!isRecord(node.items)) {
150
- out.push({
151
- path,
152
- rule: 'array-needs-typed-items',
153
- severity: 'warn',
154
- message: 'array schema has no typed `items` — models generate array elements more reliably with a concrete item schema.'
155
- });
156
- }
157
- else {
158
- lintNode(node.items, `${path}.items`, depth, false, out);
159
- }
160
- }
161
- }
162
- /** Lint one tool's input schema. Returns all findings (errors + warnings). */
163
- export function lintToolSchema(schema) {
164
- const findings = [];
165
- lintNode(schema, '$', 1, true, findings);
166
- return findings;
167
- }
168
- export const errorsOnly = (findings) => findings.filter((f) => f.severity === 'error');
169
- //# sourceMappingURL=schema-best-practices.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-best-practices.js","sourceRoot":"","sources":["../../../../test/tools/schema-best-practices.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/codesign-mcp/test/tools/schema-best-practices.ts\n//\n// A tiny JSON-Schema linter for MCP *tool input schemas*, encoding the\n// cross-vendor best practices for reliable LLM tool calling. It exists because\n// a real eval regression traced back to a schema shape, not a model bug:\n// `load`'s `source` used a `oneOf` union, and a weaker model\n// (openrouter:xiaomi/mimo-v2.5-pro) serialized the whole object as an escaped\n// JSON string on every attempt — the exact `oneOf` failure documented against\n// Claude too (samchon/openapi#88). OpenAI sidesteps it by banning `oneOf` in\n// strict mode outright.\n//\n// Sources for the rules below:\n// - samchon/openapi#88 — Claude fills `oneOf` unions as a JSON string.\n// - OpenAI structured outputs: `oneOf`/`allOf`/`$ref`(recursive) unsupported\n// in strict mode; root must be `object`; `additionalProperties:false`\n// required; only a subset of JSON Schema is honoured.\n// - Cohere structured outputs: `anyOf`/`allOf`/`oneOf`/`dependencies` not\n// enforced.\n// - Vendor + practitioner guidance (Anyscale, agenta, Cohere, LangChain):\n// \"keep tool schemas as flat as possible; deep nesting lowers generation\n// quality\"; \"invest in descriptions/enums — +10-20% accuracy\".\n//\n// The linter is deliberately conservative: HARD ERRORS are the constructs\n// documented to break or degrade tool calls across providers; WARNINGS are\n// quality nudges that shouldn't fail CI. `schema-best-practices.test.ts`\n// asserts zero errors over every registered tool and prints warnings.\n\nexport type Severity = 'error' | 'warn';\n\nexport interface SchemaFinding {\n /** Human-readable location inside the schema, e.g. `properties.source`. */\n path: string;\n /** Stable rule id (see RULES below). */\n rule: string;\n severity: Severity;\n message: string;\n}\n\n// Object-nesting depth at which we start nudging toward a flatter schema.\n// Root object = depth 1; one nested object (e.g. `source`) = depth 2 (fine).\n// Depth 3+ is where the \"keep it flat\" guidance starts to bite, so we warn.\nconst MAX_OBJECT_DEPTH_BEFORE_WARN = 2;\n\n// Composition / reference keywords that are unsupported or unenforced by at\n// least one major provider's strict tool-calling path — each is a hard error.\nconst BANNED_COMPOSITION_KEYS = [\n 'oneOf', // OpenAI: banned in strict mode; Claude/mimo: stringify the value\n 'allOf', // OpenAI/Cohere: unsupported/unenforced\n 'not', // unsupported in strict schemas\n '$ref', // no external/recursive refs in strict mode — inline instead\n '$defs',\n 'definitions',\n 'prefixItems', // tuple validation unsupported\n 'dependencies',\n 'dependentSchemas',\n 'dependentRequired'\n] as const;\n\nconst SCALAR_TYPES = new Set([\n 'string',\n 'number',\n 'integer',\n 'boolean',\n 'null'\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** True when a schema node only ever produces a scalar (safe `anyOf` arm). */\nfunction isScalarSchema(node: Record<string, unknown>): boolean {\n if ('properties' in node || 'items' in node) return false;\n const t = node.type;\n if (typeof t === 'string') return SCALAR_TYPES.has(t);\n if (Array.isArray(t)) return t.every((x) => SCALAR_TYPES.has(String(x)));\n // No `type` at all → can't prove it's scalar; treat as complex.\n return false;\n}\n\n/**\n * Lint a single JSON-Schema node (recursively). `path` is the location label,\n * `depth` counts object nesting, `isRoot` gates the \"root must be object\" rule.\n */\nfunction lintNode(\n node: unknown,\n path: string,\n depth: number,\n isRoot: boolean,\n out: SchemaFinding[]\n): void {\n if (!isRecord(node)) return;\n\n // Root of a tool input schema MUST be an object — every tool-call API\n // requires it, and a non-object root cannot carry named arguments.\n if (isRoot && node.type !== 'object') {\n out.push({\n path,\n rule: 'root-must-be-object',\n severity: 'error',\n message: `tool input schema root must be { type: \"object\" }, got ${JSON.stringify(\n node.type\n )}`\n });\n }\n\n // Banned composition/reference keywords anywhere in the tree.\n for (const key of BANNED_COMPOSITION_KEYS) {\n if (key in node) {\n out.push({\n path: `${path}.${key}`,\n rule: `no-${key.replace('$', '')}`,\n severity: 'error',\n message: `\\`${key}\\` is unsupported/unenforced by strict tool-calling on at least one major provider — flatten it (prefer sibling optional fields + runtime validation, or \\`type: [...]\\` for nullability).`\n });\n }\n }\n\n // `anyOf` is the one union OpenAI keeps in strict mode, but only scalar arms\n // are safe. An object/array arm reintroduces the discriminated-union shape\n // that models stringify — so allow scalar unions (incl. the `[\"string\",\n // \"null\"]` nullable idiom) and flag complex arms.\n if (Array.isArray(node.anyOf)) {\n node.anyOf.forEach((arm, i) => {\n if (isRecord(arm) && !isScalarSchema(arm)) {\n out.push({\n path: `${path}.anyOf[${i}]`,\n rule: 'anyof-scalar-arms-only',\n severity: 'error',\n message:\n 'anyOf arms must be scalar types; an object/array arm is a discriminated union that weaker models serialize as a JSON string. Flatten to sibling fields.'\n });\n }\n lintNode(arm, `${path}.anyOf[${i}]`, depth, false, out);\n });\n }\n\n if (node.type === 'object') {\n // Strict tool-calling (OpenAI et al.) requires closed objects.\n if (node.additionalProperties !== false) {\n out.push({\n path,\n rule: 'object-additional-properties-false',\n severity: 'error',\n message:\n 'object schemas must set `additionalProperties: false` for strict-mode adherence.'\n });\n }\n\n if (depth > MAX_OBJECT_DEPTH_BEFORE_WARN) {\n out.push({\n path,\n rule: 'keep-schema-flat',\n severity: 'warn',\n message: `object nesting depth ${depth} — deep nesting lowers generation quality; keep tool schemas as flat as the domain allows.`\n });\n }\n\n const props = node.properties;\n if (isRecord(props)) {\n for (const [name, sub] of Object.entries(props)) {\n const subPath = `${path}.properties.${name}`;\n // Descriptions measurably improve tool-call accuracy (+10-20%).\n if (\n isRecord(sub) &&\n (typeof sub.description !== 'string' || sub.description.trim() === '')\n ) {\n out.push({\n path: subPath,\n rule: 'property-needs-description',\n severity: 'warn',\n message: `property \\`${name}\\` has no description — add one (+accuracy).`\n });\n }\n lintNode(sub, subPath, depth + 1, false, out);\n }\n }\n }\n\n if (node.type === 'array') {\n if (!isRecord(node.items)) {\n out.push({\n path,\n rule: 'array-needs-typed-items',\n severity: 'warn',\n message:\n 'array schema has no typed `items` — models generate array elements more reliably with a concrete item schema.'\n });\n } else {\n lintNode(node.items, `${path}.items`, depth, false, out);\n }\n }\n}\n\n/** Lint one tool's input schema. Returns all findings (errors + warnings). */\nexport function lintToolSchema(schema: unknown): SchemaFinding[] {\n const findings: SchemaFinding[] = [];\n lintNode(schema, '$', 1, true, findings);\n return findings;\n}\n\nexport const errorsOnly = (findings: SchemaFinding[]): SchemaFinding[] =>\n findings.filter((f) => f.severity === 'error');\n"]}
@@ -1,3 +0,0 @@
1
- import { type SchemaFinding } from './schema-best-practices.js';
2
- export declare function collectSchemaErrors(): SchemaFinding[];
3
- //# sourceMappingURL=schema-best-practices.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-best-practices.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/schema-best-practices.test.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAC;AAgNpC,wBAAgB,mBAAmB,IAAI,aAAa,EAAE,CAIrD"}