@cartanova/qgrid-cli 2.10.9 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/bundle/dist/_virtual/_rolldown/runtime.js +1 -10
  2. package/bundle/dist/application/monit/log-buffer.js +12 -6
  3. package/bundle/dist/application/monit/monit.frame.js +11 -3
  4. package/bundle/dist/application/monit/monit.types.js +1 -1
  5. package/bundle/dist/application/qgrid/conv-routing.js +9 -6
  6. package/bundle/dist/application/qgrid/expired-token-reminder.js +2 -2
  7. package/bundle/dist/application/qgrid/oauth.js +31 -4
  8. package/bundle/dist/application/qgrid/qgrid-image-generation.js +1 -1
  9. package/bundle/dist/application/qgrid/qgrid-response-format.js +22 -20
  10. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +43 -34
  11. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +128 -93
  12. package/bundle/dist/application/qgrid/qgrid.frame.js +930 -49
  13. package/bundle/dist/application/qgrid/qgrid.types.js +212 -5
  14. package/bundle/dist/application/qgrid/schema-prompt.js +75 -48
  15. package/bundle/dist/application/qgrid/slack-user-map.js +1 -1
  16. package/bundle/dist/application/qgrid/token-death.js +1 -1
  17. package/bundle/dist/application/qgrid/token-subscriber.js +83 -30
  18. package/bundle/dist/application/qgrid/token-trigger-setup.js +1 -1
  19. package/bundle/dist/application/qgrid/token-window-keepalive.js +8 -6
  20. package/bundle/dist/application/qgrid/tool-emulation-schema.js +13 -13
  21. package/bundle/dist/application/qgrid/tool-emulation.js +19 -15
  22. package/bundle/dist/application/request-log/json-schema-type-text.js +29 -12
  23. package/bundle/dist/application/request-log/request-log.model.js +83 -69
  24. package/bundle/dist/application/request-log/request-log.types.js +8 -2
  25. package/bundle/dist/application/request-log/zod-code-format.js +1 -1
  26. package/bundle/dist/application/request-log-step/request-log-step.model.js +1 -1
  27. package/bundle/dist/application/request-log-step/request-log-step.types.js +1 -1
  28. package/bundle/dist/application/setting/setting.constant.js +1 -1
  29. package/bundle/dist/application/setting/setting.model.js +2 -2
  30. package/bundle/dist/application/setting/setting.store.js +2 -2
  31. package/bundle/dist/application/setting/setting.types.js +1 -1
  32. package/bundle/dist/application/sonamu.generated.js +40 -16
  33. package/bundle/dist/application/sonamu.generated.sso.js +2 -1
  34. package/bundle/dist/application/token/token.model.js +26 -9
  35. package/bundle/dist/application/token/token.types.js +20 -3
  36. package/bundle/dist/i18n/en.js +15 -1
  37. package/bundle/dist/i18n/ko.js +15 -1
  38. package/bundle/dist/i18n/sd.generated.js +2 -1
  39. package/bundle/dist/migrations/20260807133553_manual__backfill_request_log_cost.js +8 -6
  40. package/bundle/dist/migrations/20261018215031_alter_request_logs_add1_alter1.js +15 -0
  41. package/bundle/dist/server-bootstrap.js +4 -2
  42. package/bundle/dist/server-error-handler.js +1 -1
  43. package/bundle/dist/sonamu.config.js +37 -8
  44. package/bundle/dist/startup-migrations.js +17 -7
  45. package/bundle/dist/testing/global.js +1 -1
  46. package/bundle/dist/testing/schema-bench.js +0 -0
  47. package/bundle/dist/testing/test-database-name.js +1 -1
  48. package/bundle/dist/utils/providers/anthropic/anthropic-constants.js +1 -1
  49. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +11 -5
  50. package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +4 -4
  51. package/bundle/dist/utils/providers/anthropic/anthropic-thinking.js +1 -1
  52. package/bundle/dist/utils/providers/anthropic/claude-session.js +20 -19
  53. package/bundle/dist/utils/providers/anthropic/fence-strip.js +1 -1
  54. package/bundle/dist/utils/providers/anthropic/stream-json-adapter.js +49 -23
  55. package/bundle/dist/utils/providers/common/credentials.js +5 -1
  56. package/bundle/dist/utils/providers/common/effort.js +3 -3
  57. package/bundle/dist/utils/providers/common/model-cost.js +69 -14
  58. package/bundle/dist/utils/providers/common/provider-types.js +29 -1
  59. package/bundle/dist/utils/providers/common/schema-validation.js +78 -40
  60. package/bundle/dist/utils/providers/common/smooth-weighted-round-robin.js +1 -1
  61. package/bundle/dist/utils/providers/common/strictifier.js +62 -43
  62. package/bundle/dist/utils/providers/common/thinking.js +3 -2
  63. package/bundle/dist/utils/providers/common/ttft.js +1 -1
  64. package/bundle/dist/utils/providers/grok/grok-backend-protocol.js +427 -0
  65. package/bundle/dist/utils/providers/grok/grok-constants.js +55 -0
  66. package/bundle/dist/utils/providers/grok/grok-direct-client.js +196 -0
  67. package/bundle/dist/utils/providers/grok/grok-dispatcher.js +1313 -0
  68. package/bundle/dist/utils/providers/grok/grok-headers.js +118 -0
  69. package/bundle/dist/utils/providers/grok/grok-media-client.js +420 -0
  70. package/bundle/dist/utils/providers/grok/grok-models.js +114 -0
  71. package/bundle/dist/utils/providers/grok/grok-oauth.js +455 -0
  72. package/bundle/dist/utils/providers/grok/grok-quota.js +119 -0
  73. package/bundle/dist/utils/providers/grok/grok-refresh.js +192 -0
  74. package/bundle/dist/utils/providers/grok/grok-types.js +186 -0
  75. package/bundle/dist/utils/providers/grok/grok-voice-client.js +199 -0
  76. package/bundle/dist/utils/providers/grok/index.js +11 -0
  77. package/bundle/dist/utils/providers/openai/openai-backend-protocol.js +111 -83
  78. package/bundle/dist/utils/providers/openai/openai-callback-relay.js +3 -2
  79. package/bundle/dist/utils/providers/openai/openai-constants.js +1 -1
  80. package/bundle/dist/utils/providers/openai/openai-direct-client.js +23 -7
  81. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +143 -106
  82. package/bundle/dist/utils/providers/openai/openai-image-generation.js +48 -33
  83. package/bundle/dist/utils/providers/openai/openai-oauth.js +28 -13
  84. package/bundle/dist/utils/providers/openai/openai-quota.js +38 -27
  85. package/bundle/dist/utils/providers/openai/openai-refresh.js +17 -9
  86. package/bundle/dist/utils/providers/openai/openai-sse.js +1 -1
  87. package/bundle/dist/utils/providers/openai/openai-transport-config.js +1 -1
  88. package/bundle/dist/utils/quiet-hours.js +1 -1
  89. package/bundle/dist/utils/slack-notify.js +14 -8
  90. package/bundle/dist/utils/subset-loaders.js +4 -5
  91. package/bundle/src/application/monit/log-buffer.ts +23 -9
  92. package/bundle/src/application/monit/monit.frame.ts +22 -5
  93. package/bundle/src/application/monit/monit.types.ts +6 -0
  94. package/bundle/src/application/qgrid/conv-routing.test.ts +76 -4
  95. package/bundle/src/application/qgrid/conv-routing.ts +35 -9
  96. package/bundle/src/application/qgrid/expired-token-reminder.ts +9 -1
  97. package/bundle/src/application/qgrid/oauth.ts +51 -15
  98. package/bundle/src/application/qgrid/qgrid-image-generation.ts +9 -0
  99. package/bundle/src/application/qgrid/qgrid-response-format.ts +50 -25
  100. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +33 -0
  101. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +87 -40
  102. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +97 -0
  103. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +274 -126
  104. package/bundle/src/application/qgrid/qgrid.frame.test.ts +1013 -3
  105. package/bundle/src/application/qgrid/qgrid.frame.ts +1415 -60
  106. package/bundle/src/application/qgrid/qgrid.types.ts +335 -0
  107. package/bundle/src/application/qgrid/schema-prompt.ts +105 -41
  108. package/bundle/src/application/qgrid/slack-user-map.ts +5 -0
  109. package/bundle/src/application/qgrid/token-death.ts +4 -0
  110. package/bundle/src/application/qgrid/token-subscriber.test.ts +21 -19
  111. package/bundle/src/application/qgrid/token-subscriber.ts +186 -53
  112. package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -0
  113. package/bundle/src/application/qgrid/token-window-keepalive.ts +49 -6
  114. package/bundle/src/application/qgrid/tool-emulation-schema.ts +33 -29
  115. package/bundle/src/application/qgrid/tool-emulation.ts +52 -27
  116. package/bundle/src/application/request-log/json-schema-type-text.test.ts +5 -5
  117. package/bundle/src/application/request-log/json-schema-type-text.ts +81 -26
  118. package/bundle/src/application/request-log/request-log.entity.json +8 -0
  119. package/bundle/src/application/request-log/request-log.model.test.ts +111 -4
  120. package/bundle/src/application/request-log/request-log.model.ts +279 -145
  121. package/bundle/src/application/request-log/request-log.types.ts +17 -0
  122. package/bundle/src/application/request-log/zod-code-format.ts +11 -0
  123. package/bundle/src/application/request-log-step/request-log-step.model.ts +1 -0
  124. package/bundle/src/application/request-log-step/request-log-step.types.ts +2 -0
  125. package/bundle/src/application/setting/setting.constant.ts +9 -0
  126. package/bundle/src/application/setting/setting.model.ts +12 -2
  127. package/bundle/src/application/setting/setting.store.ts +5 -1
  128. package/bundle/src/application/setting/setting.types.ts +8 -0
  129. package/bundle/src/application/sonamu.generated.http +135 -0
  130. package/bundle/src/application/sonamu.generated.sso.ts +3 -0
  131. package/bundle/src/application/sonamu.generated.ts +29 -0
  132. package/bundle/src/application/token/token.identity.test.ts +20 -0
  133. package/bundle/src/application/token/token.model.ts +66 -7
  134. package/bundle/src/application/token/token.types.ts +24 -1
  135. package/bundle/src/i18n/en.ts +19 -0
  136. package/bundle/src/i18n/ko.ts +21 -0
  137. package/bundle/src/i18n/sd.generated.ts +24 -2
  138. package/bundle/src/migrations/20260807133553_manual__backfill_request_log_cost.ts +16 -8
  139. package/bundle/src/migrations/20261018215031_alter_request_logs_add1_alter1.ts +15 -0
  140. package/bundle/src/server-bootstrap.ts +4 -2
  141. package/bundle/src/server-error-handler.ts +1 -0
  142. package/bundle/src/sonamu.config.ts +69 -7
  143. package/bundle/src/startup-migrations.ts +27 -9
  144. package/bundle/src/testing/global.ts +20 -0
  145. package/bundle/src/testing/schema-bench.ts +105 -30
  146. package/bundle/src/testing/test-database-name.ts +2 -0
  147. package/bundle/src/utils/providers/anthropic/anthropic-constants.ts +8 -0
  148. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +41 -6
  149. package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +7 -3
  150. package/bundle/src/utils/providers/anthropic/anthropic-thinking.ts +6 -0
  151. package/bundle/src/utils/providers/anthropic/claude-session.ts +58 -24
  152. package/bundle/src/utils/providers/anthropic/fence-strip.ts +11 -0
  153. package/bundle/src/utils/providers/anthropic/stream-json-adapter.ts +110 -45
  154. package/bundle/src/utils/providers/common/credentials.ts +8 -0
  155. package/bundle/src/utils/providers/common/effort.ts +9 -2
  156. package/bundle/src/utils/providers/common/model-cost.test.ts +48 -1
  157. package/bundle/src/utils/providers/common/model-cost.ts +259 -158
  158. package/bundle/src/utils/providers/common/provider-dispatcher.ts +98 -2
  159. package/bundle/src/utils/providers/common/provider-types.ts +46 -3
  160. package/bundle/src/utils/providers/common/schema-validation.test.ts +5 -1
  161. package/bundle/src/utils/providers/common/schema-validation.ts +124 -56
  162. package/bundle/src/utils/providers/common/smooth-weighted-round-robin.ts +4 -0
  163. package/bundle/src/utils/providers/common/strictifier.ts +185 -59
  164. package/bundle/src/utils/providers/common/thinking.ts +2 -2
  165. package/bundle/src/utils/providers/common/ttft.ts +4 -2
  166. package/bundle/src/utils/providers/grok/__fixtures__/golden-responses-1.0.60.json +164 -0
  167. package/bundle/src/utils/providers/grok/grok-backend-protocol.test.ts +325 -0
  168. package/bundle/src/utils/providers/grok/grok-backend-protocol.ts +721 -0
  169. package/bundle/src/utils/providers/grok/grok-constants.ts +105 -0
  170. package/bundle/src/utils/providers/grok/grok-direct-client.test.ts +179 -0
  171. package/bundle/src/utils/providers/grok/grok-direct-client.ts +313 -0
  172. package/bundle/src/utils/providers/grok/grok-dispatcher.test.ts +1395 -0
  173. package/bundle/src/utils/providers/grok/grok-dispatcher.ts +2105 -0
  174. package/bundle/src/utils/providers/grok/grok-headers.test.ts +149 -0
  175. package/bundle/src/utils/providers/grok/grok-headers.ts +199 -0
  176. package/bundle/src/utils/providers/grok/grok-media-client.test.ts +446 -0
  177. package/bundle/src/utils/providers/grok/grok-media-client.ts +703 -0
  178. package/bundle/src/utils/providers/grok/grok-models.test.ts +96 -0
  179. package/bundle/src/utils/providers/grok/grok-models.ts +161 -0
  180. package/bundle/src/utils/providers/grok/grok-oauth.test.ts +406 -0
  181. package/bundle/src/utils/providers/grok/grok-oauth.ts +771 -0
  182. package/bundle/src/utils/providers/grok/grok-quota.test.ts +142 -0
  183. package/bundle/src/utils/providers/grok/grok-quota.ts +198 -0
  184. package/bundle/src/utils/providers/grok/grok-refresh.test.ts +239 -0
  185. package/bundle/src/utils/providers/grok/grok-refresh.ts +325 -0
  186. package/bundle/src/utils/providers/grok/grok-types.test.ts +252 -0
  187. package/bundle/src/utils/providers/grok/grok-types.ts +374 -0
  188. package/bundle/src/utils/providers/grok/grok-voice-client.test.ts +321 -0
  189. package/bundle/src/utils/providers/grok/grok-voice-client.ts +284 -0
  190. package/bundle/src/utils/providers/grok/index.ts +20 -0
  191. package/bundle/src/utils/providers/openai/openai-backend-protocol.ts +221 -137
  192. package/bundle/src/utils/providers/openai/openai-callback-relay.ts +12 -1
  193. package/bundle/src/utils/providers/openai/openai-constants.ts +2 -0
  194. package/bundle/src/utils/providers/openai/openai-direct-client.ts +111 -21
  195. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +322 -169
  196. package/bundle/src/utils/providers/openai/openai-image-generation.ts +116 -36
  197. package/bundle/src/utils/providers/openai/openai-oauth.ts +48 -20
  198. package/bundle/src/utils/providers/openai/openai-quota.ts +77 -46
  199. package/bundle/src/utils/providers/openai/openai-refresh.ts +42 -18
  200. package/bundle/src/utils/providers/openai/openai-sse.ts +27 -3
  201. package/bundle/src/utils/providers/openai/openai-transport-config.ts +2 -0
  202. package/bundle/src/utils/quiet-hours.ts +12 -1
  203. package/bundle/src/utils/slack-notify.ts +44 -13
  204. package/bundle/src/utils/subset-loaders.ts +7 -7
  205. package/bundle/web-dist/client/assets/clsx-5XM_pHvN.js +1 -0
  206. package/bundle/web-dist/client/assets/index-BA7uQ-6c.js +31 -0
  207. package/bundle/web-dist/client/assets/index-Be2qCZmI.css +1 -0
  208. package/bundle/web-dist/client/assets/logs-C47OJGEI.js +1 -0
  209. package/bundle/web-dist/client/assets/monit-BS9no0dU.js +4 -0
  210. package/bundle/web-dist/client/assets/plus-sdACTfH0.js +1 -0
  211. package/bundle/web-dist/client/assets/refresh-cw-CY8o_1cT.js +1 -0
  212. package/bundle/web-dist/client/assets/request-model-BRtFuZvj.js +1 -0
  213. package/bundle/web-dist/client/assets/routes-kqgkUxE8.js +5 -0
  214. package/bundle/web-dist/client/assets/{services.generated-BsieU37H.js → services.generated-CefiDXEv.js} +5 -5
  215. package/bundle/web-dist/client/assets/settings-DNDvFGSw.js +1 -0
  216. package/bundle/web-dist/client/assets/show-u8Np69q3.js +2 -0
  217. package/bundle/web-dist/client/assets/tokens-QvP410NW.js +1 -0
  218. package/bundle/web-dist/client/assets/total-cost-MnfKU8IN.js +1 -0
  219. package/bundle/web-dist/client/assets/use-oauth-login-flow-Bzdh28Ws.js +1 -0
  220. package/bundle/web-dist/client/assets/use-update-token-mutation-CfJKaxmR.js +1 -0
  221. package/bundle/web-dist/client/assets/vendor-react-CUFFrVN9.js +244 -0
  222. package/bundle/web-dist/client/assets/vendor-tanstack-Bf_jag9G.js +1 -0
  223. package/bundle/web-dist/client/index.html +6 -6
  224. package/bundle/web-dist/server/assets/clsx-D_N5PN_2.js +16 -0
  225. package/bundle/web-dist/server/assets/{lazyRouteComponent-Dtd0NR4r.js → lazyRouteComponent-D8iTIHyn.js} +129 -2
  226. package/bundle/web-dist/server/assets/logs-B3OTd9hy.js +825 -0
  227. package/bundle/web-dist/server/assets/{monit-DvzribSE.js → monit-B2hqorPe.js} +385 -126
  228. package/bundle/web-dist/server/assets/plus-DwG7h9kq.js +28 -0
  229. package/bundle/web-dist/server/assets/{react-dom-B4a6-uuR.js → react-dom-BIYQzBqG.js} +2 -129
  230. package/bundle/web-dist/server/assets/refresh-cw-mMBkwOq0.js +44 -0
  231. package/bundle/web-dist/server/assets/{request-model-CbqfnQOJ.js → request-model-I6OdODSH.js} +15 -6
  232. package/bundle/web-dist/server/assets/{routes-BAPWTizT.js → routes-B95YlUv2.js} +760 -213
  233. package/bundle/web-dist/server/assets/{services.generated-CEbGjsI0.js → services.generated-DQATJ6qf.js} +12821 -12194
  234. package/bundle/web-dist/server/assets/settings-C3gbGk0m.js +830 -0
  235. package/bundle/web-dist/server/assets/{show-CxVZTAdv.js → show-Bxaj3rhH.js} +1355 -419
  236. package/bundle/web-dist/server/assets/tokens-CZjH3cB7.js +1038 -0
  237. package/bundle/web-dist/server/assets/total-cost-fTE9Xy82.js +18 -0
  238. package/bundle/web-dist/server/assets/use-oauth-login-flow-CKDHgc3U.js +2379 -0
  239. package/bundle/web-dist/server/assets/{use-update-token-mutation-CfYmk_4_.js → use-update-token-mutation-BEbgOzaa.js} +1 -1
  240. package/bundle/web-dist/server/entry-server.generated.js +4303 -22810
  241. package/dist/cli.js +9 -9
  242. package/package.json +4 -11
  243. package/postinstall.mjs +12 -0
  244. package/skills/qgrid/SKILL.md +1 -0
  245. package/skills/qgrid/references/cli-env-and-server-boot.md +4 -1
  246. package/skills/qgrid/references/decision-rationale.md +24 -1
  247. package/skills/qgrid/references/grok-runtime.md +89 -0
  248. package/skills/qgrid/references/provider-runtime-differences.md +30 -29
  249. package/skills/qgrid/references/repo-map.md +10 -0
  250. package/skills/qgrid/references/request-log-run-lifecycle.md +1 -1
  251. package/skills/qgrid/references/sonamu-api-web-flow.md +1 -1
  252. package/skills/qgrid/references/token-auth-quota-lifecycle.md +56 -3
  253. package/bundle/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -406
  254. package/bundle/dist/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.js +0 -1507
  255. package/bundle/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.js +0 -887
  256. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2756
  257. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/index.js +0 -8
  258. package/bundle/dist/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/utils.js +0 -8
  259. package/bundle/dist/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.js +0 -922
  260. package/bundle/dist/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.js +0 -386
  261. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -121
  262. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/constants.js +0 -51
  263. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.js +0 -1317
  264. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.js +0 -561
  265. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/error.js +0 -40
  266. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.js +0 -272
  267. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.js +0 -27
  268. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.js +0 -77
  269. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.js +0 -381
  270. package/bundle/dist/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/timers.js +0 -51
  271. package/bundle/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2986
  272. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
  273. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
  274. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
  275. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
  276. package/bundle/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
  277. package/bundle/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1126
  278. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
  279. package/bundle/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
  280. package/bundle/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -517
  281. package/bundle/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -101
  282. package/bundle/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.11.2_@emnapi_runtime@1.11.2_@types_node@25.0.7_esbuild@0.27.4_5b35fc1fb85d2af4d105e0d74f940f50/node_modules/vite/dist/node/module-runner.js +0 -1265
  283. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +0 -9
  284. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +0 -41
  285. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -19
  286. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +0 -202
  287. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/index.DdgEv5B1.js +0 -46
  288. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +0 -119
  289. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/test.DNmyFkvJ.js +0 -3044
  290. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +0 -59
  291. package/bundle/dist/node_modules/.pnpm/vitest@4.1.10_@opentelemetry_api@1.9.0_@types_node@25.0.7_@vitest_coverage-v8@4.1.10_vi_a1977b93af7bb69308bcb1dbab39b618/node_modules/vitest/dist/index.js +0 -28
  292. package/bundle/dist/testing/setup-mocks.js +0 -16
  293. package/bundle/src/testing/setup-mocks.ts +0 -44
  294. package/bundle/web-dist/client/assets/index-Bw2s88Ot.css +0 -1
  295. package/bundle/web-dist/client/assets/index-CrXKu_J8.js +0 -106
  296. package/bundle/web-dist/client/assets/logs-B2SmWJXA.js +0 -1
  297. package/bundle/web-dist/client/assets/monit-D-JwXXoA.js +0 -1
  298. package/bundle/web-dist/client/assets/plus-B62CuqT8.js +0 -1
  299. package/bundle/web-dist/client/assets/refresh-cw-NNcU0Cmq.js +0 -1
  300. package/bundle/web-dist/client/assets/request-model-CojtinqK.js +0 -1
  301. package/bundle/web-dist/client/assets/routes-C5r4gTvY.js +0 -5
  302. package/bundle/web-dist/client/assets/settings-D3tDSAck.js +0 -1
  303. package/bundle/web-dist/client/assets/show-Cah_kUKC.js +0 -2
  304. package/bundle/web-dist/client/assets/tokens-DU2Iyxxd.js +0 -1
  305. package/bundle/web-dist/client/assets/use-oauth-login-flow-D0UaR9_e.js +0 -1
  306. package/bundle/web-dist/client/assets/use-update-token-mutation-DAq0F8p-.js +0 -1
  307. package/bundle/web-dist/client/assets/vendor-react-DH4kUfK2.js +0 -9
  308. package/bundle/web-dist/client/assets/vendor-tanstack-DJRQAbrY.js +0 -1
  309. package/bundle/web-dist/server/assets/logs-l91avYif.js +0 -509
  310. package/bundle/web-dist/server/assets/plus-KZrECewT.js +0 -19
  311. package/bundle/web-dist/server/assets/refresh-cw-BND93JrF.js +0 -23
  312. package/bundle/web-dist/server/assets/settings-BZR5HSAL.js +0 -461
  313. package/bundle/web-dist/server/assets/tokens-D1hb4eKk.js +0 -448
  314. package/bundle/web-dist/server/assets/use-oauth-login-flow-Cvdy0zF8.js +0 -178
  315. package/dist/cli.d.ts +0 -10
  316. /package/bundle/web-dist/client/assets/{cost-DH2jZbrw.js → cost-DB3HbU7T.js} +0 -0
  317. /package/bundle/web-dist/client/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
  318. /package/bundle/web-dist/server/assets/{cost-eEHeTRT5.js → cost-BC9yzyA9.js} +0 -0
@@ -8,20 +8,32 @@ import { createFenceStripTransform } from "../../utils/providers/anthropic/fence
8
8
  import { getAccessToken } from "../../utils/providers/common/credentials";
9
9
  import { calculateCostUsd } from "../../utils/providers/common/model-cost";
10
10
  import {
11
+ type GenerateRequest,
11
12
  type GenerateResult,
13
+ type GenerateStreamCallbacks,
14
+ type MediaDispatcher,
15
+ type ProviderDispatcher,
12
16
  type StreamCallbacks,
17
+ type VoiceDispatcher,
13
18
  } from "../../utils/providers/common/provider-dispatcher";
14
- import { type JsonValue } from "../../utils/providers/common/provider-types";
19
+ import {
20
+ isJsonObject,
21
+ isProviderId,
22
+ type JsonValue,
23
+ type ProviderId,
24
+ } from "../../utils/providers/common/provider-types";
15
25
  import {
16
26
  parseAndValidateCallerSchemas,
17
27
  serializeAndValidateDispatchSchema,
18
28
  } from "../../utils/providers/common/schema-validation";
19
29
  import { strictify } from "../../utils/providers/common/strictifier";
30
+ import { type GrokTokenStateSnapshot } from "../../utils/providers/grok/grok-dispatcher";
20
31
  import {
21
32
  ImageGenerationError,
22
33
  type OpenAIDispatcher,
23
34
  } from "../../utils/providers/openai/openai-dispatcher";
24
35
  import { type TokenSubsetA } from "../sonamu.generated";
36
+ import { type TokenCredentials } from "../token/token.types";
25
37
  import { decideConvRouting, issueConvContext } from "./conv-routing";
26
38
  import {
27
39
  maskToken,
@@ -41,6 +53,61 @@ export type InternalQueryInput = QueryInput & {
41
53
  requirePreferredToken?: boolean;
42
54
  };
43
55
 
56
+ // qgrid 상위가 provider 에 요구하는 실행 계약. 스트리밍은 공통 ProviderDispatcher 에 아직 없어서
57
+ // 여기서 합친다.
58
+ export type StreamingProviderDispatcher = ProviderDispatcher & {
59
+ generateStream(req: GenerateRequest, cb: GenerateStreamCallbacks): Promise<void>;
60
+ };
61
+
62
+ // TokenSubscriber 가 grok row 를 전달하는 계약. credentials 는 DB JSONB 원본이며 dispatcher 가
63
+ // 자체 스키마로 검증한다.
64
+ export type GrokTokenRow = {
65
+ id: number;
66
+ name: string;
67
+ credentials: TokenCredentials;
68
+ quotaThreshold: number | null;
69
+ weight: number;
70
+ };
71
+
72
+ export interface GrokTokenSink {
73
+ onTokenAdded(
74
+ id: number,
75
+ name: string,
76
+ credentials: TokenCredentials,
77
+ quotaThreshold: number | null,
78
+ weight: number,
79
+ ): void | Promise<void>;
80
+ onTokenUpdated(
81
+ id: number,
82
+ name: string,
83
+ credentials: TokenCredentials,
84
+ quotaThreshold: number | null,
85
+ weight: number,
86
+ ): void | Promise<void>;
87
+ onTokenRemoved(id: number): void | Promise<void>;
88
+ replaceTokens(rows: GrokTokenRow[]): void | Promise<void>;
89
+ }
90
+
91
+ /** Observability the concrete Grok dispatcher exposes beyond routing. */
92
+ export interface GrokTokenStateSource {
93
+ tokenStates(): GrokTokenStateSnapshot[];
94
+ }
95
+
96
+ /** Media, voice, and token-state reporting are optional capabilities of a Grok dispatcher. */
97
+ export type GrokProviderDispatcher = StreamingProviderDispatcher &
98
+ GrokTokenSink &
99
+ Partial<MediaDispatcher & VoiceDispatcher & GrokTokenStateSource>;
100
+
101
+ export type QgridDispatcherOptions = {
102
+ grokDispatcher?: GrokProviderDispatcher | null;
103
+ };
104
+
105
+ const PROVIDER_LABELS: Record<ProviderId, string> = {
106
+ openai: "OpenAI",
107
+ anthropic: "Anthropic",
108
+ grok: "Grok",
109
+ };
110
+
44
111
  export class QgridDispatcherClass {
45
112
  tokens = new Map<number, TokenSubsetA>();
46
113
 
@@ -49,23 +116,76 @@ export class QgridDispatcherClass {
49
116
  requestCounts = new Map<string, number>();
50
117
 
51
118
  subscriber: TokenSubscriber | null = null;
52
- openaiDispatcher: OpenAIDispatcher | null = null;
53
- anthropicDispatcher: AnthropicDispatcher | null = null;
119
+
120
+ // provider route prefix → 실행 dispatcher. 등록되지 않은 provider 는 not-ready 로 응답한다.
121
+ readonly dispatchers = new Map<ProviderId, StreamingProviderDispatcher>();
122
+ #openaiDispatcher: OpenAIDispatcher | null = null;
123
+ #anthropicDispatcher: AnthropicDispatcher | null = null;
124
+ #grokDispatcher: GrokProviderDispatcher | null = null;
54
125
 
55
126
  // HTTP가 dispatcher보다 먼저 열리므로 초기화 중과 초기화 실패를 구분한다.
56
- startupState: Record<"openai" | "anthropic", ProviderStartupState> = {
127
+ // grok 은 bootstrap 이 붙기 전까지 상태가 없으며, 없으면 기동 중으로 본다.
128
+ startupState: Record<"openai" | "anthropic", ProviderStartupState> &
129
+ Partial<Record<"grok", ProviderStartupState>> = {
57
130
  openai: "starting",
58
131
  anthropic: "starting",
59
132
  };
60
133
 
134
+ constructor(options: QgridDispatcherOptions = {}) {
135
+ if (options.grokDispatcher) this.grokDispatcher = options.grokDispatcher;
136
+ }
137
+
138
+ get openaiDispatcher(): OpenAIDispatcher | null {
139
+ return this.#openaiDispatcher;
140
+ }
141
+
142
+ set openaiDispatcher(dispatcher: OpenAIDispatcher | null) {
143
+ this.#openaiDispatcher = dispatcher;
144
+ this.register("openai", dispatcher);
145
+ }
146
+
147
+ get anthropicDispatcher(): AnthropicDispatcher | null {
148
+ return this.#anthropicDispatcher;
149
+ }
150
+
151
+ set anthropicDispatcher(dispatcher: AnthropicDispatcher | null) {
152
+ this.#anthropicDispatcher = dispatcher;
153
+ this.register("anthropic", dispatcher);
154
+ }
155
+
156
+ get grokDispatcher(): GrokProviderDispatcher | null {
157
+ return this.#grokDispatcher;
158
+ }
159
+
160
+ set grokDispatcher(dispatcher: GrokProviderDispatcher | null) {
161
+ this.#grokDispatcher = dispatcher;
162
+ this.register("grok", dispatcher);
163
+ }
164
+
165
+ register(provider: ProviderId, dispatcher: StreamingProviderDispatcher | null): void {
166
+ if (dispatcher) this.dispatchers.set(provider, dispatcher);
167
+ else this.dispatchers.delete(provider);
168
+ }
169
+
61
170
  // 초기화 중에는 503 + Retry-After, 초기화 실패에는 500을 반환한다.
62
- notReadyError(provider: "openai" | "anthropic"): Error {
63
- const label = provider === "openai" ? "OpenAI" : "Anthropic";
171
+ notReadyError(provider: ProviderId): Error {
172
+ const label = PROVIDER_LABELS[provider];
173
+
64
174
  return this.startupState[provider] === "failed"
65
175
  ? new InternalServerErrorException(SD("qgrid.dispatcherFailed")(label))
66
176
  : new ServiceUnavailableException(SD("qgrid.dispatcherStarting")(label));
67
177
  }
68
178
 
179
+ // prefix 없는/미지원 prefix 는 기존대로 ProcessError, 지원 provider 의 미등록은 not-ready.
180
+ resolveDispatcher(input: QueryInput, provider: string | undefined): ResolvedDispatcher {
181
+ if (!isProviderId(provider)) throw directLlmApiFallbackNotImplemented(input);
182
+ const dispatcher = this.dispatchers.get(provider);
183
+
184
+ if (!dispatcher) throw this.notReadyError(provider);
185
+
186
+ return { provider, dispatcher };
187
+ }
188
+
69
189
  recordCompleted(provider: string, tokenName: string): void {
70
190
  const key = `${provider}\0${tokenName}`;
71
191
  this.requestCounts.set(key, (this.requestCounts.get(key) ?? 0) + 1);
@@ -88,55 +208,12 @@ export class QgridDispatcherClass {
88
208
  const route = parseProviderRoute(input.model);
89
209
  const outputSchema = buildAndValidateStrictOutputSchema(input, route.provider);
90
210
  const answerKind = input.jsonSchema ? ("json" as const) : ("text" as const);
211
+ const { provider, dispatcher } = this.resolveDispatcher(input, route.provider);
91
212
 
92
- if (route.provider === "openai") {
93
- if (!this.openaiDispatcher) throw this.notReadyError("openai");
94
-
95
- const decision = decideConvRouting(input, {
96
- directOpenAI: true,
97
- modelNamespace: `openai/${route.model}`,
98
- });
99
- const result = await this.openaiDispatcher.generate({
100
- model: route.model,
101
- systemPrompt: input.system,
102
- outputSchema,
103
- stopAfterOutputMessage: input.tools?.length
104
- ? (text, phase) => isToolCallEnvelope(text, input.tools!, answerKind, phase)
105
- : undefined,
106
- thinking: input.thinking,
107
- effort: input.thinking === false ? undefined : input.effort,
108
- verbosity: input.verbosity,
109
- reasoningSummary: input.reasoningSummary,
110
- serviceTier: input.serviceTier,
111
- timeoutMs: input.timeout,
112
- coldInput: decision.coldInput,
113
- coldHistory: decision.coldHistory,
114
- promptCacheKey: input.imageGeneration ? undefined : decision.promptCacheKey,
115
- preferredTokenId:
116
- input.preferredTokenId ?? (input.imageGeneration ? undefined : decision.preferredTokenId),
117
- requirePreferredToken: input.requirePreferredToken,
118
- abortSignal,
119
- imageGeneration: input.imageGeneration,
120
- imageGenerationOptions: input.imageGenerationOptions,
121
- });
122
- this.recordCompleted("openai", result.tokenName);
123
-
124
- // 이미지 요청은 cold-only(R8)라 재사용 좌표를 발급하지 않는다. 좌표를 실으면
125
- // sessionKey 소비자의 warm 좌표를 죽은 좌표로 덮어써 다음 텍스트 turn 이 cold 로 떨어진다.
126
- const coord = input.imageGeneration
127
- ? undefined
128
- : issueConvContext(result.threadCoord, decision, result.threadCoord.workerId);
129
-
130
- return applyToolCallEmulation(toEmulationResult(result, input.serviceTier), input.tools, {
131
- threadCoord: coord,
132
- images: result.images,
133
- answerKind,
134
- });
135
- } else if (route.provider === "anthropic") {
136
- if (!this.anthropicDispatcher) throw this.notReadyError("anthropic");
137
-
213
+ if (provider === "anthropic") {
138
214
  const decision = decideConvRouting(input);
139
- const result = await this.anthropicDispatcher.generate({
215
+
216
+ const result = await dispatcher.generate({
140
217
  model: input.model,
141
218
  systemPrompt: composeSystemWithSchemaContract(input.system, input),
142
219
  thinking: input.thinking,
@@ -149,7 +226,8 @@ export class QgridDispatcherClass {
149
226
  imageGeneration: input.imageGeneration,
150
227
  imageGenerationOptions: input.imageGenerationOptions,
151
228
  });
152
- this.recordCompleted("anthropic", result.tokenName);
229
+
230
+ this.recordCompleted(provider, result.tokenName);
153
231
 
154
232
  return applyToolCallEmulation(toEmulationResult(result), input.tools, {
155
233
  threadCoord: issueConvContext(result.threadCoord, decision),
@@ -157,7 +235,33 @@ export class QgridDispatcherClass {
157
235
  });
158
236
  }
159
237
 
160
- throw directLlmApiFallbackNotImplemented(input);
238
+ const decision = decideConvRouting(input, {
239
+ directProvider: provider,
240
+ modelNamespace: `${provider}/${route.model}`,
241
+ });
242
+
243
+ const result = await dispatcher.generate({
244
+ ...buildDirectRequest(input, route.model, outputSchema, answerKind, abortSignal),
245
+ promptCacheKey: input.imageGeneration ? undefined : decision.promptCacheKey,
246
+ preferredTokenId:
247
+ input.preferredTokenId ?? (input.imageGeneration ? undefined : decision.preferredTokenId),
248
+ coldInput: decision.coldInput,
249
+ coldHistory: decision.coldHistory,
250
+ });
251
+
252
+ this.recordCompleted(provider, result.tokenName);
253
+
254
+ // 이미지 요청은 cold-only(R8)라 재사용 좌표를 발급하지 않는다. 좌표를 실으면
255
+ // sessionKey 소비자의 warm 좌표를 죽은 좌표로 덮어써 다음 텍스트 turn 이 cold 로 떨어진다.
256
+ const coord = input.imageGeneration
257
+ ? undefined
258
+ : issueConvContext(result.threadCoord, decision, result.threadCoord.workerId);
259
+
260
+ return applyToolCallEmulation(toEmulationResult(result, input.serviceTier), input.tools, {
261
+ threadCoord: coord,
262
+ images: result.images,
263
+ answerKind,
264
+ });
161
265
  }
162
266
 
163
267
  async queryStream(
@@ -168,77 +272,23 @@ export class QgridDispatcherClass {
168
272
  const route = parseProviderRoute(input.model);
169
273
  const outputSchema = buildAndValidateStrictOutputSchema(input, route.provider);
170
274
  const answerKind = input.jsonSchema ? ("json" as const) : ("text" as const);
275
+ const { provider, dispatcher } = this.resolveDispatcher(input, route.provider);
171
276
 
172
- if (route.provider === "openai") {
173
- if (!this.openaiDispatcher) throw this.notReadyError("openai");
174
-
175
- const decision = decideConvRouting(input, {
176
- directOpenAI: true,
177
- modelNamespace: `openai/${route.model}`,
178
- });
179
- await this.openaiDispatcher.generateStream(
180
- {
181
- model: route.model,
182
- systemPrompt: input.system,
183
- outputSchema,
184
- stopAfterOutputMessage: input.tools?.length
185
- ? (text, phase) => isToolCallEnvelope(text, input.tools!, answerKind, phase)
186
- : undefined,
187
- thinking: input.thinking,
188
- effort: input.thinking === false ? undefined : input.effort,
189
- verbosity: input.verbosity,
190
- reasoningSummary: input.reasoningSummary,
191
- serviceTier: input.serviceTier,
192
- timeoutMs: input.timeout,
193
- coldInput: decision.coldInput,
194
- coldHistory: decision.coldHistory,
195
- promptCacheKey: decision.promptCacheKey,
196
- preferredTokenId: input.preferredTokenId ?? decision.preferredTokenId,
197
- requirePreferredToken: input.requirePreferredToken,
198
- abortSignal,
199
- imageGeneration: input.imageGeneration,
200
- imageGenerationOptions: input.imageGenerationOptions,
201
- },
202
- {
203
- onDelta: cb.onDelta,
204
- onThreadId: cb.onThreadId,
205
- onTurnId: cb.onTurnId,
206
- onComplete: (turnResult) => {
207
- this.recordCompleted("openai", turnResult.tokenName);
208
- cb.onComplete(
209
- applyToolCallEmulation(
210
- toEmulationResult(turnResult, input.serviceTier),
211
- input.tools,
212
- {
213
- threadCoord: issueConvContext(
214
- turnResult.threadCoord,
215
- decision,
216
- turnResult.threadCoord.workerId,
217
- ),
218
- answerKind,
219
- },
220
- ),
221
- );
222
- },
223
- onError: cb.onError,
224
- },
225
- );
226
- return;
227
- } else if (route.provider === "anthropic") {
228
- if (!this.anthropicDispatcher) throw this.notReadyError("anthropic");
229
-
277
+ if (provider === "anthropic") {
230
278
  // 구조화 응답은 최종 텍스트와 델타 모두 코드펜스를 제거한다.
231
279
  const hasSchemaContract = input.jsonSchema !== undefined || Boolean(input.tools?.length);
232
280
  const fenceStrip = hasSchemaContract ? createFenceStripTransform() : undefined;
281
+
233
282
  const emitDelta = fenceStrip
234
283
  ? (text: string) => {
235
284
  const safe = fenceStrip.push(text);
285
+
236
286
  if (safe) cb.onDelta(safe);
237
287
  }
238
288
  : cb.onDelta;
239
289
 
240
290
  const decision = decideConvRouting(input);
241
- await this.anthropicDispatcher.generateStream(
291
+ await dispatcher.generateStream(
242
292
  {
243
293
  model: input.model,
244
294
  systemPrompt: composeSystemWithSchemaContract(input.system, input),
@@ -256,9 +306,10 @@ export class QgridDispatcherClass {
256
306
  onDelta: emitDelta,
257
307
  onThreadId: cb.onThreadId,
258
308
  onComplete: (turnResult) => {
259
- this.recordCompleted("anthropic", turnResult.tokenName);
309
+ this.recordCompleted(provider, turnResult.tokenName);
260
310
  // 닫는 펜스 판정 때문에 보류한 텍스트를 완료 전에 방출한다.
261
311
  const rest = fenceStrip?.flush();
312
+
262
313
  if (rest) cb.onDelta(rest);
263
314
 
264
315
  const issuedCoord = issueConvContext(turnResult.threadCoord, decision);
@@ -272,20 +323,98 @@ export class QgridDispatcherClass {
272
323
  onError: cb.onError,
273
324
  },
274
325
  );
326
+
275
327
  return;
276
328
  }
277
329
 
278
- throw directLlmApiFallbackNotImplemented(input);
330
+ const decision = decideConvRouting(input, {
331
+ directProvider: provider,
332
+ modelNamespace: `${provider}/${route.model}`,
333
+ });
334
+
335
+ await dispatcher.generateStream(
336
+ {
337
+ ...buildDirectRequest(input, route.model, outputSchema, answerKind, abortSignal),
338
+ promptCacheKey: decision.promptCacheKey,
339
+ preferredTokenId: input.preferredTokenId ?? decision.preferredTokenId,
340
+ coldInput: decision.coldInput,
341
+ coldHistory: decision.coldHistory,
342
+ },
343
+ {
344
+ onDelta: cb.onDelta,
345
+ onThreadId: cb.onThreadId,
346
+ onTurnId: cb.onTurnId,
347
+ onComplete: (turnResult) => {
348
+ this.recordCompleted(provider, turnResult.tokenName);
349
+ cb.onComplete(
350
+ applyToolCallEmulation(toEmulationResult(turnResult, input.serviceTier), input.tools, {
351
+ threadCoord: issueConvContext(
352
+ turnResult.threadCoord,
353
+ decision,
354
+ turnResult.threadCoord.workerId,
355
+ ),
356
+ answerKind,
357
+ }),
358
+ );
359
+ },
360
+ onError: cb.onError,
361
+ },
362
+ );
279
363
  }
280
364
  }
281
365
 
282
- function parseProviderRoute(model: string | undefined): { provider?: string; model: string } {
366
+ // Direct provider(OpenAI, Grok) 공통 요청 필드. cache affinity 필드는 stream 여부에 따라 호출부가 채운다.
367
+ function buildDirectRequest(
368
+ input: InternalQueryInput,
369
+ model: string,
370
+ outputSchema: JsonValue | undefined,
371
+ answerKind: "json" | "text",
372
+ abortSignal: AbortSignal | undefined,
373
+ ): Omit<GenerateRequest, "coldInput" | "coldHistory" | "promptCacheKey" | "preferredTokenId"> {
374
+ const request: Omit<
375
+ GenerateRequest,
376
+ "coldInput" | "coldHistory" | "promptCacheKey" | "preferredTokenId"
377
+ > = {
378
+ model,
379
+ systemPrompt: input.system,
380
+ outputSchema,
381
+ stopAfterOutputMessage: input.tools?.length
382
+ ? (text, phase) => isToolCallEnvelope(text, input.tools!, answerKind, phase)
383
+ : undefined,
384
+ thinking: input.thinking,
385
+ effort: input.thinking === false ? undefined : input.effort,
386
+ verbosity: input.verbosity,
387
+ reasoningSummary: input.reasoningSummary,
388
+ serviceTier: input.serviceTier,
389
+ timeoutMs: input.timeout,
390
+ requirePreferredToken: input.requirePreferredToken,
391
+ abortSignal,
392
+ imageGeneration: input.imageGeneration,
393
+ imageGenerationOptions: input.imageGenerationOptions,
394
+ };
395
+
396
+ // Opaque namespaces; the Grok dispatcher reads `xai`, OpenAI ignores it.
397
+ if (input.providerOptions?.xai) {
398
+ request.providerOptions = { xai: { ...input.providerOptions.xai } };
399
+ }
400
+
401
+ return request;
402
+ }
403
+
404
+ interface ProviderRoute {
405
+ provider?: string;
406
+ model: string;
407
+ }
408
+
409
+ function parseProviderRoute(model: string | undefined): ProviderRoute {
283
410
  if (!model?.includes("/")) {
284
411
  return { model: model ?? "" };
285
412
  }
286
413
 
287
414
  const [provider, routedModel] = model.split("/", 2);
415
+
288
416
  if (!provider || !routedModel) throw new ProcessError("unknown model");
417
+
289
418
  return { provider, model: routedModel };
290
419
  }
291
420
 
@@ -301,9 +430,10 @@ export function buildStrictOutputSchema(
301
430
  ): JsonValue | undefined {
302
431
  const outputSchema = buildRawOutputSchema(input);
303
432
 
304
- return outputSchema
305
- ? (strictify(outputSchema as Parameters<typeof strictify>[0], { provider }) as JsonValue)
306
- : undefined;
433
+ if (!outputSchema) return undefined;
434
+
435
+ // Boolean and other non-object schemas have nothing to tighten and pass through unchanged.
436
+ return isJsonObject(outputSchema) ? strictify(outputSchema, { provider }) : outputSchema;
307
437
  }
308
438
 
309
439
  export function buildAndValidateStrictOutputSchema(
@@ -314,11 +444,13 @@ export function buildAndValidateStrictOutputSchema(
314
444
  // Anthropic은 원본 스키마를 검증한 뒤 프롬프트로 전달하고, 응답 검증은 소비자에 맡긴다.
315
445
  if (provider === "anthropic") {
316
446
  parseAndValidateCallerSchemas(input);
447
+
317
448
  return undefined;
318
449
  }
319
450
 
320
451
  const outputSchema = buildStrictOutputSchema(input, provider);
321
452
  serializeAndValidateDispatchSchema(outputSchema, provider);
453
+
322
454
  return outputSchema;
323
455
  }
324
456
 
@@ -333,11 +465,12 @@ function buildRawOutputSchema(
333
465
  }
334
466
 
335
467
  export function completedDriverFromImageFailure(
336
- error: unknown,
468
+ cause: unknown,
337
469
  serviceTier?: string,
338
470
  ): QueryOutput | undefined {
339
- if (!(error instanceof ImageGenerationError) || !error.completedDriver) return undefined;
340
- return applyToolCallEmulation(toEmulationResult(error.completedDriver, serviceTier), undefined, {
471
+ if (!(cause instanceof ImageGenerationError) || !cause.completedDriver) return undefined;
472
+
473
+ return applyToolCallEmulation(toEmulationResult(cause.completedDriver, serviceTier), undefined, {
341
474
  answerKind: "text",
342
475
  });
343
476
  }
@@ -347,8 +480,12 @@ function toEmulationResult(
347
480
  result: GenerateResult,
348
481
  serviceTier?: string,
349
482
  ): Omit<QueryOutput, "content" | "finishReason" | "runContext"> {
350
- const hasProviderCost = result.costUsd !== undefined && result.costUsd > 0;
351
- return {
483
+ // A provider that labels its own cost (Grok ticks) is authoritative even at 0; otherwise only a
484
+ // positive provider cost (Claude Code total_cost_usd) wins over the price table.
485
+ const hasProviderCost =
486
+ result.costUsd !== undefined && (result.costSource === "provider" || result.costUsd > 0);
487
+
488
+ const emulationResult: Omit<QueryOutput, "content" | "finishReason" | "runContext"> = {
352
489
  text: result.text,
353
490
  tokenName: result.tokenName,
354
491
  model: result.model,
@@ -379,8 +516,19 @@ function toEmulationResult(
379
516
  },
380
517
  serviceTier,
381
518
  ),
382
- costSource: hasProviderCost ? "provider" : "pricing_table",
519
+ costSource: hasProviderCost ? (result.costSource ?? "provider") : "pricing_table",
383
520
  };
521
+
522
+ if (result.providerMetadata?.xai) {
523
+ emulationResult.providerMetadata = { xai: result.providerMetadata.xai };
524
+ }
525
+
526
+ return emulationResult;
384
527
  }
385
528
 
386
529
  export const QgridDispatcher = new QgridDispatcherClass();
530
+
531
+ interface ResolvedDispatcher {
532
+ provider: ProviderId;
533
+ dispatcher: StreamingProviderDispatcher;
534
+ }