@kitn.ai/ui 0.28.0 → 0.29.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 (376) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-tooling/construct/public.d.ts +15 -0
  3. package/dist/agent-tooling/construct/schema.d.ts +111 -0
  4. package/dist/construct-cli.es.js +3 -3
  5. package/dist/construct.d.ts +5 -0
  6. package/dist/construct.js +327 -0
  7. package/dist/core-C8fzo39E.js +1 -12
  8. package/dist/engine-javascript-C1x7zo1_.js +1 -141
  9. package/dist/mcp.es.js +1 -1
  10. package/package.json +16 -16
  11. package/bin/route.test.js +0 -58
  12. package/src/agent-tooling/README.md +0 -192
  13. package/src/agent-tooling/archetypes.ts +0 -205
  14. package/src/agent-tooling/catalog/README.md +0 -676
  15. package/src/agent-tooling/catalog/catalog-types.ts +0 -231
  16. package/src/agent-tooling/catalog/fabrications.ts +0 -96
  17. package/src/agent-tooling/catalog/invariants.ts +0 -284
  18. package/src/agent-tooling/catalog/labs-titles.ts +0 -114
  19. package/src/agent-tooling/catalog/scenarios.ts +0 -87
  20. package/src/agent-tooling/catalog/surfaces.ts +0 -347
  21. package/src/agent-tooling/construct/cli-entry.ts +0 -8
  22. package/src/agent-tooling/construct/cli.ts +0 -147
  23. package/src/agent-tooling/construct/codegen.ts +0 -1635
  24. package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
  25. package/src/agent-tooling/construct/dev.ts +0 -148
  26. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
  27. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
  28. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
  29. package/src/agent-tooling/construct/schema.ts +0 -452
  30. package/src/agent-tooling/integrations/anthropic.ts +0 -442
  31. package/src/agent-tooling/integrations/cloudflare.ts +0 -218
  32. package/src/agent-tooling/integrations/langgraph.ts +0 -133
  33. package/src/agent-tooling/integrations/mastra.ts +0 -182
  34. package/src/agent-tooling/integrations/mock.ts +0 -148
  35. package/src/agent-tooling/integrations/ollama.ts +0 -97
  36. package/src/agent-tooling/integrations/openai.ts +0 -95
  37. package/src/agent-tooling/integrations/openrouter.ts +0 -82
  38. package/src/agent-tooling/integrations/pi.ts +0 -176
  39. package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
  40. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
  41. package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
  42. package/src/agent-tooling/mcp/manifest.ts +0 -422
  43. package/src/agent-tooling/mcp/server.ts +0 -116
  44. package/src/agent-tooling/mcp/stdio.ts +0 -18
  45. package/src/agent-tooling/mcp/tools/construct.ts +0 -130
  46. package/src/agent-tooling/mcp/tools/debug.ts +0 -621
  47. package/src/agent-tooling/mcp/tools/reference.ts +0 -980
  48. package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
  49. package/src/agent-tooling/mcp/tools/theme.ts +0 -456
  50. package/src/agent-tooling/mcp/tools/types.ts +0 -14
  51. package/src/agent-tooling/mcp/validate-args.ts +0 -141
  52. package/src/agent-tooling/recipes/composed-thread.ts +0 -714
  53. package/src/agent-tooling/recipes/index.ts +0 -22
  54. package/src/agent-tooling/recipes/types.ts +0 -32
  55. package/src/agent-tooling/registry.ts +0 -132
  56. package/src/agent-tooling/route-emit.ts +0 -305
  57. package/src/agent-tooling/types.ts +0 -390
  58. package/src/components/artifact-card.tsx +0 -120
  59. package/src/components/artifact.tsx +0 -914
  60. package/src/components/attachment-types.ts +0 -66
  61. package/src/components/attachments.tsx +0 -536
  62. package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
  63. package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
  64. package/src/components/audio-visualizer/fit-scale.ts +0 -112
  65. package/src/components/audio-visualizer/index.tsx +0 -528
  66. package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
  67. package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
  68. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
  69. package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
  70. package/src/components/audio-visualizer/sizes.ts +0 -54
  71. package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
  72. package/src/components/audio-visualizer/variant-bar.tsx +0 -175
  73. package/src/components/audio-visualizer/variant-custom.tsx +0 -344
  74. package/src/components/audio-visualizer/variant-grid.tsx +0 -175
  75. package/src/components/audio-visualizer/variant-radial.tsx +0 -163
  76. package/src/components/audio-visualizer/variant-wave.tsx +0 -130
  77. package/src/components/audio-visualizer/wave.glsl.ts +0 -130
  78. package/src/components/card-fallback.tsx +0 -60
  79. package/src/components/card-renderer.tsx +0 -220
  80. package/src/components/card.tsx +0 -212
  81. package/src/components/chain-of-thought.tsx +0 -348
  82. package/src/components/chat-container.tsx +0 -89
  83. package/src/components/chat-scope-picker.tsx +0 -57
  84. package/src/components/chat-thread.tsx +0 -1014
  85. package/src/components/checkpoint.tsx +0 -88
  86. package/src/components/choice-card.tsx +0 -743
  87. package/src/components/coachmark.tsx +0 -249
  88. package/src/components/code-block.tsx +0 -181
  89. package/src/components/composer-dom.ts +0 -159
  90. package/src/components/composer-highlight.ts +0 -242
  91. package/src/components/composer-history.ts +0 -82
  92. package/src/components/composer.tsx +0 -1075
  93. package/src/components/confirm-card.tsx +0 -402
  94. package/src/components/context.tsx +0 -377
  95. package/src/components/conversation-item.tsx +0 -241
  96. package/src/components/conversation-list.tsx +0 -429
  97. package/src/components/conversation-panel.tsx +0 -118
  98. package/src/components/dismissed-stub.tsx +0 -80
  99. package/src/components/embed.tsx +0 -200
  100. package/src/components/empty.tsx +0 -177
  101. package/src/components/feedback-bar.tsx +0 -166
  102. package/src/components/file-tree.tsx +0 -530
  103. package/src/components/file-upload.tsx +0 -167
  104. package/src/components/form-widgets.tsx +0 -525
  105. package/src/components/form.tsx +0 -1296
  106. package/src/components/home-panel.tsx +0 -159
  107. package/src/components/image.tsx +0 -65
  108. package/src/components/link-preview.tsx +0 -195
  109. package/src/components/loader.tsx +0 -367
  110. package/src/components/markdown.tsx +0 -162
  111. package/src/components/message-skills.tsx +0 -40
  112. package/src/components/message.tsx +0 -765
  113. package/src/components/model-switcher.tsx +0 -132
  114. package/src/components/prompt-input.tsx +0 -188
  115. package/src/components/prompt-suggestion.tsx +0 -150
  116. package/src/components/reasoning.tsx +0 -293
  117. package/src/components/response-compare-types.ts +0 -152
  118. package/src/components/response-compare.tsx +0 -455
  119. package/src/components/response-stream.tsx +0 -109
  120. package/src/components/screen.tsx +0 -181
  121. package/src/components/scroll-button.tsx +0 -93
  122. package/src/components/source.tsx +0 -192
  123. package/src/components/tasks-card.tsx +0 -801
  124. package/src/components/text-shimmer.tsx +0 -37
  125. package/src/components/thinking-bar.tsx +0 -50
  126. package/src/components/thread.tsx +0 -228
  127. package/src/components/toast.tsx +0 -503
  128. package/src/components/tool-classify.ts +0 -26
  129. package/src/components/tool-types.ts +0 -49
  130. package/src/components/tool.tsx +0 -190
  131. package/src/components/use-card-resolution.ts +0 -44
  132. package/src/components/voice-input.tsx +0 -222
  133. package/src/components/voice-output.tsx +0 -242
  134. package/src/components/widget-tab-bar.tsx +0 -73
  135. package/src/components/workspace-shell.tsx +0 -285
  136. package/src/diagnostics/hook.ts +0 -352
  137. package/src/diagnostics/index.ts +0 -82
  138. package/src/diagnostics/report-request.ts +0 -296
  139. package/src/elements/agent-card.tsx +0 -67
  140. package/src/elements/artifact.tsx +0 -194
  141. package/src/elements/attachments.tsx +0 -140
  142. package/src/elements/audio-visualizer.tsx +0 -227
  143. package/src/elements/autoloader.ts +0 -89
  144. package/src/elements/avatar.tsx +0 -33
  145. package/src/elements/badge.tsx +0 -32
  146. package/src/elements/button.tsx +0 -149
  147. package/src/elements/card.tsx +0 -133
  148. package/src/elements/cards.tsx +0 -266
  149. package/src/elements/chain-of-thought.tsx +0 -141
  150. package/src/elements/chat-scope-picker.tsx +0 -65
  151. package/src/elements/chat-types.ts +0 -102
  152. package/src/elements/chat-workspace.tsx +0 -188
  153. package/src/elements/chat.tsx +0 -304
  154. package/src/elements/checkbox-group.tsx +0 -164
  155. package/src/elements/checkbox.tsx +0 -134
  156. package/src/elements/checkpoint.tsx +0 -47
  157. package/src/elements/choice.tsx +0 -100
  158. package/src/elements/coachmark.tsx +0 -90
  159. package/src/elements/code-block.tsx +0 -60
  160. package/src/elements/command.tsx +0 -197
  161. package/src/elements/compare.tsx +0 -111
  162. package/src/elements/compiled.css +0 -2
  163. package/src/elements/composer.tsx +0 -162
  164. package/src/elements/confirm-card.tsx +0 -76
  165. package/src/elements/context-meter.tsx +0 -107
  166. package/src/elements/conversation-item.tsx +0 -120
  167. package/src/elements/conversation-list.tsx +0 -203
  168. package/src/elements/css.ts +0 -5
  169. package/src/elements/default-input.tsx +0 -392
  170. package/src/elements/define-entry.ts +0 -12
  171. package/src/elements/define.tsx +0 -563
  172. package/src/elements/diagnostic-events.ts +0 -114
  173. package/src/elements/dialog.tsx +0 -145
  174. package/src/elements/disclosure.ts +0 -95
  175. package/src/elements/dock.tsx +0 -215
  176. package/src/elements/dropdown.tsx +0 -123
  177. package/src/elements/editable-label.tsx +0 -140
  178. package/src/elements/element-data-types.ts +0 -152
  179. package/src/elements/element-diagnostics.ts +0 -392
  180. package/src/elements/element-manifest.json +0 -354
  181. package/src/elements/element-nonscalar.json +0 -187
  182. package/src/elements/element-types.d.ts +0 -4353
  183. package/src/elements/embed.tsx +0 -35
  184. package/src/elements/empty.tsx +0 -29
  185. package/src/elements/feedback-bar.tsx +0 -66
  186. package/src/elements/file-tree.tsx +0 -62
  187. package/src/elements/file-upload.tsx +0 -44
  188. package/src/elements/form.tsx +0 -94
  189. package/src/elements/hover-card.tsx +0 -80
  190. package/src/elements/icon.tsx +0 -41
  191. package/src/elements/image.tsx +0 -32
  192. package/src/elements/input.tsx +0 -492
  193. package/src/elements/kbd.tsx +0 -45
  194. package/src/elements/link-preview.tsx +0 -34
  195. package/src/elements/loader.tsx +0 -25
  196. package/src/elements/markdown.tsx +0 -38
  197. package/src/elements/menu.tsx +0 -241
  198. package/src/elements/message-skills.tsx +0 -83
  199. package/src/elements/message.tsx +0 -361
  200. package/src/elements/model-switcher.tsx +0 -121
  201. package/src/elements/nav.tsx +0 -87
  202. package/src/elements/notice.tsx +0 -53
  203. package/src/elements/pane-grid.tsx +0 -111
  204. package/src/elements/pane-group.tsx +0 -119
  205. package/src/elements/pane.tsx +0 -129
  206. package/src/elements/popover.tsx +0 -81
  207. package/src/elements/progress-bar.tsx +0 -39
  208. package/src/elements/prompt-dock.tsx +0 -82
  209. package/src/elements/prompt-input.tsx +0 -240
  210. package/src/elements/prompt-suggestions.tsx +0 -113
  211. package/src/elements/radio-group.tsx +0 -129
  212. package/src/elements/reasoning.tsx +0 -71
  213. package/src/elements/register-impl.ts +0 -148
  214. package/src/elements/register.ts +0 -76
  215. package/src/elements/remote.tsx +0 -225
  216. package/src/elements/resizable.globals.d.ts +0 -36
  217. package/src/elements/resizable.tsx +0 -704
  218. package/src/elements/response-stream.tsx +0 -40
  219. package/src/elements/screen.tsx +0 -90
  220. package/src/elements/scroll-area.tsx +0 -33
  221. package/src/elements/scroll-button.tsx +0 -179
  222. package/src/elements/search.tsx +0 -185
  223. package/src/elements/segmented.tsx +0 -129
  224. package/src/elements/select.tsx +0 -165
  225. package/src/elements/separator.tsx +0 -36
  226. package/src/elements/setting-item.tsx +0 -50
  227. package/src/elements/settings-group.tsx +0 -37
  228. package/src/elements/skeleton.tsx +0 -45
  229. package/src/elements/slider.tsx +0 -171
  230. package/src/elements/slot-text.ts +0 -73
  231. package/src/elements/slots.ts +0 -787
  232. package/src/elements/source.tsx +0 -126
  233. package/src/elements/status.tsx +0 -42
  234. package/src/elements/styles.css +0 -328
  235. package/src/elements/switch.tsx +0 -118
  236. package/src/elements/tabs.tsx +0 -95
  237. package/src/elements/tasks.tsx +0 -103
  238. package/src/elements/text-shimmer.tsx +0 -28
  239. package/src/elements/thinking-bar.tsx +0 -43
  240. package/src/elements/thread.tsx +0 -139
  241. package/src/elements/toast.tsx +0 -106
  242. package/src/elements/tool.tsx +0 -78
  243. package/src/elements/tooltip.tsx +0 -85
  244. package/src/elements/validate-messages.ts +0 -76
  245. package/src/elements/voice-input.tsx +0 -100
  246. package/src/elements/voice-output.tsx +0 -82
  247. package/src/index.ts +0 -430
  248. package/src/primitives/audio-bands.ts +0 -174
  249. package/src/primitives/card-component-types.ts +0 -63
  250. package/src/primitives/card-contract.ts +0 -80
  251. package/src/primitives/card-data-types.ts +0 -361
  252. package/src/primitives/card-host.tsx +0 -37
  253. package/src/primitives/card-recovery.ts +0 -160
  254. package/src/primitives/card-registry.tsx +0 -78
  255. package/src/primitives/card-resolution.ts +0 -36
  256. package/src/primitives/card-routing.ts +0 -85
  257. package/src/primitives/card-schemas/artifact.schema.json +0 -92
  258. package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
  259. package/src/primitives/card-schemas/card-event.schema.json +0 -12
  260. package/src/primitives/card-schemas/choice.schema.json +0 -75
  261. package/src/primitives/card-schemas/confirm.schema.json +0 -65
  262. package/src/primitives/card-schemas/embed.schema.json +0 -63
  263. package/src/primitives/card-schemas/form.result.schema.json +0 -7
  264. package/src/primitives/card-schemas/form.schema.json +0 -53
  265. package/src/primitives/card-schemas/link.schema.json +0 -54
  266. package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
  267. package/src/primitives/card-schemas/tasks.schema.json +0 -83
  268. package/src/primitives/card-tags.ts +0 -61
  269. package/src/primitives/card-validate-cards.ts +0 -255
  270. package/src/primitives/card-validate-schemas.ts +0 -58
  271. package/src/primitives/card-validate.ts +0 -131
  272. package/src/primitives/chat-config.tsx +0 -103
  273. package/src/primitives/composer-model.ts +0 -49
  274. package/src/primitives/composer-triggers.ts +0 -35
  275. package/src/primitives/controllable.ts +0 -21
  276. package/src/primitives/conversation-store.ts +0 -248
  277. package/src/primitives/create-kai-chat.ts +0 -66
  278. package/src/primitives/create-tween.ts +0 -266
  279. package/src/primitives/embed-providers.ts +0 -254
  280. package/src/primitives/field-mask.ts +0 -256
  281. package/src/primitives/field-semantics.ts +0 -115
  282. package/src/primitives/highlighter.ts +0 -157
  283. package/src/primitives/input-mask.ts +0 -853
  284. package/src/primitives/link-preview.ts +0 -87
  285. package/src/primitives/message-feedback.ts +0 -124
  286. package/src/primitives/pdf-preview.ts +0 -121
  287. package/src/primitives/toast-store.ts +0 -338
  288. package/src/primitives/url-scheme-policy.ts +0 -70
  289. package/src/primitives/use-audio-analysis.ts +0 -340
  290. package/src/primitives/use-auto-resize.ts +0 -31
  291. package/src/primitives/use-resize-observer.ts +0 -45
  292. package/src/primitives/use-sequencer.ts +0 -72
  293. package/src/primitives/use-speech-recognition.ts +0 -146
  294. package/src/primitives/use-stick-to-bottom.ts +0 -75
  295. package/src/primitives/use-text-stream.ts +0 -143
  296. package/src/primitives/use-voice-recorder.ts +0 -62
  297. package/src/primitives/visualizer-sequences.ts +0 -350
  298. package/src/remote/host-embed.ts +0 -345
  299. package/src/remote/index.ts +0 -2
  300. package/src/remote/origin.ts +0 -30
  301. package/src/remote/provider-runtime.ts +0 -262
  302. package/src/remote/provider.ts +0 -2
  303. package/src/remote/validate.ts +0 -22
  304. package/src/remote/version.ts +0 -12
  305. package/src/remote/wire.ts +0 -48
  306. package/src/schemas/from-tool-call.ts +0 -151
  307. package/src/schemas/index.ts +0 -341
  308. package/src/schemas/provider-subsets.ts +0 -538
  309. package/src/schemas/registry.ts +0 -355
  310. package/src/schemas/tool-defs.ts +0 -793
  311. package/src/solid.ts +0 -173
  312. package/src/state/index.ts +0 -44
  313. package/src/state/messages.ts +0 -53
  314. package/src/state/mock.ts +0 -297
  315. package/src/state/parts.ts +0 -295
  316. package/src/state/persistence.ts +0 -180
  317. package/src/state/stream.ts +0 -246
  318. package/src/state/suggestions.ts +0 -9
  319. package/src/state/threads.ts +0 -142
  320. package/src/test-utils/fake-clock.ts +0 -88
  321. package/src/types.ts +0 -95
  322. package/src/ui/action-icons.ts +0 -53
  323. package/src/ui/agent-card.tsx +0 -218
  324. package/src/ui/avatar.tsx +0 -23
  325. package/src/ui/badge.tsx +0 -21
  326. package/src/ui/button.tsx +0 -49
  327. package/src/ui/card.tsx +0 -245
  328. package/src/ui/checkbox-group.tsx +0 -153
  329. package/src/ui/checkbox.tsx +0 -63
  330. package/src/ui/collapsible.tsx +0 -163
  331. package/src/ui/command.tsx +0 -103
  332. package/src/ui/dialog.tsx +0 -223
  333. package/src/ui/dock.tsx +0 -630
  334. package/src/ui/dropdown.tsx +0 -636
  335. package/src/ui/editable-label.tsx +0 -112
  336. package/src/ui/hover-card.tsx +0 -345
  337. package/src/ui/icon.tsx +0 -167
  338. package/src/ui/input.tsx +0 -391
  339. package/src/ui/kbd.tsx +0 -118
  340. package/src/ui/nav.tsx +0 -282
  341. package/src/ui/notice.tsx +0 -84
  342. package/src/ui/overlay.tsx +0 -277
  343. package/src/ui/pane-grid.tsx +0 -117
  344. package/src/ui/pane-group.tsx +0 -291
  345. package/src/ui/pane.tsx +0 -193
  346. package/src/ui/popover.tsx +0 -106
  347. package/src/ui/progress-bar.tsx +0 -83
  348. package/src/ui/prompt-dock.tsx +0 -145
  349. package/src/ui/radio.tsx +0 -150
  350. package/src/ui/resizable.tsx +0 -693
  351. package/src/ui/scroll-area.tsx +0 -26
  352. package/src/ui/segmented.tsx +0 -109
  353. package/src/ui/select.tsx +0 -168
  354. package/src/ui/separator.tsx +0 -10
  355. package/src/ui/settings-group.tsx +0 -67
  356. package/src/ui/skeleton.tsx +0 -73
  357. package/src/ui/slider.tsx +0 -178
  358. package/src/ui/status.tsx +0 -48
  359. package/src/ui/switch.tsx +0 -119
  360. package/src/ui/tabs.tsx +0 -168
  361. package/src/ui/textarea.tsx +0 -21
  362. package/src/ui/tooltip.tsx +0 -118
  363. package/src/utils/cn.ts +0 -30
  364. package/src/wire/chunk.ts +0 -248
  365. package/src/wire/consume.ts +0 -599
  366. package/src/wire/diagnostics.ts +0 -727
  367. package/src/wire/encode-probe.ts +0 -214
  368. package/src/wire/encode.ts +0 -947
  369. package/src/wire/files.ts +0 -342
  370. package/src/wire/formats/anthropic.ts +0 -274
  371. package/src/wire/formats/openai.ts +0 -228
  372. package/src/wire/index.ts +0 -95
  373. package/src/wire/media-types.ts +0 -344
  374. package/src/wire/read.ts +0 -396
  375. package/src/wire/sink-helpers.ts +0 -50
  376. package/src/wire/sse.ts +0 -153
@@ -1,82 +0,0 @@
1
- import type { Integration } from '../types';
2
-
3
- const openrouter: Integration = {
4
- id: 'openrouter',
5
- title: 'OpenRouter',
6
- category: 'gateway',
7
- language: 'ts',
8
- streamFormat: 'openai-sse',
9
- envVars: ['OPENROUTER_API_KEY'],
10
- // No per-framework templates: the handler below is web-standard, so the
11
- // scaffolder wraps it in whatever the target framework routes with. This used
12
- // to be a `next`-only entry that every other framework inherited verbatim.
13
- routeTemplates: {},
14
- webRoute: `async function chatHandler(request: Request): Promise<Response> {
15
- // tools is undefined unless the front end sent one; JSON.stringify drops it,
16
- // so the same handler serves a tool archetype and a plain chat.
17
- let chatBody: ChatRequestBody;
18
- try {
19
- chatBody = await readChatRequest(request);
20
- } catch (error) {
21
- return toChatErrorResponse(error);
22
- }
23
- const { model, messages, tools } = chatBody;
24
-
25
- const upstream = await fetch('https://openrouter.ai/api/v1/chat/completions', {
26
- method: 'POST',
27
- headers: {
28
- Authorization: \`Bearer \${process.env.OPENROUTER_API_KEY}\`,
29
- 'Content-Type': 'application/json',
30
- },
31
- body: JSON.stringify({ model, messages, tools, stream: true }),
32
- });
33
-
34
- // FORWARD THE STATUS. Returning 200 here is how a missing key turns into
35
- // silence: the 401 body is JSON, it goes out labelled text/event-stream, the
36
- // SSE reader finds no frame, and the turn resolves empty with nothing logged
37
- // and no bubble. With the status intact readOpenAIStream throws a WireError
38
- // carrying the provider's own message.
39
- if (!upstream.ok) {
40
- return new Response(await upstream.text(), {
41
- status: upstream.status,
42
- headers: {
43
- 'Content-Type': upstream.headers.get('content-type') ?? 'application/json',
44
- },
45
- });
46
- }
47
- if (!upstream.body) {
48
- return new Response(JSON.stringify({ error: { message: 'The provider returned no body to stream.' } }), {
49
- status: 502,
50
- headers: { 'Content-Type': 'application/json' },
51
- });
52
- }
53
-
54
- return new Response(upstream.body, {
55
- status: 200,
56
- headers: {
57
- 'Content-Type': 'text/event-stream; charset=utf-8',
58
- // no-transform stops a proxy buffering the stream into one blob.
59
- 'Cache-Control': 'no-cache, no-transform',
60
- Connection: 'keep-alive',
61
- },
62
- });
63
- }`,
64
- streamMapping:
65
- 'OpenRouter returns OpenAI-format SSE. Pipe upstream.body straight to the browser; readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls and reasoning.',
66
- runNote: 'Set OPENROUTER_API_KEY. Model ids are vendor/model, e.g. openai/gpt-4o.',
67
- docsSlug: 'integrations/connect-any-model',
68
- // The route forwards both, so a scaffold's editable consts reach the gateway.
69
- forwardsFromClient: ['model', 'tools'],
70
- // The handler puts the client's `tools` straight into an OpenAI-compatible
71
- // /chat/completions body, unconverted — so the client must send OpenAI's own
72
- // `{ type: 'function', function: { parameters } }` envelope.
73
- clientToolFormat: 'openai',
74
- // Nothing to install. The route is global `fetch` and imports no module.
75
- deps: { npm: [], pip: [] },
76
- // The route reads OPENROUTER_API_KEY into an `Authorization: Bearer` header.
77
- keyExposure: 'needs-proxy',
78
- // A remote HTTPS endpoint and a key. Nothing to install, nothing to start.
79
- outOfBand: 'none',
80
- };
81
-
82
- export default openrouter;
@@ -1,176 +0,0 @@
1
- import type { Integration } from '../types';
2
-
3
- const pi: Integration = {
4
- id: 'pi',
5
- title: 'Pi',
6
- category: 'harness',
7
- language: 'ts',
8
- streamFormat: 'native',
9
- envVars: [],
10
- routeTemplates: {
11
- // Express only, and genuinely so: the bridge spawns a local process, which a
12
- // Worker or an edge runtime cannot do. Every other framework gets this under
13
- // the scaffolder's "cannot host" warning, the same way pydantic-ai emits a
14
- // whole FastAPI service.
15
- express: `// server.ts
16
- import express from 'express';
17
- import { spawn } from 'node:child_process';
18
- import type { OpenAIWireMessage } from '@kitn.ai/ui/wire';
19
-
20
- const app = express();
21
- app.use(express.json());
22
-
23
- class ChatRequestError extends Error {
24
- constructor(readonly status: number, message: string) { super(message); }
25
- }
26
-
27
- /** Narrow the JSON body once, at the edge. A bare GET or a missing messages
28
- * array is a ChatRequestError with a status — NEVER an unhandled throw
29
- * (findings F-10). Malformed JSON is rejected upstream by express.json(),
30
- * which the error handler below turns into a JSON 400. */
31
- function readChatRequest(req: express.Request): { messages: OpenAIWireMessage[] } {
32
- if (req.method !== 'POST') {
33
- throw new ChatRequestError(405, \`Method \${req.method} not allowed — POST /api/chat.\`);
34
- }
35
- const body = (req.body ?? {}) as { messages?: OpenAIWireMessage[] };
36
- if (!Array.isArray(body.messages)) {
37
- throw new ChatRequestError(400, 'Request body must carry a messages array.');
38
- }
39
- return body as { messages: OpenAIWireMessage[] };
40
- }
41
-
42
- /** Map a guard rejection to the response its status demands; rethrow anything
43
- * else — an unexpected error should be loud, not laundered into a 400. */
44
- function toChatErrorResponse(error: unknown, res: express.Response): void {
45
- if (error instanceof ChatRequestError) {
46
- res.status(error.status).json({ error: { message: error.message } });
47
- return;
48
- }
49
- throw error;
50
- }
51
-
52
- // express.json() rejects a malformed body at the MIDDLEWARE level, before any
53
- // handler runs — without this it surfaces as Express's default HTML error page.
54
- app.use(
55
- (
56
- err: { status?: number; message?: string },
57
- _req: express.Request,
58
- res: express.Response,
59
- _next: express.NextFunction,
60
- ) => {
61
- res.status(err?.status ?? 500).json({ error: { message: err?.message ?? 'Request failed.' } });
62
- },
63
- );
64
-
65
- // A bare GET is a 405 with the method named, not Express's default 404 —
66
- // the resource exists, the verb is wrong.
67
- app.get('/api/chat', (_req, res) => {
68
- res.status(405).json({ error: { message: 'Method not allowed — POST /api/chat.' } });
69
- });
70
-
71
- // POST /api/chat: bridge a Pi RPC session to the browser as SSE.
72
- app.post('/api/chat', (req, res) => {
73
- let chatBody: { messages: OpenAIWireMessage[] };
74
- try {
75
- chatBody = readChatRequest(req);
76
- } catch (error) {
77
- toChatErrorResponse(error, res);
78
- return;
79
- }
80
- const { messages } = chatBody;
81
- const last = messages.at(-1)?.content;
82
- // \`content\` is a plain string until the turn carries an attachment, at which
83
- // point it is an ARRAY of content parts. Pi's RPC mode takes a TEXT prompt and
84
- // has no channel for a file, so an attachment is REFUSED here. Passing the
85
- // array straight through would JSON.stringify an object graph into the prompt
86
- // — no type error, no crash, just a model reading serialised noise.
87
- if (Array.isArray(last) && last.some((part) => part.type !== 'text')) {
88
- res.status(400).json({
89
- error: {
90
- message:
91
- 'This Pi bridge forwards a text prompt only and has no channel for an attachment. Extract the file content into the message text, or send it through a tool.',
92
- },
93
- });
94
- return;
95
- }
96
- const prompt =
97
- typeof last === 'string'
98
- ? last
99
- : (last ?? []).map((part) => (part.type === 'text' ? part.text : '')).join('');
100
-
101
- // Headers before the first frame: without text/event-stream the browser
102
- // buffers the body and readOpenAIStream never sees a frame.
103
- res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
104
- // no-transform stops a proxy buffering the stream into one blob.
105
- res.setHeader('Cache-Control', 'no-cache, no-transform');
106
- res.setHeader('Connection', 'keep-alive');
107
-
108
- // ONE PROCESS PER REQUEST. Pi in RPC mode holds conversation state, so a
109
- // module-level process would leak one caller's turns into the next caller's
110
- // reply and serialise every request behind a single stdin.
111
- const pi = spawn('pi', ['--mode', 'rpc', '--no-session']);
112
-
113
- // A spawn failure (pi not on PATH) arrives asynchronously, by which point the
114
- // SSE headers are out and the status is spent, so report IN BAND:
115
- // readOpenAIStream lands this on turn.error instead of leaving a blank bubble.
116
- pi.on('error', (err) => {
117
- res.write(\`data: \${JSON.stringify({ error: { message: err.message } })}\\n\\n\`);
118
- res.write('data: [DONE]\\n\\n');
119
- res.end();
120
- });
121
-
122
- // Send the user's turn. Pi commands are { type, message }.
123
- pi.stdin.write(JSON.stringify({ type: 'prompt', message: prompt }) + '\\n');
124
-
125
- let buffer = '';
126
- pi.stdout.on('data', (chunk: Buffer) => {
127
- buffer += chunk.toString();
128
- // Split on \\n rather than readline: readline also breaks on the Unicode
129
- // separators U+2028/U+2029, which appear inside model output.
130
- const lines = buffer.split('\\n');
131
- // pop() is \`string | undefined\`, and the buffer has to stay a string.
132
- buffer = lines.pop() ?? ''; // hold the partial line for the next chunk
133
- for (const line of lines) {
134
- if (!line) continue;
135
- const event = JSON.parse(line);
136
- const part = event.assistantMessageEvent;
137
- if (event.type === 'message_update' && part?.type === 'text_delta') {
138
- res.write(\`data: \${JSON.stringify({ choices: [{ delta: { content: part.delta } }] })}\\n\\n\`);
139
- }
140
- }
141
- });
142
- pi.on('close', () => { res.write('data: [DONE]\\n\\n'); res.end(); });
143
- });
144
-
145
- app.listen(3001, () => console.log('chat api: http://localhost:3001/api/chat'));
146
- // The front end fetches a RELATIVE /api/chat, so proxy that path to this port
147
- // from your dev server, or serve both from one origin.`,
148
- },
149
- streamMapping: "Pi runs as a local stdio process in RPC mode (pi --mode rpc --no-session). It emits newline-delimited JSON events on stdout. Map message_update events where assistantMessageEvent.type === 'text_delta' to data: {choices:[{delta:{content:part.delta}}]} SSE frames; send data: [DONE] on close. Split stdout on \\n (not readline, which breaks on Unicode separators U+2028/U+2029). Pi also emits thinking_delta and toolcall_* events; re-frame them onto delta.reasoning and delta.tool_calls in the same frames. readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls and reasoning.",
150
- runNote: "Pi must be installed locally and available on PATH as 'pi'. No API key is required by the bridge itself; Pi uses its own credentials. Pi runs with full user permissions, so sandbox it before exposing to a public endpoint. See the RPC reference: https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/rpc.md",
151
- docsSlug: 'integrations/harnesses',
152
- // Nothing. Pi runs with its own credentials, model and tools; the bridge
153
- // forwards a prompt, not a model id.
154
- forwardsFromClient: [],
155
- // `express` only. The bridge's other two imports are excluded by the rule:
156
- // `node:child_process` is a builtin and '@kitn.ai/ui/wire' is the kit. Pi
157
- // itself is NOT an npm dep of the app — runNote requires it on PATH, and the
158
- // route reaches it with spawn('pi'), not an import.
159
- deps: { npm: ['express'], pip: [] },
160
- // No key anywhere: envVars is empty and Pi uses its own credentials on disk.
161
- // This is 'needs-proxy' for the OTHER reason the flag covers — a server-only
162
- // capability. The bridge calls spawn(), which no browser can do, and runNote
163
- // warns that Pi runs with full user permissions and wants sandboxing before it
164
- // is exposed. "No key" is not the same fact as "safe in the browser", and
165
- // collapsing the two is exactly how this flag would be got wrong.
166
- keyExposure: 'needs-proxy',
167
- // Not 'local-server': nothing is listening in advance. The bridge spawns
168
- // `pi --mode rpc` per request, so what the developer supplies is an EXECUTABLE
169
- // on PATH ("Pi must be installed locally and available on PATH as 'pi'"), and
170
- // "start the server first" would be the wrong instruction to print. Pi is not
171
- // in `deps` for the same reason — the route reaches it with spawn(), not an
172
- // import. The schema's SPAWNS_PROCESS net catches this one independently.
173
- outOfBand: 'local-binary',
174
- };
175
-
176
- export default pi;
@@ -1,108 +0,0 @@
1
- import type { Integration } from '../types';
2
-
3
- const pydanticAi: Integration = {
4
- id: 'pydantic-ai',
5
- title: 'Pydantic AI',
6
- category: 'framework',
7
- language: 'python',
8
- streamFormat: 'openai-sse',
9
- envVars: ['OPENAI_API_KEY'],
10
- routeTemplates: {
11
- fastapi: `# main.py
12
- import json
13
- from fastapi import FastAPI, HTTPException
14
- from fastapi.middleware.cors import CORSMiddleware
15
- from fastapi.responses import StreamingResponse
16
- from pydantic import BaseModel
17
- from pydantic_ai import Agent
18
-
19
- agent = Agent('openai:gpt-4o')
20
-
21
- app = FastAPI()
22
- app.add_middleware(
23
- CORSMiddleware, allow_origins=['*'], allow_methods=['*'], allow_headers=['*']
24
- )
25
-
26
- class ContentPart(BaseModel):
27
- type: str
28
- text: str | None = None
29
-
30
- class Message(BaseModel):
31
- role: str
32
- # A plain string until the turn carries an attachment, at which point the
33
- # wire sends an ARRAY of content parts. Declaring this \`str\` alone made a
34
- # message with a file a 422 that named pydantic rather than the cause.
35
- content: str | list[ContentPart] | None = None
36
-
37
- class ChatRequest(BaseModel):
38
- messages: list[Message]
39
-
40
- def prompt_text(message: Message) -> str:
41
- """The text of a turn, refusing what this route cannot carry.
42
-
43
- agent.run_stream() takes a TEXT prompt, so an image or a document has no
44
- channel here. Dropping it would send the model a turn that silently lost the
45
- user's file; a 400 that names the reason is the honest failure.
46
- """
47
- if message.content is None:
48
- return ''
49
- if isinstance(message.content, str):
50
- return message.content
51
- if any(part.type != 'text' for part in message.content):
52
- raise HTTPException(
53
- status_code=400,
54
- detail='This route forwards a text prompt only and has no channel for an attachment. '
55
- 'Extract the file content into the message text, or send it through a tool.',
56
- )
57
- return ''.join(part.text or '' for part in message.content)
58
-
59
- async def openai_sse(messages: list[Message]):
60
- prompt = prompt_text(messages[-1]) if messages else ''
61
- async with agent.run_stream(prompt) as result:
62
- async for delta in result.stream_text(delta=True):
63
- chunk = {'choices': [{'delta': {'content': delta}}]}
64
- yield f'data: {json.dumps(chunk)}\\n\\n'
65
- yield 'data: [DONE]\\n\\n'
66
-
67
- @app.post('/api/chat')
68
- async def chat(req: ChatRequest):
69
- return StreamingResponse(openai_sse(req.messages), media_type='text/event-stream')`,
70
- },
71
- streamMapping:
72
- "Pydantic AI's agent.run_stream() yields text deltas via result.stream_text(delta=True). Each delta is re-framed as a data: {choices:[{delta:{content}}]} SSE line and the stream closes with data: [DONE]. readOpenAIStream from @kitn.ai/ui/wire parses tool calls and reasoning too, but stream_text() yields text only: iterate the run's events instead and re-frame its tool-call events onto delta.tool_calls to fill kai-tool.",
73
- // No install list here: `deps` below is the one, and the scaffolder emits it.
74
- // This sentence used to open "Install: pip install pydantic-ai fastapi
75
- // uvicorn." — three of the four packages, missing the `pydantic` its own route
76
- // imports on the next line.
77
- runNote:
78
- 'Set OPENAI_API_KEY. Run: uvicorn main:app --reload (default port 8000). Point kai-chat at http://localhost:8000/api/chat.',
79
- docsSlug: 'integrations/pydantic-ai',
80
- // Nothing. Agent('openai:gpt-4o') pins the model and the agent registers its
81
- // own tools, both server-side.
82
- forwardsFromClient: [],
83
- // The only python integration, so the only non-empty `pip`. Three of the four
84
- // are the route's own imports (`pydantic_ai` is imported under its module
85
- // name and installed under its hyphenated one); `uvicorn` is the ASGI server
86
- // the run note starts, which nothing imports and without which the app cannot
87
- // run. That asymmetry is why the pip guard checks imports ⊆ declared and not
88
- // the reverse.
89
- deps: { npm: [], pip: ['pydantic-ai', 'fastapi', 'pydantic', 'uvicorn'] },
90
- // Agent('openai:gpt-4o') reads OPENAI_API_KEY inside the python process.
91
- //
92
- // Worth being precise about, because this one looks like an exception: the
93
- // FastAPI app sets CORS to allow_origins=['*'] and the browser fetches it
94
- // DIRECTLY on :8000, with no JS proxy in front. That still is not
95
- // 'frontend-safe' — the flag asks where the SECRET lives, and it lives in the
96
- // python process. The FastAPI service IS the server hop.
97
- keyExposure: 'needs-proxy',
98
- // The emitted backend is a FastAPI service: a python interpreter, the four
99
- // `deps.pip` packages, and `uvicorn main:app` on its own port — none of which a
100
- // node toolchain provides or starts. It also needs OPENAI_API_KEY, so this is
101
- // the entry that proves the groups are not mutually exclusive: a runtime
102
- // prerequisite and a key at once. The prompt should lead with the runtime,
103
- // because a key is useless until the service runs. The schema's `language ===
104
- // 'python'` net catches this one independently.
105
- outOfBand: 'language-runtime',
106
- };
107
-
108
- export default pydanticAi;