@gajae-code/ai 0.1.1

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 (349) hide show
  1. package/CHANGELOG.md +2644 -0
  2. package/README.md +1181 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +66 -0
  5. package/dist/types/auth-broker/index.d.ts +5 -0
  6. package/dist/types/auth-broker/refresher.d.ts +25 -0
  7. package/dist/types/auth-broker/remote-store.d.ts +96 -0
  8. package/dist/types/auth-broker/server.d.ts +32 -0
  9. package/dist/types/auth-broker/types.d.ts +105 -0
  10. package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
  11. package/dist/types/auth-gateway/http.d.ts +39 -0
  12. package/dist/types/auth-gateway/index.d.ts +3 -0
  13. package/dist/types/auth-gateway/server.d.ts +17 -0
  14. package/dist/types/auth-gateway/types.d.ts +115 -0
  15. package/dist/types/auth-storage.d.ts +641 -0
  16. package/dist/types/cli.d.ts +2 -0
  17. package/dist/types/index.d.ts +49 -0
  18. package/dist/types/model-cache.d.ts +17 -0
  19. package/dist/types/model-manager.d.ts +62 -0
  20. package/dist/types/model-thinking.d.ts +71 -0
  21. package/dist/types/models.d.ts +12 -0
  22. package/dist/types/provider-details.d.ts +24 -0
  23. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  24. package/dist/types/provider-models/descriptors.d.ts +48 -0
  25. package/dist/types/provider-models/google.d.ts +20 -0
  26. package/dist/types/provider-models/index.d.ts +5 -0
  27. package/dist/types/provider-models/ollama.d.ts +7 -0
  28. package/dist/types/provider-models/openai-compat.d.ts +237 -0
  29. package/dist/types/provider-models/special.d.ts +16 -0
  30. package/dist/types/providers/amazon-bedrock.d.ts +36 -0
  31. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  32. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  33. package/dist/types/providers/anthropic.d.ts +188 -0
  34. package/dist/types/providers/aws-credentials.d.ts +43 -0
  35. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  36. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  37. package/dist/types/providers/azure-openai-responses.d.ts +15 -0
  38. package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
  39. package/dist/types/providers/cursor.d.ts +42 -0
  40. package/dist/types/providers/error-message.d.ts +27 -0
  41. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  42. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  43. package/dist/types/providers/google-auth.d.ts +24 -0
  44. package/dist/types/providers/google-gemini-cli.d.ts +72 -0
  45. package/dist/types/providers/google-gemini-headers.d.ts +18 -0
  46. package/dist/types/providers/google-shared.d.ts +163 -0
  47. package/dist/types/providers/google-types.d.ts +138 -0
  48. package/dist/types/providers/google-vertex.d.ts +7 -0
  49. package/dist/types/providers/google.d.ts +4 -0
  50. package/dist/types/providers/grammar.d.ts +1 -0
  51. package/dist/types/providers/kimi.d.ts +27 -0
  52. package/dist/types/providers/mock.d.ts +175 -0
  53. package/dist/types/providers/ollama.d.ts +6 -0
  54. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  55. package/dist/types/providers/openai-chat-server-schema.d.ts +814 -0
  56. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  57. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  58. package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
  59. package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
  60. package/dist/types/providers/openai-codex-responses.d.ts +67 -0
  61. package/dist/types/providers/openai-completions-compat.d.ts +25 -0
  62. package/dist/types/providers/openai-completions.d.ts +33 -0
  63. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  64. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  65. package/dist/types/providers/openai-responses-shared.d.ts +89 -0
  66. package/dist/types/providers/openai-responses.d.ts +32 -0
  67. package/dist/types/providers/pi-native-client.d.ts +13 -0
  68. package/dist/types/providers/pi-native-server.d.ts +68 -0
  69. package/dist/types/providers/register-builtins.d.ts +31 -0
  70. package/dist/types/providers/synthetic.d.ts +26 -0
  71. package/dist/types/providers/transform-messages.d.ts +12 -0
  72. package/dist/types/providers/vision-guard.d.ts +8 -0
  73. package/dist/types/rate-limit-utils.d.ts +19 -0
  74. package/dist/types/stream.d.ts +24 -0
  75. package/dist/types/types.d.ts +746 -0
  76. package/dist/types/usage/claude.d.ts +3 -0
  77. package/dist/types/usage/gemini.d.ts +2 -0
  78. package/dist/types/usage/github-copilot.d.ts +7 -0
  79. package/dist/types/usage/google-antigravity.d.ts +2 -0
  80. package/dist/types/usage/kimi.d.ts +2 -0
  81. package/dist/types/usage/minimax-code.d.ts +2 -0
  82. package/dist/types/usage/openai-codex.d.ts +3 -0
  83. package/dist/types/usage/shared.d.ts +1 -0
  84. package/dist/types/usage/zai.d.ts +2 -0
  85. package/dist/types/usage.d.ts +258 -0
  86. package/dist/types/utils/abort.d.ts +19 -0
  87. package/dist/types/utils/anthropic-auth.d.ts +31 -0
  88. package/dist/types/utils/discovery/antigravity.d.ts +61 -0
  89. package/dist/types/utils/discovery/codex.d.ts +38 -0
  90. package/dist/types/utils/discovery/cursor.d.ts +23 -0
  91. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  92. package/dist/types/utils/discovery/index.d.ts +4 -0
  93. package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
  94. package/dist/types/utils/event-stream.d.ts +28 -0
  95. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  96. package/dist/types/utils/foundry.d.ts +1 -0
  97. package/dist/types/utils/h2-fetch.d.ts +22 -0
  98. package/dist/types/utils/http-inspector.d.ts +31 -0
  99. package/dist/types/utils/idle-iterator.d.ts +67 -0
  100. package/dist/types/utils/json-parse.d.ts +10 -0
  101. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
  102. package/dist/types/utils/oauth/anthropic.d.ts +22 -0
  103. package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
  104. package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
  105. package/dist/types/utils/oauth/callback-server.d.ts +57 -0
  106. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  107. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  108. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  109. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  110. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  111. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  112. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  113. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  114. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  115. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  116. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  117. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  118. package/dist/types/utils/oauth/index.d.ts +38 -0
  119. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  120. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  121. package/dist/types/utils/oauth/kimi.d.ts +21 -0
  122. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  123. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  124. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  125. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  126. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  127. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  128. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  129. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  130. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  131. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  132. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  133. package/dist/types/utils/oauth/perplexity.d.ts +9 -0
  134. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  135. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  136. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  137. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  138. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  139. package/dist/types/utils/oauth/together.d.ts +1 -0
  140. package/dist/types/utils/oauth/types.d.ts +44 -0
  141. package/dist/types/utils/oauth/venice.d.ts +18 -0
  142. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  143. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  144. package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
  145. package/dist/types/utils/oauth/zai.d.ts +18 -0
  146. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  147. package/dist/types/utils/overflow.d.ts +54 -0
  148. package/dist/types/utils/parse-bind.d.ts +23 -0
  149. package/dist/types/utils/provider-response.d.ts +3 -0
  150. package/dist/types/utils/retry-after.d.ts +3 -0
  151. package/dist/types/utils/retry.d.ts +26 -0
  152. package/dist/types/utils/schema/adapt.d.ts +24 -0
  153. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  154. package/dist/types/utils/schema/dereference.d.ts +11 -0
  155. package/dist/types/utils/schema/draft.d.ts +10 -0
  156. package/dist/types/utils/schema/equality.d.ts +4 -0
  157. package/dist/types/utils/schema/fields.d.ts +49 -0
  158. package/dist/types/utils/schema/index.d.ts +13 -0
  159. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  160. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  161. package/dist/types/utils/schema/normalize.d.ts +93 -0
  162. package/dist/types/utils/schema/spill.d.ts +8 -0
  163. package/dist/types/utils/schema/stamps.d.ts +25 -0
  164. package/dist/types/utils/schema/types.d.ts +4 -0
  165. package/dist/types/utils/schema/wire.d.ts +54 -0
  166. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  167. package/dist/types/utils/sse-debug.d.ts +10 -0
  168. package/dist/types/utils/tool-call-healing.d.ts +71 -0
  169. package/dist/types/utils/tool-choice.d.ts +50 -0
  170. package/dist/types/utils/validation.d.ts +17 -0
  171. package/dist/types/utils.d.ts +28 -0
  172. package/package.json +146 -0
  173. package/src/api-registry.ts +96 -0
  174. package/src/auth-broker/client.ts +358 -0
  175. package/src/auth-broker/index.ts +5 -0
  176. package/src/auth-broker/refresher.ts +127 -0
  177. package/src/auth-broker/remote-store.ts +623 -0
  178. package/src/auth-broker/server.ts +644 -0
  179. package/src/auth-broker/types.ts +127 -0
  180. package/src/auth-broker/wire-schemas.ts +200 -0
  181. package/src/auth-gateway/http.ts +194 -0
  182. package/src/auth-gateway/index.ts +3 -0
  183. package/src/auth-gateway/server.ts +717 -0
  184. package/src/auth-gateway/types.ts +134 -0
  185. package/src/auth-storage.ts +4104 -0
  186. package/src/cli.ts +262 -0
  187. package/src/index.ts +54 -0
  188. package/src/model-cache.ts +129 -0
  189. package/src/model-manager.ts +450 -0
  190. package/src/model-thinking.ts +691 -0
  191. package/src/models.json +73853 -0
  192. package/src/models.json.d.ts +9 -0
  193. package/src/models.ts +56 -0
  194. package/src/prompts/turn-aborted-guidance.md +4 -0
  195. package/src/provider-details.ts +90 -0
  196. package/src/provider-models/bundled-references.ts +38 -0
  197. package/src/provider-models/descriptors.ts +308 -0
  198. package/src/provider-models/google.ts +91 -0
  199. package/src/provider-models/index.ts +5 -0
  200. package/src/provider-models/ollama.ts +153 -0
  201. package/src/provider-models/openai-compat.ts +2275 -0
  202. package/src/provider-models/special.ts +67 -0
  203. package/src/providers/amazon-bedrock.ts +849 -0
  204. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  205. package/src/providers/anthropic-messages-server.ts +677 -0
  206. package/src/providers/anthropic.ts +2696 -0
  207. package/src/providers/aws-credentials.ts +501 -0
  208. package/src/providers/aws-eventstream.ts +185 -0
  209. package/src/providers/aws-sigv4.ts +218 -0
  210. package/src/providers/azure-openai-responses.ts +337 -0
  211. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  212. package/src/providers/cursor/proto/agent.proto +3526 -0
  213. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  214. package/src/providers/cursor/proto/buf.yaml +17 -0
  215. package/src/providers/cursor.ts +2561 -0
  216. package/src/providers/error-message.ts +21 -0
  217. package/src/providers/github-copilot-headers.ts +140 -0
  218. package/src/providers/gitlab-duo.ts +372 -0
  219. package/src/providers/google-auth.ts +252 -0
  220. package/src/providers/google-gemini-cli.ts +795 -0
  221. package/src/providers/google-gemini-headers.ts +41 -0
  222. package/src/providers/google-shared.ts +902 -0
  223. package/src/providers/google-types.ts +167 -0
  224. package/src/providers/google-vertex.ts +88 -0
  225. package/src/providers/google.ts +41 -0
  226. package/src/providers/grammar.ts +70 -0
  227. package/src/providers/kimi.ts +52 -0
  228. package/src/providers/mock.ts +500 -0
  229. package/src/providers/ollama.ts +544 -0
  230. package/src/providers/openai-anthropic-shim.ts +138 -0
  231. package/src/providers/openai-chat-server-schema.ts +243 -0
  232. package/src/providers/openai-chat-server.ts +628 -0
  233. package/src/providers/openai-codex/constants.ts +43 -0
  234. package/src/providers/openai-codex/request-transformer.ts +161 -0
  235. package/src/providers/openai-codex/response-handler.ts +81 -0
  236. package/src/providers/openai-codex-responses.ts +2598 -0
  237. package/src/providers/openai-completions-compat.ts +279 -0
  238. package/src/providers/openai-completions.ts +1853 -0
  239. package/src/providers/openai-responses-server-schema.ts +290 -0
  240. package/src/providers/openai-responses-server.ts +1183 -0
  241. package/src/providers/openai-responses-shared.ts +800 -0
  242. package/src/providers/openai-responses.ts +621 -0
  243. package/src/providers/pi-native-client.ts +228 -0
  244. package/src/providers/pi-native-server.ts +210 -0
  245. package/src/providers/register-builtins.ts +412 -0
  246. package/src/providers/synthetic.ts +50 -0
  247. package/src/providers/transform-messages.ts +309 -0
  248. package/src/providers/vision-guard.ts +31 -0
  249. package/src/rate-limit-utils.ts +84 -0
  250. package/src/stream.ts +895 -0
  251. package/src/types.ts +884 -0
  252. package/src/usage/claude.ts +431 -0
  253. package/src/usage/gemini.ts +250 -0
  254. package/src/usage/github-copilot.ts +421 -0
  255. package/src/usage/google-antigravity.ts +201 -0
  256. package/src/usage/kimi.ts +271 -0
  257. package/src/usage/minimax-code.ts +31 -0
  258. package/src/usage/openai-codex.ts +503 -0
  259. package/src/usage/shared.ts +10 -0
  260. package/src/usage/zai.ts +247 -0
  261. package/src/usage.ts +183 -0
  262. package/src/utils/abort.ts +51 -0
  263. package/src/utils/anthropic-auth.ts +87 -0
  264. package/src/utils/discovery/antigravity.ts +261 -0
  265. package/src/utils/discovery/codex.ts +371 -0
  266. package/src/utils/discovery/cursor.ts +306 -0
  267. package/src/utils/discovery/gemini.ts +248 -0
  268. package/src/utils/discovery/index.ts +4 -0
  269. package/src/utils/discovery/openai-compatible.ts +224 -0
  270. package/src/utils/event-stream.ts +142 -0
  271. package/src/utils/fireworks-model-id.ts +30 -0
  272. package/src/utils/foundry.ts +8 -0
  273. package/src/utils/h2-fetch.ts +60 -0
  274. package/src/utils/http-inspector.ts +176 -0
  275. package/src/utils/idle-iterator.ts +250 -0
  276. package/src/utils/json-parse.ts +148 -0
  277. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  278. package/src/utils/oauth/anthropic.ts +200 -0
  279. package/src/utils/oauth/api-key-login.ts +87 -0
  280. package/src/utils/oauth/api-key-validation.ts +92 -0
  281. package/src/utils/oauth/callback-server.ts +276 -0
  282. package/src/utils/oauth/cerebras.ts +16 -0
  283. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  284. package/src/utils/oauth/cursor.ts +157 -0
  285. package/src/utils/oauth/deepseek.ts +53 -0
  286. package/src/utils/oauth/firepass.ts +24 -0
  287. package/src/utils/oauth/fireworks.ts +15 -0
  288. package/src/utils/oauth/github-copilot.ts +362 -0
  289. package/src/utils/oauth/gitlab-duo.ts +123 -0
  290. package/src/utils/oauth/google-antigravity.ts +200 -0
  291. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  292. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  293. package/src/utils/oauth/huggingface.ts +62 -0
  294. package/src/utils/oauth/index.ts +444 -0
  295. package/src/utils/oauth/kagi.ts +47 -0
  296. package/src/utils/oauth/kilo.ts +87 -0
  297. package/src/utils/oauth/kimi.ts +254 -0
  298. package/src/utils/oauth/litellm.ts +47 -0
  299. package/src/utils/oauth/lm-studio.ts +38 -0
  300. package/src/utils/oauth/minimax-code.ts +78 -0
  301. package/src/utils/oauth/moonshot.ts +16 -0
  302. package/src/utils/oauth/nanogpt.ts +15 -0
  303. package/src/utils/oauth/nvidia.ts +70 -0
  304. package/src/utils/oauth/oauth.html +199 -0
  305. package/src/utils/oauth/ollama-cloud.ts +28 -0
  306. package/src/utils/oauth/ollama.ts +47 -0
  307. package/src/utils/oauth/openai-codex.ts +299 -0
  308. package/src/utils/oauth/opencode.ts +49 -0
  309. package/src/utils/oauth/parallel.ts +46 -0
  310. package/src/utils/oauth/perplexity.ts +206 -0
  311. package/src/utils/oauth/pkce.ts +18 -0
  312. package/src/utils/oauth/qianfan.ts +58 -0
  313. package/src/utils/oauth/qwen-portal.ts +60 -0
  314. package/src/utils/oauth/synthetic.ts +16 -0
  315. package/src/utils/oauth/tavily.ts +46 -0
  316. package/src/utils/oauth/together.ts +16 -0
  317. package/src/utils/oauth/types.ts +94 -0
  318. package/src/utils/oauth/venice.ts +59 -0
  319. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  320. package/src/utils/oauth/vllm.ts +40 -0
  321. package/src/utils/oauth/xiaomi.ts +137 -0
  322. package/src/utils/oauth/zai.ts +60 -0
  323. package/src/utils/oauth/zenmux.ts +15 -0
  324. package/src/utils/overflow.ts +137 -0
  325. package/src/utils/parse-bind.ts +54 -0
  326. package/src/utils/provider-response.ts +30 -0
  327. package/src/utils/retry-after.ts +110 -0
  328. package/src/utils/retry.ts +54 -0
  329. package/src/utils/schema/CONSTRAINTS.md +164 -0
  330. package/src/utils/schema/adapt.ts +36 -0
  331. package/src/utils/schema/compatibility.ts +435 -0
  332. package/src/utils/schema/dereference.ts +98 -0
  333. package/src/utils/schema/draft.ts +341 -0
  334. package/src/utils/schema/equality.ts +97 -0
  335. package/src/utils/schema/fields.ts +190 -0
  336. package/src/utils/schema/index.ts +13 -0
  337. package/src/utils/schema/json-schema-validator.ts +577 -0
  338. package/src/utils/schema/meta-validator.ts +167 -0
  339. package/src/utils/schema/normalize.ts +1588 -0
  340. package/src/utils/schema/spill.ts +43 -0
  341. package/src/utils/schema/stamps.ts +97 -0
  342. package/src/utils/schema/types.ts +11 -0
  343. package/src/utils/schema/wire.ts +213 -0
  344. package/src/utils/schema/zod-decontaminate.ts +331 -0
  345. package/src/utils/sse-debug.ts +289 -0
  346. package/src/utils/tool-call-healing.ts +271 -0
  347. package/src/utils/tool-choice.ts +99 -0
  348. package/src/utils/validation.ts +1019 -0
  349. package/src/utils.ts +166 -0
@@ -0,0 +1,164 @@
1
+ # Schema Constraints
2
+
3
+ This document is the operational contract for schema normalization/strictness in `packages/ai/src/utils/schema`.
4
+
5
+ ## Scope
6
+
7
+ - Applies to provider-facing tool schemas produced by:
8
+ - `normalize.ts` — Google, CCA, MCP, OpenAI Responses, and OpenAI strict-mode (sanitize + enforce) sanitization. All schema walkers live here.
9
+ - `adapt.ts` — thin composer wrapping `tryEnforceStrictSchema` for provider call sites, plus the `PI_NO_STRICT` env flag callers consult to opt out of strict mode.
10
+ - `fields.ts` — keyword classification sets used by the walkers.
11
+ - Covers OpenAI-style strict mode, OpenAI Responses `oneOf` rejection, Google schema constraints, and Cloud Code Assist Anthropic constraints.
12
+ ---
13
+
14
+ ## 1) OpenAI-style strict mode (`adaptSchemaForStrict` / `tryEnforceStrictSchema`)
15
+
16
+ When strict mode is requested (`strict=true` at call site), the schema MUST satisfy all of the following after adaptation:
17
+
18
+ 1. **Non-structural keywords are removed before strict enforcement**
19
+ - Sanitization uses `sanitizeSchemaForStrictMode`.
20
+ - Removed keys include formatting/validation/decorative keywords and unsupported structural extras:
21
+ - `format`, `pattern`, `minLength`, `maxLength`, `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
22
+ - `minItems`, `maxItems`, `uniqueItems`, `multipleOf`
23
+ - `$schema`, `examples`, `default`, `title`, `$comment`
24
+ - `if`, `then`, `else`, `not`
25
+ - `unevaluatedProperties`, `unevaluatedItems`, `patternProperties`
26
+ - `propertyNames`, `contains`, `minContains`, `maxContains`
27
+ - `dependentRequired`, `dependentSchemas`
28
+ - `contentEncoding`, `contentMediaType`, `contentSchema`
29
+ - `deprecated`, `readOnly`, `writeOnly`
30
+ - `minProperties`, `maxProperties`
31
+ - `$dynamicRef`, `$dynamicAnchor`
32
+ - Before stripping `default`, its value is inlined into the sibling `description` as ` (default: X)` so that strict-mode providers retain the default hint in free-form text. Inlining is skipped when `description` already contains `(default:` or when no sibling `description` is present.
33
+
34
+ 2. **`const` is normalized to `enum`**
35
+ - If a node contains `const`, strict sanitization converts it to `enum: [const]`.
36
+
37
+ 3. **Object and tuple strictness is enforced recursively**
38
+ - Every object node gets `additionalProperties: false`.
39
+ - Every property key is included in `required`.
40
+ - Optional properties are wrapped as nullable unions:
41
+ - `anyOf: [<original schema>, { "type": "null" }]`.
42
+ - Tuple entries in `prefixItems` are strictified recursively.
43
+
44
+ 4. **Schema nodes must be representable in strict mode**
45
+ - Nodes without `type`, combinator, `$ref`, or `not` are invalid in strict enforcement and MUST throw.
46
+ - Example invalid node: `{}` or `{ items: {} }`.
47
+
48
+ 5. **Failure mode is fail-open to non-strict**
49
+ - `tryEnforceStrictSchema` MUST return `{ strict: false, schema: original }` when strict enforcement throws.
50
+ - It MUST NOT emit partially-broken strict schema.
51
+
52
+ 6. **Provider payload strict flag must match effective strictness**
53
+ - Callers MUST send `strict: true` only if enforcement succeeded (`effectiveStrict === true`).
54
+
55
+ ---
56
+
57
+ ## 2) Google Gemini / Vertex / Gemini CLI (`normalizeSchemaForGoogle`)
58
+
59
+ Schemas sent on the Google JSON Schema path MUST follow:
60
+
61
+ 1. **Unsupported JSON Schema keywords are stripped (except property names under `properties`)**
62
+ - Unsupported keys (`UNSUPPORTED_SCHEMA_FIELDS`):
63
+ - `$schema`, `$ref`, `$defs`, `$dynamicRef`, `$dynamicAnchor`
64
+ - `examples`, `prefixItems`, `unevaluatedProperties`, `unevaluatedItems`
65
+ - `patternProperties`, `additionalProperties`
66
+ - `minItems`, `maxItems`, `minLength`, `maxLength`
67
+ - `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`
68
+ - `pattern`, `format`
69
+ - Important: keys inside a `properties` object are treated as property names and MUST NOT be stripped by keyword match.
70
+ - Human-meaningful stripped keys (`pattern`, `format`, min/max constraints, `default`, `examples`, etc.) are appended to the sibling `description` as an Anthropic-style spill block: `{pattern: "^foo$", minimum: 0}`. Structural/meta keys such as `$ref`, `$defs`, and `additionalProperties` are not spilled.
71
+
72
+ 2. **`type` arrays are normalized to scalar type + nullable marker**
73
+ - `type: ["T", "null"]` becomes `type: "T"` and `nullable: true`.
74
+ - Google expects scalar type, not `type[]`.
75
+
76
+ 3. **`const` is converted to `enum`**
77
+ - If `const` exists, schema uses/merges `enum` with the const value.
78
+
79
+ 4. **Object schemas get an explicit properties map**
80
+ - `{ "type": "object" }` becomes `{ "type": "object", "properties": {} }`.
81
+ ---
82
+
83
+ ## 3) Anthropic model via Cloud Code Assist (`normalizeSchemaForCCA`)
84
+
85
+ For Cloud Code Assist Anthropic tool declarations, schema MUST satisfy stricter constraints than generic Google path.
86
+
87
+ ### 3.1 Transport contract
88
+
89
+ 1. **Use legacy `parameters` field** (not `parametersJsonSchema`) for CCA Anthropic model.
90
+ 2. CCA path uses the full `normalizeSchemaForCCA` pipeline.
91
+
92
+ ### 3.2 Sanitization contract
93
+
94
+ 1. Start with Google unsupported-key stripping behavior.
95
+ 2. **`nullable` keyword MUST be stripped** in CCA Anthropic model path.
96
+ 3. `type: ["T", "null"]` becomes `type: "T"` with no `nullable` marker.
97
+ 4. Human-meaningful stripped keys are appended to `description` with the same spill format used by the Google dispatcher.
98
+
99
+ ### 3.3 Combiner/union normalization contract
100
+
101
+ 1. Object-only `anyOf`/`oneOf` variants SHOULD be merged into a single object shape where safe.
102
+ 2. Same-type combiner variants SHOULD be collapsed to one schema.
103
+ 3. Mixed-type combiner variants MAY be lossy-collapsed to first non-null scalar type when required for CCA acceptance.
104
+ 4. Residual combiners are recursively stripped where collapsible (`stripResidualCombiners`).
105
+
106
+ ### 3.4 Nullable property normalization contract
107
+
108
+ 1. Property-local nullability expressed as:
109
+ - `nullable: true`, or
110
+ - `type` union including `null`, or
111
+ - `anyOf`/`oneOf` with one `{ "type": "null" }` branch
112
+ MUST be converted to non-required property semantics where possible.
113
+ 2. If a property is detected nullable after normalization, it MUST be removed from `required`.
114
+
115
+ ### 3.5 Residual incompatibility gate (hard stop)
116
+
117
+ After normalization, schema MUST NOT contain any of:
118
+
119
+ - `type` as array
120
+ - `type: "null"`
121
+ - `nullable` key
122
+ - `anyOf`, `oneOf`, `allOf` arrays
123
+
124
+ If any remain, schema is incompatible.
125
+
126
+ ### 3.6 Validation + fallback contract
127
+
128
+ 1. Normalized schema is validated with AJV 2020 schema validation.
129
+ 2. If invalid OR residual incompatibilities exist, output MUST fallback to:
130
+
131
+ ```json
132
+ { "type": "object", "properties": {} }
133
+ ```
134
+
135
+ 3. Fallback is per-tool and fail-open; one bad tool schema MUST NOT fail the whole request.
136
+
137
+ ---
138
+
139
+ ## 4) Practical provider mapping
140
+
141
+ - **OpenAI-compatible strict paths** (`openai-completions`, `openai-responses`, `openai-code-responses`):
142
+ - Use `adaptSchemaForStrict`.
143
+ - Emit `strict: true` only when effective strict enforcement succeeded.
144
+
145
+ - **Google Gemini/Vertex/Gemini CLI (non-CCA Anthropic model)**:
146
+ - Use `normalizeSchemaForGoogle` and send schema on `parametersJsonSchema` path.
147
+
148
+ - **Cloud Code Assist Anthropic models (`model.id` starts with `anthropic-model-`)**:
149
+ - Use `normalizeSchemaForCCA` and send sanitized normalized schema in `parameters`.
150
+
151
+ ---
152
+
153
+ ## 5) Maintenance rules
154
+
155
+ When adding/changing provider adapters:
156
+
157
+ 1. Any new unsupported keyword MUST be added to the appropriate set in `fields.ts`.
158
+ 2. Any new normalization rule MUST include regression tests under `packages/ai/test`.
159
+ 3. Never bypass adapter helpers (`adaptSchemaForStrict`, `normalizeSchemaForGoogle`, `normalizeSchemaForCCA`, `normalizeSchemaForMCP`) in provider code.
160
+ 4. If a provider rejects schema with partial support, prefer deterministic per-tool fallback over request-wide failure.
161
+
162
+ ## 6) Gemini CLI / Antigravity CCA parity
163
+
164
+ The Gemini CLI / Antigravity Anthropic model path MUST run the same full `normalizeSchemaForCCA` pipeline as the shared Google Anthropic path. It MUST NOT call only the first keyword-stripping pass, because that leaves object combiners, nullable unions, residual combiners, and fallback gating inconsistent between transports.
@@ -0,0 +1,36 @@
1
+ import { $flag } from "@gajae-code/utils";
2
+ import { upgradeJsonSchemaTo202012 } from "./draft";
3
+ import { tryEnforceStrictSchema } from "./normalize";
4
+
5
+ /**
6
+ * Set when callers want to globally bypass OpenAI strict-mode enforcement
7
+ * (e.g. for debugging a provider that misreports strict support, or when
8
+ * comparing strict vs non-strict outputs).
9
+ *
10
+ * Honored by every provider that emits `strict: true` on its function tools —
11
+ * see `openai-completions`, `openai-responses`, `OpenAI code provider-responses`, and
12
+ * the strict candidate selection in `anthropic`.
13
+ */
14
+ export const NO_STRICT = $flag("PI_NO_STRICT");
15
+
16
+ /**
17
+ * Consolidated helper for OpenAI-style strict schema enforcement.
18
+ *
19
+ * Each provider computes its own `strict` boolean (logic differs), then calls
20
+ * this to handle the tryEnforceStrictSchema dance uniformly:
21
+ * - Draft-07-shaped inputs are upgraded to draft 2020-12 first.
22
+ * - If `strict` is false, passes the upgraded schema through unchanged.
23
+ * - If `strict` is true, attempts to enforce strict mode; falls back to
24
+ * non-strict if the schema isn't representable.
25
+ */
26
+ export function adaptSchemaForStrict(
27
+ schema: Record<string, unknown>,
28
+ strict: boolean,
29
+ ): { schema: Record<string, unknown>; strict: boolean } {
30
+ const upgraded = upgradeJsonSchemaTo202012(schema) as Record<string, unknown>;
31
+ if (!strict) {
32
+ return { schema: upgraded, strict: false };
33
+ }
34
+
35
+ return tryEnforceStrictSchema(upgraded);
36
+ }
@@ -0,0 +1,435 @@
1
+ import {
2
+ CCA_UNSUPPORTED_SCHEMA_FIELDS,
3
+ COMBINATOR_KEYS,
4
+ NON_STRUCTURAL_SCHEMA_KEYS,
5
+ UNSUPPORTED_SCHEMA_FIELDS,
6
+ } from "./fields";
7
+ import { isValidJsonSchema } from "./meta-validator";
8
+ import { isJsonObject, type JsonObject } from "./types";
9
+
10
+ /**
11
+ * Schema compatibility audits.
12
+ *
13
+ * Each provider has a different idea of what JSON Schema features it accepts
14
+ * for tool definitions. The normalizers in `normalize.ts`, `strict-mode`,
15
+ * and `adapt.ts` rewrite incoming schemas to fit. This module is the
16
+ * *audit* counterpart: it walks a (presumably already-sanitized) schema and
17
+ * reports any feature the target provider would reject. Tests use it to lock
18
+ * down the contract; the runtime uses it to fail-open with diagnostic logs
19
+ * rather than silently shipping a broken tool definition.
20
+ */
21
+ export type SchemaCompatibilityProvider = "openai-strict" | "google" | "cloud-code-assist-claude";
22
+
23
+ export interface SchemaCompatibilityViolation {
24
+ path: string;
25
+ rule: string;
26
+ message: string;
27
+ key?: string;
28
+ value?: unknown;
29
+ }
30
+
31
+ export interface SchemaCompatibilityResult {
32
+ provider: SchemaCompatibilityProvider;
33
+ compatible: boolean;
34
+ violations: SchemaCompatibilityViolation[];
35
+ }
36
+
37
+ export interface StrictSchemaEnforcementResult {
38
+ schema: Record<string, unknown>;
39
+ strict: boolean;
40
+ }
41
+
42
+ // Per-provider forbidden-key sets. Subsets of the shared `fields.ts` constants
43
+ // plus a few provider-specific extras (`const`, `nullable`) folded in here so
44
+ // each rule is defined in exactly one place.
45
+ const STRICT_FORBIDDEN_KEYS: Record<string, true> = { ...NON_STRUCTURAL_SCHEMA_KEYS, const: true, nullable: true };
46
+ const GOOGLE_FORBIDDEN_KEYS: Record<string, true> = { ...UNSUPPORTED_SCHEMA_FIELDS, const: true };
47
+ const CCA_FORBIDDEN_KEYS: Record<string, true> = { ...CCA_UNSUPPORTED_SCHEMA_FIELDS, const: true };
48
+
49
+ // Keys whose values are JSON-Schema *containers* (arrays of values, scalars,
50
+ // etc.) rather than nested schemas. The traversal must skip these — recursing
51
+ // would walk into `enum` strings or `default` objects and emit spurious
52
+ // violations against keys that happen to share JSON-Schema keyword names.
53
+ const NON_SCHEMA_CONTAINER_ARRAY_KEYS: Record<string, true> = {
54
+ enum: true,
55
+ required: true,
56
+ examples: true,
57
+ type: true,
58
+ };
59
+ const NON_SCHEMA_CONTAINER_OBJECT_KEYS: Record<string, true> = { const: true, default: true, example: true };
60
+
61
+ interface TraversalState {
62
+ path: string;
63
+ }
64
+
65
+ function createViolation(
66
+ path: string,
67
+ rule: string,
68
+ message: string,
69
+ key?: string,
70
+ value?: unknown,
71
+ ): SchemaCompatibilityViolation {
72
+ return {
73
+ path,
74
+ rule,
75
+ message,
76
+ ...(key === undefined ? {} : { key }),
77
+ ...(value === undefined ? {} : { value }),
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Recursively visit every schema node in a JSON Schema tree.
83
+ *
84
+ * The walker is *structural*, not type-aware: it knows which keywords contain
85
+ * nested schemas vs. plain values, so it descends into `properties.*`,
86
+ * `$defs.*`, `items`, combinator arrays, etc. but never into `enum`, `const`,
87
+ * `default`, or `type` arrays.
88
+ */
89
+ function walkSchema(
90
+ value: unknown,
91
+ state: TraversalState,
92
+ visitNode: (node: JsonObject, state: TraversalState) => void,
93
+ ): void {
94
+ if (Array.isArray(value)) {
95
+ for (let index = 0; index < value.length; index++) {
96
+ walkSchema(value[index], { path: `${state.path}[${index}]` }, visitNode);
97
+ }
98
+ return;
99
+ }
100
+
101
+ if (!isJsonObject(value)) {
102
+ return;
103
+ }
104
+
105
+ visitNode(value, state);
106
+
107
+ for (const key in value) {
108
+ // Schema-map keywords: value is `{ name: schema, … }`. Recurse into each
109
+ // entry's schema rather than the map object itself.
110
+ const entry = value[key];
111
+ if (key === "properties" || key === "$defs" || key === "definitions" || key === "dependentSchemas") {
112
+ if (isJsonObject(entry)) {
113
+ for (const name in entry) {
114
+ const child = entry[name];
115
+ walkSchema(child, { path: `${state.path}.${key}.${name}` }, visitNode);
116
+ }
117
+ }
118
+ continue;
119
+ }
120
+ // Non-schema container keywords — values are not schemas, do not descend.
121
+
122
+ if (key in NON_SCHEMA_CONTAINER_ARRAY_KEYS || key in NON_SCHEMA_CONTAINER_OBJECT_KEYS) {
123
+ continue;
124
+ }
125
+ // Array-of-schemas keywords (e.g. `allOf`, `anyOf`, `oneOf`, `prefixItems`).
126
+
127
+ if (Array.isArray(entry)) {
128
+ for (let index = 0; index < entry.length; index++) {
129
+ walkSchema(entry[index], { path: `${state.path}.${key}[${index}]` }, visitNode);
130
+ }
131
+ continue;
132
+ }
133
+
134
+ if (isJsonObject(entry)) {
135
+ walkSchema(entry, { path: `${state.path}.${key}` }, visitNode);
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Strict-mode audit (OpenAI Responses / OpenAI code backend `strict: true`):
142
+ * 1. Forbid keywords that strict mode disallows (`format`, `pattern`, `const`,
143
+ * `nullable`, etc. — see `STRICT_FORBIDDEN_KEYS`).
144
+ * 2. Every node must declare *something* concrete: a `type`, a combinator,
145
+ * a `$ref`, or a `not` branch. Empty `{}` is rejected.
146
+ * 3. Object nodes must set `additionalProperties: false`, declare a real
147
+ * `properties` map, and require every property in that map. Required
148
+ * properties not in `properties` are also rejected — strict mode demands
149
+ * a closed object shape.
150
+ */
151
+ function validateStrictNode(node: JsonObject, state: TraversalState): SchemaCompatibilityViolation[] {
152
+ const violations: SchemaCompatibilityViolation[] = [];
153
+
154
+ for (const key in node) {
155
+ const value = node[key];
156
+ if (!(key in STRICT_FORBIDDEN_KEYS)) {
157
+ continue;
158
+ }
159
+
160
+ violations.push(
161
+ createViolation(
162
+ `${state.path}.${key}`,
163
+ "strict-forbidden-key",
164
+ `Strict schema contains forbidden key "${key}"`,
165
+ key,
166
+ value,
167
+ ),
168
+ );
169
+ }
170
+ // Rule 2: node must declare at least one concrete shape descriptor.
171
+
172
+ const hasCombinator = COMBINATOR_KEYS.some(key => Array.isArray(node[key]));
173
+ const hasRef = typeof node.$ref === "string";
174
+ const hasNot = isJsonObject(node.not);
175
+ if (node.type === undefined && !hasCombinator && !hasRef && !hasNot) {
176
+ violations.push(
177
+ createViolation(
178
+ state.path,
179
+ "strict-unrepresentable-node",
180
+ "Strict schema node must declare type, combinator, $ref, or not",
181
+ ),
182
+ );
183
+ }
184
+ // Rules 3a-3d apply only to object-shaped nodes.
185
+
186
+ const isObjectNode = node.type === "object" || isJsonObject(node.properties);
187
+ if (!isObjectNode) {
188
+ return violations;
189
+ }
190
+
191
+ if (node.additionalProperties !== false) {
192
+ violations.push(
193
+ createViolation(
194
+ `${state.path}.additionalProperties`,
195
+ "strict-object-additional-properties",
196
+ "Strict object schema must set additionalProperties to false",
197
+ "additionalProperties",
198
+ node.additionalProperties,
199
+ ),
200
+ );
201
+ }
202
+ // 3b: `properties` must exist and be an object — without it strict mode has nothing to validate.
203
+
204
+ if (!isJsonObject(node.properties)) {
205
+ violations.push(
206
+ createViolation(
207
+ `${state.path}.properties`,
208
+ "strict-object-properties",
209
+ "Strict object schema must provide an object-valued properties map",
210
+ "properties",
211
+ node.properties,
212
+ ),
213
+ );
214
+ return violations;
215
+ }
216
+
217
+ const propertyNames = Object.keys(node.properties);
218
+ const requiredValues = Array.isArray(node.required)
219
+ ? node.required.filter((entry): entry is string => typeof entry === "string")
220
+ : [];
221
+ const requiredSet = new Set(requiredValues);
222
+
223
+ for (const propertyName of propertyNames) {
224
+ if (requiredSet.has(propertyName)) {
225
+ continue;
226
+ }
227
+ violations.push(
228
+ createViolation(
229
+ `${state.path}.required`,
230
+ "strict-object-required",
231
+ `Strict object schema must require property "${propertyName}"`,
232
+ "required",
233
+ node.required,
234
+ ),
235
+ );
236
+ }
237
+ // 3d: any property declared in `required` but missing from `properties` is unrepresentable.
238
+
239
+ const propertyNameSet = new Set(propertyNames);
240
+ for (const requiredKey of requiredValues) {
241
+ if (propertyNameSet.has(requiredKey)) {
242
+ continue;
243
+ }
244
+ violations.push(
245
+ createViolation(
246
+ `${state.path}.required`,
247
+ "strict-object-required-extra",
248
+ `Strict object schema requires non-existent property "${requiredKey}"`,
249
+ "required",
250
+ node.required,
251
+ ),
252
+ );
253
+ }
254
+
255
+ return violations;
256
+ }
257
+
258
+ function validateGoogleNode(node: JsonObject, state: TraversalState): SchemaCompatibilityViolation[] {
259
+ const violations: SchemaCompatibilityViolation[] = [];
260
+
261
+ for (const key in node) {
262
+ const value = node[key];
263
+ if (!(key in GOOGLE_FORBIDDEN_KEYS)) {
264
+ continue;
265
+ }
266
+ violations.push(
267
+ createViolation(
268
+ `${state.path}.${key}`,
269
+ "google-forbidden-key",
270
+ `Google schema contains unsupported key "${key}"`,
271
+ key,
272
+ value,
273
+ ),
274
+ );
275
+ }
276
+
277
+ if (Array.isArray(node.type)) {
278
+ violations.push(
279
+ createViolation(
280
+ `${state.path}.type`,
281
+ "google-type-array",
282
+ "Google schema type must be a scalar string, not an array",
283
+ "type",
284
+ node.type,
285
+ ),
286
+ );
287
+ }
288
+
289
+ return violations;
290
+ }
291
+
292
+ function validateCloudCodeAssistNode(node: JsonObject, state: TraversalState): SchemaCompatibilityViolation[] {
293
+ const violations: SchemaCompatibilityViolation[] = [];
294
+
295
+ for (const key in node) {
296
+ const value = node[key];
297
+ if (key in CCA_FORBIDDEN_KEYS) {
298
+ violations.push(
299
+ createViolation(
300
+ `${state.path}.${key}`,
301
+ "cca-forbidden-key",
302
+ `Cloud Code Assist schema contains unsupported key "${key}"`,
303
+ key,
304
+ value,
305
+ ),
306
+ );
307
+ }
308
+ }
309
+
310
+ if (Array.isArray(node.type)) {
311
+ violations.push(
312
+ createViolation(
313
+ `${state.path}.type`,
314
+ "cca-type-array",
315
+ "Cloud Code Assist schema forbids array-valued type",
316
+ "type",
317
+ node.type,
318
+ ),
319
+ );
320
+ }
321
+
322
+ if (node.type === "null") {
323
+ violations.push(
324
+ createViolation(
325
+ `${state.path}.type`,
326
+ "cca-null-type",
327
+ 'Cloud Code Assist schema forbids type: "null"',
328
+ "type",
329
+ node.type,
330
+ ),
331
+ );
332
+ }
333
+
334
+ if (Object.hasOwn(node, "nullable")) {
335
+ violations.push(
336
+ createViolation(
337
+ `${state.path}.nullable`,
338
+ "cca-nullable-key",
339
+ "Cloud Code Assist schema forbids nullable keyword",
340
+ "nullable",
341
+ node.nullable,
342
+ ),
343
+ );
344
+ }
345
+
346
+ for (const key of COMBINATOR_KEYS) {
347
+ if (Array.isArray(node[key])) {
348
+ violations.push(
349
+ createViolation(
350
+ `${state.path}.${key}`,
351
+ "cca-combiner",
352
+ `Cloud Code Assist schema forbids ${key}`,
353
+ key,
354
+ node[key],
355
+ ),
356
+ );
357
+ }
358
+ }
359
+
360
+ return violations;
361
+ }
362
+
363
+ function validateCloudCodeAssistSchema(schema: unknown): SchemaCompatibilityViolation[] {
364
+ if (isValidJsonSchema(schema)) {
365
+ return [];
366
+ }
367
+ return [
368
+ createViolation(
369
+ "root",
370
+ "cca-meta-schema-validation",
371
+ "Cloud Code Assist schema is not a structurally valid JSON Schema",
372
+ ),
373
+ ];
374
+ }
375
+
376
+ export function validateSchemaCompatibility(
377
+ schema: unknown,
378
+ provider: SchemaCompatibilityProvider,
379
+ ): SchemaCompatibilityResult {
380
+ const violations: SchemaCompatibilityViolation[] = [];
381
+
382
+ switch (provider) {
383
+ case "openai-strict": {
384
+ walkSchema(schema, { path: "root" }, (node, state) => {
385
+ violations.push(...validateStrictNode(node, state));
386
+ });
387
+ break;
388
+ }
389
+ case "google": {
390
+ walkSchema(schema, { path: "root" }, (node, state) => {
391
+ violations.push(...validateGoogleNode(node, state));
392
+ });
393
+ break;
394
+ }
395
+ case "cloud-code-assist-claude": {
396
+ walkSchema(schema, { path: "root" }, (node, state) => {
397
+ violations.push(...validateCloudCodeAssistNode(node, state));
398
+ });
399
+ violations.push(...validateCloudCodeAssistSchema(schema));
400
+ break;
401
+ }
402
+ }
403
+
404
+ return {
405
+ provider,
406
+ compatible: violations.length === 0,
407
+ violations,
408
+ };
409
+ }
410
+
411
+ export function validateStrictSchemaEnforcement(
412
+ originalSchema: Record<string, unknown>,
413
+ result: StrictSchemaEnforcementResult,
414
+ ): SchemaCompatibilityResult {
415
+ if (result.strict) {
416
+ return validateSchemaCompatibility(result.schema, "openai-strict");
417
+ }
418
+
419
+ const violations: SchemaCompatibilityViolation[] = [];
420
+ if (result.schema !== originalSchema) {
421
+ violations.push(
422
+ createViolation(
423
+ "root",
424
+ "strict-fail-open-original-schema",
425
+ "Strict fail-open must return the original schema object when strict=false",
426
+ ),
427
+ );
428
+ }
429
+
430
+ return {
431
+ provider: "openai-strict",
432
+ compatible: violations.length === 0,
433
+ violations,
434
+ };
435
+ }