@jini-ai/ui 0.5.5 → 0.6.10

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 (263) hide show
  1. package/CHANGELOG.md +91 -2
  2. package/dist/core/theme/validation.js +40 -38
  3. package/dist/features/a2ui/renderer.js +15 -13
  4. package/dist/features/about/react/components/AboutTab.js +64 -34
  5. package/dist/features/about/rules.d.ts +2 -2
  6. package/dist/features/about/rules.js +156 -129
  7. package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
  8. package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
  9. package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
  10. package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
  11. package/dist/features/admin-widgets/components/InfoTip.js +34 -7
  12. package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
  13. package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
  14. package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
  15. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
  16. package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
  17. package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
  18. package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
  19. package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
  20. package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
  21. package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
  22. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
  23. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
  24. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
  25. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
  26. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
  27. package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
  28. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
  29. package/dist/features/admin-widgets/index.d.ts +14 -2
  30. package/dist/features/admin-widgets/index.js +7 -1
  31. package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
  32. package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
  33. package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
  34. package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
  35. package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
  36. package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
  37. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
  38. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
  39. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
  40. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
  41. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
  42. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
  43. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
  44. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
  45. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
  46. package/dist/features/asset-tree-browser/rules.js +40 -22
  47. package/dist/features/browser-chrome/rules.js +74 -70
  48. package/dist/features/charts/browser.js +11 -5
  49. package/dist/features/charts/react.js +2 -2
  50. package/dist/features/charts/table.js +2 -2
  51. package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
  52. package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
  53. package/dist/features/connectors/ConnectorsBrowser.js +26 -14
  54. package/dist/features/connectors/components/ConnectorCard.js +67 -38
  55. package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
  56. package/dist/features/connectors/components/ConnectorGrid.js +11 -6
  57. package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
  58. package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
  59. package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
  60. package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
  61. package/dist/features/connectors/rules.d.ts +8 -1
  62. package/dist/features/connectors/rules.js +42 -20
  63. package/dist/features/connectors/types.d.ts +11 -0
  64. package/dist/features/execution/constants.d.ts +4 -10
  65. package/dist/features/execution/constants.js +9 -94
  66. package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
  67. package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
  68. package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
  69. package/dist/features/execution/react/components/ExecutionTab.js +52 -30
  70. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
  71. package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
  72. package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
  73. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
  74. package/dist/features/execution/react/hooks/useReasoningControl.js +114 -78
  75. package/dist/features/execution/rules.d.ts +2 -2
  76. package/dist/features/execution/rules.js +31 -15
  77. package/dist/features/execution/types.d.ts +4 -42
  78. package/dist/features/execution/types.js +0 -17
  79. package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
  80. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
  81. package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
  82. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
  83. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
  84. package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
  85. package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
  86. package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
  87. package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
  88. package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
  89. package/dist/features/html-editor/css.js +76 -69
  90. package/dist/features/html-editor/editor-blocks.js +47 -33
  91. package/dist/features/html-editor/editor-serialization.js +6 -5
  92. package/dist/features/html-editor/protected-elements.js +39 -28
  93. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
  94. package/dist/features/html-editor/source-splice.js +51 -30
  95. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
  96. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
  97. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
  98. package/dist/features/i18n/locale.js +20 -16
  99. package/dist/features/iframe-pool/react/dom-sync.js +8 -6
  100. package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
  101. package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
  102. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  103. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
  104. package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
  105. package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
  106. package/dist/features/lexical-rich-text-editor/rules.js +87 -74
  107. package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
  108. package/dist/features/mcp-ui/secret-card.js +111 -61
  109. package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
  110. package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
  111. package/dist/features/mcp-ui/surfaces/document.js +10 -2
  112. package/dist/features/mcp-ui/surfaces/fields.js +30 -50
  113. package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
  114. package/dist/features/mcp-ui/surfaces/form.js +43 -21
  115. package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
  116. package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
  117. package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
  118. package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
  119. package/dist/features/media-providers/constants.d.ts +1 -0
  120. package/dist/features/media-providers/constants.js +2 -81
  121. package/dist/features/media-providers/index.d.ts +1 -1
  122. package/dist/features/media-providers/ports.d.ts +2 -0
  123. package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
  124. package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
  125. package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
  126. package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
  127. package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
  128. package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
  129. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
  130. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
  131. package/dist/features/media-providers/rules.js +17 -10
  132. package/dist/features/media-providers/types.d.ts +4 -22
  133. package/dist/features/media-providers/types.js +3 -8
  134. package/dist/features/memory/formatters.js +127 -92
  135. package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
  136. package/dist/features/memory/react/components/MemoryList.js +37 -18
  137. package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
  138. package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
  139. package/dist/features/observability/long-task.d.ts +0 -12
  140. package/dist/features/observability/long-task.js +15 -19
  141. package/dist/features/observability/resource-error.d.ts +0 -10
  142. package/dist/features/observability/resource-error.js +18 -13
  143. package/dist/features/observability/stuck-run.js +10 -6
  144. package/dist/features/observability/telemetry-url.d.ts +6 -0
  145. package/dist/features/observability/telemetry-url.js +16 -0
  146. package/dist/features/observability/white-screen.js +12 -24
  147. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  148. package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
  149. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
  150. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
  151. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
  152. package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
  153. package/dist/features/progress-card/components/ProgressCard.js +7 -3
  154. package/dist/features/progress-card/reference-adapters.js +59 -54
  155. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
  156. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
  157. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
  158. package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
  159. package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
  160. package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
  161. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
  162. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
  163. package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
  164. package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
  165. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
  166. package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
  167. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
  168. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
  169. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
  170. package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
  171. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
  172. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
  173. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
  174. package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
  175. package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
  176. package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
  177. package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
  178. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
  179. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
  180. package/dist/features/resource-dashboard/rules.js +2 -8
  181. package/dist/features/sketch-editor/dom.js +150 -99
  182. package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
  183. package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
  184. package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
  185. package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
  186. package/dist/features/skills/react/components/SkillRow.js +38 -18
  187. package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
  188. package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
  189. package/dist/features/skills/react/components/SkillsTab.js +24 -14
  190. package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
  191. package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
  192. package/dist/features/skills/rules.js +32 -23
  193. package/dist/features/source-config-list/agent-handles.js +2 -1
  194. package/dist/features/source-config-list/dependencies.js +53 -49
  195. package/dist/features/source-config-list/index.d.ts +3 -3
  196. package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
  197. package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
  198. package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
  199. package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
  200. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
  201. package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
  202. package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
  203. package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
  204. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
  205. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
  206. package/dist/features/source-config-list/types.d.ts +14 -0
  207. package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
  208. package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
  209. package/dist/features/tab-strip/react/components/TabBar.js +8 -1
  210. package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
  211. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
  212. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
  213. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
  214. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
  215. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
  216. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
  217. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
  218. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
  219. package/dist/features/version-manager/dependencies.d.ts +3 -1
  220. package/dist/features/version-manager/dependencies.js +3 -2
  221. package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
  222. package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
  223. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
  224. package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
  225. package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
  226. package/dist/features/viewer-shell/rules.js +19 -23
  227. package/dist/react/components/CustomSelect.d.ts +38 -1
  228. package/dist/react/components/CustomSelect.js +94 -38
  229. package/dist/react/components/OnboardingDropdown.js +20 -8
  230. package/dist/react/components/RemixIcon.stylesheet.js +3 -1
  231. package/dist/react/components/Toast.js +13 -2
  232. package/dist/react/components/TooltipLayer.js +5 -14
  233. package/dist/react/components/WorkingDirPicker.js +78 -50
  234. package/dist/react/components/remixicon-font-recovery.js +38 -30
  235. package/dist/react/components/select-popover.d.ts +8 -6
  236. package/dist/react/components/select-popover.js +33 -29
  237. package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
  238. package/dist/react/mcp-ui/McpUiHost.js +10 -11
  239. package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
  240. package/dist/react/mcp-ui/host-message-source.js +15 -5
  241. package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
  242. package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
  243. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
  244. package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
  245. package/dist/renderers/react/components/ArtifactView.js +9 -6
  246. package/dist/renderers/srcdoc/build.d.ts +4 -1
  247. package/dist/renderers/srcdoc/build.js +62 -48
  248. package/dist/styles/admin.css +29 -14
  249. package/dist/styles/lightbox.css +142 -0
  250. package/dist/styles/variables.css +128 -0
  251. package/dist/theme/apply.js +9 -5
  252. package/dist/utils/auto-open-file.d.ts +2 -1
  253. package/dist/utils/auto-open-file.js +69 -85
  254. package/dist/utils/markdown-scroll-sync.js +108 -87
  255. package/dist/utils/polygon-selection.js +25 -26
  256. package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
  257. package/dist/utils/smooth-scroll-to-top.js +2 -32
  258. package/dist/utils/sse.js +26 -26
  259. package/dist/utils/unit-bezier.d.ts +7 -0
  260. package/dist/utils/unit-bezier.js +38 -0
  261. package/package.json +9 -12
  262. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
  263. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
@@ -78,4 +78,15 @@ export interface ConnectorAuthResultEvent {
78
78
  result: 'success' | 'failed' | 'cancelled';
79
79
  errorCode?: string;
80
80
  }
81
+ /** Derived connect/disconnect state shared by the connector card and detail drawer. */
82
+ export interface ConnectorActionFlags {
83
+ isConnecting: boolean;
84
+ isDisconnecting: boolean;
85
+ isConnected: boolean;
86
+ isAuthorizationPending: boolean;
87
+ canConnect: boolean;
88
+ canDisconnect: boolean;
89
+ /** The pending authorization's redirect URL — only set while authorization is pending. */
90
+ redirectUrl: string | undefined;
91
+ }
81
92
  //# sourceMappingURL=types.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import type { AgentCliEnvFieldSpec, ApiProtocol, ProviderPreset } from './types.js';
2
- /** Fallback endpoint per protocol, used when a host supplies no preset and
3
- * the operator has not typed a base URL. Origin: `DEFAULT_BASE_URL_BY_PROTOCOL`. */
2
+ /** @deprecated Endpoints come from host presets. Blank means the operator must supply an endpoint. */
4
3
  export declare const DEFAULT_BASE_URL_BY_PROTOCOL: Readonly<Record<ApiProtocol, string>>;
5
4
  /** Display order and English labels for the protocol row. Values are i18n
6
5
  * keys (this package's convention is "the English string is the key"). */
@@ -18,8 +17,8 @@ export declare const CUSTOM_MODEL_SENTINEL = "__custom__";
18
17
  /**
19
18
  * The shortest string `apiKeyFormatWarning` will let pass without remark.
20
19
  *
21
- * Chosen from evidence rather than taste. The shortest real key shape any preset in
22
- * {@link DEFAULT_PROVIDER_PRESETS} describes is Azure OpenAI's bare 32-character hex string;
20
+ * Chosen from evidence rather than taste. The shortest real key shape in the integrations
21
+ * starter presets is Azure OpenAI's bare 32-character hex string;
23
22
  * Google's is `AIza` + 35 = 39, OpenAI's `sk-` + 48 = 51, OpenRouter's `sk-or-v1-` + 64 = 73, and
24
23
  * Anthropic's longer again. 20 therefore sits 12 characters below that floor, so no credential this
25
24
  * catalog knows about can trip it, while it still catches the failure the check exists for: the
@@ -45,12 +44,7 @@ export declare const API_KEY_CROSS_VENDOR_WARNING = "This looks like an API key
45
44
  /** Shown when a key is shorter than {@link MIN_PLAUSIBLE_API_KEY_LENGTH}. Carries no placeholders:
46
45
  * the length floor is global, so there is no vendor to name. */
47
46
  export declare const API_KEY_TOO_SHORT_WARNING = "This is shorter than any provider API key \u2014 check that the whole key was pasted. You can still save and test it.";
48
- /**
49
- * A small, vendor-shaped starting catalog. Deliberately NOT the origin's full
50
- * ~30-entry list: that list is hand-curated product content that goes stale,
51
- * and this package's rule for tables like this (see `LanguageTab`) is that the
52
- * host owns them. Pass your own `presets` to replace this wholesale.
53
- */
47
+ /** @deprecated Supply host presets. The former starter set is exported by integrations/media-providers/catalog. */
54
48
  export declare const DEFAULT_PROVIDER_PRESETS: readonly ProviderPreset[];
55
49
  /**
56
50
  * One-line descriptions for the code-agent CLIs this package knows about,
@@ -1,11 +1,9 @@
1
- import { VENDOR_API_BASE_URLS } from '@jini-ai/integrations/media-providers/catalog';
2
- /** Fallback endpoint per protocol, used when a host supplies no preset and
3
- * the operator has not typed a base URL. Origin: `DEFAULT_BASE_URL_BY_PROTOCOL`. */
1
+ /** @deprecated Endpoints come from host presets. Blank means the operator must supply an endpoint. */
4
2
  export const DEFAULT_BASE_URL_BY_PROTOCOL = {
5
- anthropic: VENDOR_API_BASE_URLS.anthropic,
6
- openai: VENDOR_API_BASE_URLS.openai,
7
- azure: VENDOR_API_BASE_URLS.azure,
8
- google: VENDOR_API_BASE_URLS.google,
3
+ anthropic: '',
4
+ openai: '',
5
+ azure: '',
6
+ google: '',
9
7
  };
10
8
  /** Display order and English labels for the protocol row. Values are i18n
11
9
  * keys (this package's convention is "the English string is the key"). */
@@ -25,8 +23,8 @@ export const CUSTOM_MODEL_SENTINEL = '__custom__';
25
23
  /**
26
24
  * The shortest string `apiKeyFormatWarning` will let pass without remark.
27
25
  *
28
- * Chosen from evidence rather than taste. The shortest real key shape any preset in
29
- * {@link DEFAULT_PROVIDER_PRESETS} describes is Azure OpenAI's bare 32-character hex string;
26
+ * Chosen from evidence rather than taste. The shortest real key shape in the integrations
27
+ * starter presets is Azure OpenAI's bare 32-character hex string;
30
28
  * Google's is `AIza` + 35 = 39, OpenAI's `sk-` + 48 = 51, OpenRouter's `sk-or-v1-` + 64 = 73, and
31
29
  * Anthropic's longer again. 20 therefore sits 12 characters below that floor, so no credential this
32
30
  * catalog knows about can trip it, while it still catches the failure the check exists for: the
@@ -52,91 +50,8 @@ export const API_KEY_CROSS_VENDOR_WARNING = 'This looks like an API key for {ven
52
50
  /** Shown when a key is shorter than {@link MIN_PLAUSIBLE_API_KEY_LENGTH}. Carries no placeholders:
53
51
  * the length floor is global, so there is no vendor to name. */
54
52
  export const API_KEY_TOO_SHORT_WARNING = 'This is shorter than any provider API key — check that the whole key was pasted. You can still save and test it.';
55
- /**
56
- * A small, vendor-shaped starting catalog. Deliberately NOT the origin's full
57
- * ~30-entry list: that list is hand-curated product content that goes stale,
58
- * and this package's rule for tables like this (see `LanguageTab`) is that the
59
- * host owns them. Pass your own `presets` to replace this wholesale.
60
- */
61
- export const DEFAULT_PROVIDER_PRESETS = [
62
- {
63
- id: 'anthropic',
64
- title: 'Anthropic',
65
- protocol: 'anthropic',
66
- baseUrl: VENDOR_API_BASE_URLS.anthropic,
67
- preferredModels: ['claude-sonnet-5', 'claude-opus-5', 'claude-haiku-4-5'],
68
- kind: 'protocol',
69
- apiKeyPrefix: 'sk-ant-',
70
- },
71
- {
72
- id: 'openai',
73
- title: 'OpenAI',
74
- protocol: 'openai',
75
- baseUrl: VENDOR_API_BASE_URLS.openai,
76
- // `gpt-4o`/`gpt-4o-mini`/`o3`/`o4-mini` are stale: OpenAI's GPT-5.6 family
77
- // (released 2026-07-09, per the API changelog) replaced the old
78
- // flagship/mini/nano naming with named tiers — sol/terra/luna. `sol` is
79
- // the one the changelog and pricing docs call out as "recommended for
80
- // production API usage", so it leads (see `preferredModels[0]` default
81
- // in `rules.ts`) rather than the newer, pricier `gpt-6-astra` flagship
82
- // (released 2026-09-03) — picking that as the silent default would jump
83
- // an existing user's cost without them choosing it.
84
- preferredModels: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'],
85
- kind: 'protocol',
86
- apiKeyPrefix: 'sk-',
87
- },
88
- {
89
- id: 'azure-openai',
90
- title: 'Azure OpenAI',
91
- protocol: 'azure',
92
- baseUrl: VENDOR_API_BASE_URLS.azure,
93
- preferredModels: [],
94
- kind: 'protocol',
95
- // No `apiKeyPrefix` deliberately: an Azure OpenAI key is a bare hex string with no vendor
96
- // prefix, so any value here would be a guess — and a wrong prefix claim does not merely stay
97
- // silent, it lets some OTHER vendor's valid key be announced as Azure's. Its 32 hex characters
98
- // are also the shortest real key shape in this catalog, which is what sets
99
- // `MIN_PLAUSIBLE_API_KEY_LENGTH`.
100
- },
101
- {
102
- id: 'google-gemini',
103
- title: 'Google Gemini',
104
- protocol: 'google',
105
- baseUrl: VENDOR_API_BASE_URLS.google,
106
- // Ordered newest-first: `rules.ts`'s `saved?.model ?? preset.preferredModels[0]` makes entry [0]
107
- // the default for anyone who has never picked a model, so a stale head silently pins new users
108
- // to an old generation. Reported live: the field defaulted to `gemini-2.5-flash` while the
109
- // operator was actually running `gemini-3.6-flash`. This list is only a FALLBACK — the form
110
- // prefers live `listModels` discovery and drops back here when that call fails.
111
- preferredModels: ['gemini-3.6-flash', 'gemini-2.5-flash', 'gemini-2.5-pro'],
112
- kind: 'protocol',
113
- // `AIza` + 35 characters is the classic Google key shape, and it is recorded here so a key of
114
- // this shape is recognised as GOOGLE'S when it turns up in some other provider's field.
115
- // Emphatically not a test that a Google key must look like this: an operator pasted a real,
116
- // working Gemini key in a newer shape and the old allowlist told them it was not a Google key.
117
- apiKeyPrefix: 'AIza',
118
- },
119
- {
120
- id: 'openrouter',
121
- title: 'OpenRouter',
122
- protocol: 'openai',
123
- baseUrl: VENDOR_API_BASE_URLS.openrouter,
124
- preferredModels: ['anthropic/claude-sonnet-5', 'google/gemini-2.5-pro', 'openai/gpt-5.6-sol'],
125
- kind: 'gateway',
126
- // Longer than OpenAI's `sk-`, and deliberately so: `apiKeyFormatWarning` resolves a key to the
127
- // MOST specific prefix that claims it, so `sk-or-v1-…` is attributed here rather than to OpenAI.
128
- apiKeyPrefix: 'sk-or-',
129
- },
130
- {
131
- id: 'ollama',
132
- title: 'Ollama',
133
- protocol: 'openai',
134
- baseUrl: VENDOR_API_BASE_URLS.ollama,
135
- preferredModels: [],
136
- kind: 'gateway',
137
- requiresApiKey: false,
138
- },
139
- ];
53
+ /** @deprecated Supply host presets. The former starter set is exported by integrations/media-providers/catalog. */
54
+ export const DEFAULT_PROVIDER_PRESETS = [];
140
55
  /**
141
56
  * One-line descriptions for the code-agent CLIs this package knows about,
142
57
  * keyed by the agent id `ExecutionPort` reports. Origin:
@@ -10,10 +10,8 @@ export interface ByokProviderFormProps {
10
10
  * as ANOTHER row's, so `apiKeyFormatWarning` can say whose it looks like. Never used to validate
11
11
  * the selected preset's own key — see that function for why that direction is forbidden.
12
12
  *
13
- * Defaults to `DEFAULT_PROVIDER_PRESETS`, matching `ExecutionTab`'s own default for the same
14
- * prop, so a host rendering this card standalone still gets the shipped vendor prefixes rather
15
- * than losing the check silently. A host with its own catalog should pass it, exactly as
16
- * `ExecutionTab` does.
13
+ * Defaults to no presets. Hosts rendering this card standalone must pass their catalog
14
+ * to enable cross-provider prefix hints, exactly as `ExecutionTab` does.
17
15
  */
18
16
  presets?: readonly ProviderPreset[];
19
17
  /** Live model-discovery result. `'ok'` suggestions win over the preset's
@@ -2,20 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useId, useState } from 'react';
3
3
  import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
4
4
  import { useT } from '../../../i18n/index.js';
5
- import { CUSTOM_MODEL_SENTINEL, DEFAULT_PROVIDER_PRESETS } from '../../constants.js';
5
+ import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
6
6
  import { apiKeyFormatWarning, parseMaxTokens, presetRequiresApiKey, shouldShowCustomModelInput, showsBaseUrlField, } from '../../rules.js';
7
7
  import { SearchableModelSelect } from './SearchableModelSelect.js';
8
8
  import { useByokFieldValidation } from '../hooks/use-byok-field-validation.hooks.js';
9
+ /** `' is-missing'` when `field` is a required field the operator still has to fill. */
10
+ function missingClass(validation, field) {
11
+ return validation.isMissing({ field }) ? ' is-missing' : '';
12
+ }
9
13
  /**
10
14
  * The BYOK credential card: API key, base URL, optional token cap, and model.
11
15
  * Origin: `components/byok/*` (`ByokKeyField`, `ByokProviderBaseUrl`,
12
16
  * `ByokModelField`, `ByokConnectionTestControl`) — already factored out
13
17
  * upstream, so this is assembly plus the origin's required-field marking.
14
18
  */
15
- export function ByokProviderForm({ config, onConfigChange, preset, presets = DEFAULT_PROVIDER_PRESETS, modelDiscovery, connectionTest, onTestConnection, canTestConnection = true, apiKeyFooter, formFooter, modelFooter, apiKeyStoredExternally = false, apiKeyPlaceholder, agentHandle, apiKeyActions, collapseAdvanced = false, deferMissingFeedback = false, }) {
19
+ export function ByokProviderForm({ config, onConfigChange, preset, presets = [], modelDiscovery, connectionTest, onTestConnection, canTestConnection = true, apiKeyFooter, formFooter, modelFooter, apiKeyStoredExternally = false, apiKeyPlaceholder, agentHandle, apiKeyActions, collapseAdvanced = false, deferMissingFeedback = false, }) {
16
20
  const t = useT();
17
- const keyInputId = useId();
18
- const KeyField = apiKeyActions ? 'div' : 'label';
19
21
  const [revealKey, setRevealKey] = useState(false);
20
22
  /** Whether the operator explicitly picked "Custom…" in the model picker — distinct from "the
21
23
  * saved model simply isn't in the live list". Same UI-local toggle, for the same reason, as
@@ -36,12 +38,53 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
36
38
  useEffect(() => {
37
39
  setRevealKey(false);
38
40
  }, [config.providerId]);
39
- const { missing, baseUrlInvalid, isMissing, markTouched } = useByokFieldValidation({ config, preset, apiKeyStoredExternally }, { deferMissingFeedback });
41
+ const validation = useByokFieldValidation({ config, preset, apiKeyStoredExternally }, { deferMissingFeedback });
42
+ const patch = (next) => onConfigChange({ ...config, ...next });
43
+ const part = { config, patch, preset, agentHandle, validation };
44
+ const connectionFields = _jsx(ByokConnectionFields, { ...part });
45
+ return (_jsxs("div", { className: "jini-byok-card", children: [_jsx("div", { className: "jini-byok-card-head", children: _jsx("h4", { className: "jini-byok-card-title", children: preset ? preset.title : t('Custom endpoint') }) }), presetRequiresApiKey(preset) ? (_jsx(ByokApiKeyField, { ...part, presets: presets, revealKey: revealKey, onToggleReveal: () => setRevealKey((shown) => !shown), apiKeyFooter: apiKeyFooter, apiKeyPlaceholder: apiKeyPlaceholder, apiKeyActions: apiKeyActions })) : null, collapseAdvanced ? null : connectionFields, _jsx(ByokModelRow, { ...part, modelDiscovery: modelDiscovery, explicitCustomModel: explicitCustomModel, setExplicitCustomModel: setExplicitCustomModel, connectionTest: connectionTest, onTestConnection: onTestConnection, canTestConnection: canTestConnection }), _jsx(ByokDiscoveryError, { modelDiscovery: modelDiscovery }), _jsx(ByokConnectionResult, { connectionTest: connectionTest, canTestConnection: canTestConnection }), _jsx(ByokSlot, { className: "jini-byok-model-footer", children: modelFooter }), collapseAdvanced ? _jsx(ByokAdvanced, { validation: validation, children: connectionFields }) : null, _jsx(ByokSlot, { className: "jini-byok-form-footer", children: formFooter })] }));
46
+ }
47
+ /** Base URL (unless the preset has a fixed origin) and Max tokens, on one wrapping row. */
48
+ function ByokConnectionFields({ config, patch, preset, agentHandle, validation }) {
49
+ const t = useT();
50
+ return (_jsx(_Fragment, { children: _jsxs("div", { className: "jini-byok-field-row", children: [showsBaseUrlField(preset) ? (_jsxs("label", { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'baseUrl' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Base URL'), preset?.baseUrl ? null : _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), _jsx("input", { className: 'jini-input' + missingClass(validation, 'baseUrl'), type: "url", inputMode: "url", spellCheck: false, value: config.baseUrl, onChange: (event) => patch({ baseUrl: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'base-url', role: 'field', label: t('Base URL') }) }) }), _jsx("span", { className: 'jini-field-hint' + (validation.baseUrlInvalid ? ' is-error' : ''), children: validation.baseUrlInvalid
51
+ ? t('Enter an absolute http(s) URL.')
52
+ : t('Default endpoint. Usually no need to change this.') })] })) : null, _jsxs("label", { className: "jini-field", children: [_jsx("span", { className: "jini-field-label", children: t('Max tokens (optional)') }), _jsx("input", { className: "jini-input", type: "number", min: 1, step: 1, value: config.maxTokens ?? '', onChange: (event) => patch({ maxTokens: parseMaxTokens(event.target.value) }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'max-tokens', role: 'field', label: t('Max tokens (optional)') }) }) }), _jsx("span", { className: "jini-field-hint", children: t('Cap on the response length. Leave blank to use the model default.') })] })] }) }));
53
+ }
54
+ /** The API-key field (with Show/Hide, host actions and the advisory format warning) plus the
55
+ * host's `apiKeyFooter` slot beneath it. */
56
+ function ByokApiKeyField({ config, patch, preset, agentHandle, validation, presets, revealKey, onToggleReveal, apiKeyFooter, apiKeyPlaceholder, apiKeyActions, }) {
57
+ const t = useT();
58
+ const keyInputId = useId();
59
+ const KeyField = apiKeyActions ? 'div' : 'label';
40
60
  /** Deliberately NOT folded into `missing` above: that set drives `is-missing` styling and the
41
61
  * Test-connection disable, and a wrong-SHAPED key must never reach either. */
42
62
  const keyFormatWarning = apiKeyFormatWarning(config, preset, presets);
43
- const suggestions = modelDiscovery.status === 'ok' ? modelDiscovery.models : (preset?.preferredModels ?? []);
44
- const modelListId = 'jini-byok-model-options';
63
+ return (_jsxs(_Fragment, { children: [_jsxs(KeyField, { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'apiKey' }), children: [_jsxs("span", { className: "jini-field-label", children: [_jsx(ByokApiKeyLabel, { keyInputId: apiKeyActions ? keyInputId : undefined }), preset?.apiKeyConsoleUrl ? (_jsx("a", { className: "jini-field-link", href: preset.apiKeyConsoleUrl, target: "_blank", rel: "noreferrer noopener", children: t('Get key') })) : null] }), _jsxs("span", { className: "jini-field-input-row", children: [_jsx("input", { ...(apiKeyActions ? { id: keyInputId } : {}), className: 'jini-input' + missingClass(validation, 'apiKey'), type: revealKey ? 'text' : 'password',
64
+ /* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on credential-shaped
65
+ fields (a long-standing intentional decision, not a bug). `off` here is what let the
66
+ reported autofill through: Chrome filled this field with the admin's own saved
67
+ PASSWORD, the form sent it, and the provider answered with its own "API key not
68
+ valid" — a true message about a string the operator never typed. `new-password` is
69
+ the value Chrome/Safari/Firefox actually honor for "this is not a saved-login
70
+ field", which suppresses both the credential dropdown and the silent fill.
71
+
72
+ Known trade-off, not fixed here: Chrome may now offer to GENERATE a password on
73
+ this field — a suggestion popup, not a silently wrong value. */
74
+ autoComplete: "new-password", spellCheck: false, placeholder: apiKeyPlaceholder, value: config.apiKey, onChange: (event) => patch({ apiKey: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'api-key', role: 'field', label: t('API key') }) }) }), _jsx("button", { type: "button", className: "jini-input-affix-btn", "aria-pressed": revealKey, onClick: onToggleReveal, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'reveal-key', role: 'button', label: t('Reveal API key') }) }), children: revealKey ? t('Hide') : t('Show') })] }), apiKeyActions ? _jsx("span", { className: "jini-byok-key-actions", children: apiKeyActions }) : null, _jsx("span", { className: "jini-field-hint", children: t('Stored only by this host.') }), keyFormatWarning ? (_jsx("span", { className: "jini-field-hint is-error", role: "status", children: t(keyFormatWarning.message, keyFormatWarning.vars) })) : null] }), _jsx(ByokSlot, { className: "jini-byok-key-footer", children: apiKeyFooter })] }));
75
+ }
76
+ /** "API key *". With host key actions the field is a `<div>`, so the name gets its own `<label>`
77
+ * pointing at the input by id; otherwise the whole field is already the label. */
78
+ function ByokApiKeyLabel({ keyInputId }) {
79
+ const t = useT();
80
+ if (keyInputId) {
81
+ return (_jsxs("label", { className: "jini-field-label", htmlFor: keyInputId, children: [_jsx("span", { children: t('API key') }), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }));
82
+ }
83
+ return (_jsxs(_Fragment, { children: [t('API key'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }));
84
+ }
85
+ /** The Model field (live picker, free-text box, or both) with "Test connection" beside it. */
86
+ function ByokModelRow({ config, patch, preset, agentHandle, validation, modelDiscovery, explicitCustomModel, setExplicitCustomModel, connectionTest, onTestConnection, canTestConnection, }) {
87
+ const t = useT();
45
88
  /**
46
89
  * Live discovery turns the Model field from a text box into a real picker.
47
90
  *
@@ -66,41 +109,95 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
66
109
  // opens the box for an explicit "Custom…" pick AND for a value that simply isn't in the list, so
67
110
  // a hydrated or hand-typed id is never silently replaced by whatever sorts first.
68
111
  const customModelActive = showModelPicker && shouldShowCustomModelInput(config.model, liveModels, explicitCustomModel);
69
- const patch = (next) => onConfigChange({ ...config, ...next });
70
- const connectionFields = (_jsx(_Fragment, { children: _jsxs("div", { className: "jini-byok-field-row", children: [showsBaseUrlField(preset) ? (_jsxs("label", { className: "jini-field", onBlurCapture: () => markTouched({ field: 'baseUrl' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Base URL'), preset?.baseUrl ? null : _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), _jsx("input", { className: 'jini-input' + (isMissing({ field: 'baseUrl' }) ? ' is-missing' : ''), type: "url", inputMode: "url", spellCheck: false, value: config.baseUrl, onChange: (event) => patch({ baseUrl: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'base-url', role: 'field', label: t('Base URL') }) }) }), _jsx("span", { className: 'jini-field-hint' + (baseUrlInvalid ? ' is-error' : ''), children: baseUrlInvalid
71
- ? t('Enter an absolute http(s) URL.')
72
- : t('Default endpoint. Usually no need to change this.') })] })) : null, _jsxs("label", { className: "jini-field", children: [_jsx("span", { className: "jini-field-label", children: t('Max tokens (optional)') }), _jsx("input", { className: "jini-input", type: "number", min: 1, step: 1, value: config.maxTokens ?? '', onChange: (event) => patch({ maxTokens: parseMaxTokens(event.target.value) }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'max-tokens', role: 'field', label: t('Max tokens (optional)') }) }) }), _jsx("span", { className: "jini-field-hint", children: t('Cap on the response length. Leave blank to use the model default.') })] })] }) }));
73
- return (_jsxs("div", { className: "jini-byok-card", children: [_jsx("div", { className: "jini-byok-card-head", children: _jsx("h4", { className: "jini-byok-card-title", children: preset ? preset.title : t('Custom endpoint') }) }), presetRequiresApiKey(preset) ? (_jsxs(_Fragment, { children: [_jsxs(KeyField, { className: "jini-field", onBlurCapture: () => markTouched({ field: 'apiKey' }), children: [_jsxs("span", { className: "jini-field-label", children: [apiKeyActions ? (_jsxs("label", { className: "jini-field-label", htmlFor: keyInputId, children: [_jsx("span", { children: t('API key') }), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] })) : (_jsxs(_Fragment, { children: [t('API key'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] })), preset?.apiKeyConsoleUrl ? (_jsx("a", { className: "jini-field-link", href: preset.apiKeyConsoleUrl, target: "_blank", rel: "noreferrer noopener", children: t('Get key') })) : null] }), _jsxs("span", { className: "jini-field-input-row", children: [_jsx("input", { ...(apiKeyActions ? { id: keyInputId } : {}), className: 'jini-input' + (isMissing({ field: 'apiKey' }) ? ' is-missing' : ''), type: revealKey ? 'text' : 'password',
74
- /* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on credential-shaped
75
- fields (a long-standing intentional decision, not a bug). `off` here is what let the
76
- reported autofill through: Chrome filled this field with the admin's own saved
77
- PASSWORD, the form sent it, and the provider answered with its own "API key not
78
- valid" — a true message about a string the operator never typed. `new-password` is
79
- the value Chrome/Safari/Firefox actually honor for "this is not a saved-login
80
- field", which suppresses both the credential dropdown and the silent fill.
81
-
82
- Known trade-off, not fixed here: Chrome may now offer to GENERATE a password on
83
- this field — a suggestion popup, not a silently wrong value. */
84
- autoComplete: "new-password", spellCheck: false, placeholder: apiKeyPlaceholder, value: config.apiKey, onChange: (event) => patch({ apiKey: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'api-key', role: 'field', label: t('API key') }) }) }), _jsx("button", { type: "button", className: "jini-input-affix-btn", "aria-pressed": revealKey, onClick: () => setRevealKey((shown) => !shown), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'reveal-key', role: 'button', label: t('Reveal API key') }) }), children: revealKey ? t('Hide') : t('Show') })] }), apiKeyActions ? _jsx("span", { className: "jini-byok-key-actions", children: apiKeyActions }) : null, _jsx("span", { className: "jini-field-hint", children: t('Stored only by this host.') }), keyFormatWarning ? (_jsx("span", { className: "jini-field-hint is-error", role: "status", children: t(keyFormatWarning.message, keyFormatWarning.vars) })) : null] }), apiKeyFooter ? _jsx("div", { className: "jini-byok-key-footer", children: apiKeyFooter }) : null] })) : null, collapseAdvanced ? null : connectionFields, _jsxs("div", { className: "jini-byok-model-row", children: [_jsxs("label", { className: "jini-field", onBlurCapture: () => markTouched({ field: 'model' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Model'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), showModelPicker ? (_jsx(SearchableModelSelect, { className: 'jini-input' + (isMissing({ field: 'model' }) ? ' is-missing' : ''), ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: "jini-byok-model-select", searchInputTestId: "jini-byok-model-search", ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {}), value: customModelActive ? CUSTOM_MODEL_SENTINEL : config.model, models: liveModels.map((model) => ({ id: model, label: model })), additionalOptions: [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }], onChange: (next) => {
85
- if (next === CUSTOM_MODEL_SENTINEL) {
86
- // Open the free-text box WITHOUT clearing `config.model`. Blanking it here would
87
- // discard a working model the moment someone opened the picker to look at it.
88
- setExplicitCustomModel(true);
89
- return;
90
- }
91
- setExplicitCustomModel(false);
92
- patch({ model: next });
93
- } })) : null, !showModelPicker || customModelActive ? (_jsx("input", { className: 'jini-input' + (isMissing({ field: 'model' }) ? ' is-missing' : ''), list: !showModelPicker && suggestions.length > 0 ? modelListId : undefined, spellCheck: false, value: config.model, onChange: (event) => patch({ model: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'model', role: 'field', label: t('Model') }) }) })) : null, !showModelPicker && suggestions.length > 0 ? (_jsx("datalist", { id: modelListId, children: suggestions.map((model) => (_jsx("option", { value: model }, model))) })) : null] }), canTestConnection ? (_jsx("button", { type: "button", className: "jini-btn jini-byok-test-btn", disabled: connectionTest.status === 'testing' || missing.size > 0, onClick: onTestConnection, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'test-connection', role: 'button', label: t('Test connection') }) }), children: connectionTest.status === 'testing' ? t('Testing…') : t('Test connection') })) : null] }), modelDiscovery.status === 'error' ? (
94
- // Non-blocking (the field above stays editable, with the preset's static
95
- // suggestions) but never silent — a discovery failure is an operator-actionable
96
- // fact (bad key, wrong base URL, unreachable endpoint), not "no models exist".
97
- //
98
- // Moved out of the Model `<label>` when the probe button moved up beside it: as a label
99
- // child it grew the field and pushed the bottom-aligned button off the input's line every
100
- // time discovery failed. Same element, same `.jini-field-hint.is-error[role="status"]`
101
- // identity host suites locate it by — only its parent changed.
102
- _jsx("span", { className: "jini-field-hint is-error", role: "status", children: t('Could not load live models: {message}', { message: t(modelDiscovery.message) }) })) : null, canTestConnection && (connectionTest.status === 'ok' || connectionTest.status === 'error') ? (_jsx("div", { className: "jini-byok-test-row", children: _jsx("span", { className: `jini-byok-test-status is-${connectionTest.status}`, role: connectionTest.status === 'error' ? 'alert' : 'status', children: connectionTest.status === 'ok'
103
- ? (connectionTest.message !== undefined ? t(connectionTest.message) : t('Connection succeeded'))
104
- : t(connectionTest.message) }) })) : null, modelFooter ? _jsx("div", { className: "jini-byok-model-footer", children: modelFooter }) : null, collapseAdvanced ? (_jsxs("details", { className: "jini-byok-advanced", open: baseUrlInvalid || missing.has('baseUrl') || undefined, children: [_jsx("summary", { children: t('Advanced') }), connectionFields] })) : null, formFooter ? _jsx("div", { className: "jini-byok-form-footer", children: formFooter }) : null] }));
112
+ const modelClassName = 'jini-input' + missingClass(validation, 'model');
113
+ return (
114
+ // "Test connection" sits BESIDE the Model control (owner ruling, 2026-09-02), not on a row of
115
+ // its own underneath it. It probes the endpoint the Model field belongs to, and a row's
116
+ // distance made it read as unrelated to any particular field.
117
+ //
118
+ // A SIBLING of the label, never a child: a `<button>` inside a `<label>` makes every click on
119
+ // the button also focus and activate the labelled control — here, opening the model picker —
120
+ // and nesting interactive controls in a label is an accessibility defect regardless. Same rule
121
+ // `apiKeyFooter` documents.
122
+ //
123
+ // Wrapping, and `align-items: flex-end`, so the button sits on the input's own line at a
124
+ // comfortable width and drops below the field at a narrow one. Both status lines
125
+ // (discovery error, connection result) are deliberately OUTSIDE this row: inside it they would
126
+ // either sit beside the button or, as label children, push it out of line the moment either
127
+ // appeared.
128
+ _jsxs("div", { className: "jini-byok-model-row", children: [_jsxs("label", { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'model' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Model'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), showModelPicker ? (_jsx(ByokLiveModelSelect, { className: modelClassName, agentHandle: agentHandle, value: customModelActive ? CUSTOM_MODEL_SENTINEL : config.model, liveModels: liveModels, patch: patch, setExplicitCustomModel: setExplicitCustomModel })) : null, !showModelPicker || customModelActive ? (_jsx(ByokModelTextInput, { className: modelClassName, value: config.model, patch: patch, agentHandle: agentHandle, suggestions: showModelPicker ? [] : modelSuggestions(modelDiscovery, preset) })) : null] }), _jsx(ByokTestConnectionButton, { agentHandle: agentHandle, validation: validation, connectionTest: connectionTest, onTestConnection: onTestConnection, canTestConnection: canTestConnection })] }));
129
+ }
130
+ /** The probe's RESULT keeps its own full-width line. Beside the button it would have made the
131
+ * Model row's width depend on the length of a provider's error message. Renders nothing until
132
+ * the probe has finished. */
133
+ function ByokConnectionResult({ connectionTest, canTestConnection }) {
134
+ const t = useT();
135
+ if (!canTestConnection)
136
+ return null;
137
+ if (connectionTest.status !== 'ok' && connectionTest.status !== 'error')
138
+ return null;
139
+ return (_jsx("div", { className: "jini-byok-test-row", children: _jsx("span", { className: `jini-byok-test-status is-${connectionTest.status}`, role: connectionTest.status === 'error' ? 'alert' : 'status', children: connectionTest.status === 'ok'
140
+ ? (connectionTest.message !== undefined ? t(connectionTest.message) : t('Connection succeeded'))
141
+ : t(connectionTest.message) }) }));
142
+ }
143
+ /** Discovery failure, as an inline hint below the Model row. Renders nothing otherwise. */
144
+ function ByokDiscoveryError({ modelDiscovery }) {
145
+ const t = useT();
146
+ if (modelDiscovery.status !== 'error')
147
+ return null;
148
+ return (
149
+ // Non-blocking (the field above stays editable, with the preset's static
150
+ // suggestions) but never silent — a discovery failure is an operator-actionable
151
+ // fact (bad key, wrong base URL, unreachable endpoint), not "no models exist".
152
+ //
153
+ // Moved out of the Model `<label>` when the probe button moved up beside it: as a label
154
+ // child it grew the field and pushed the bottom-aligned button off the input's line every
155
+ // time discovery failed. Same element, same `.jini-field-hint.is-error[role="status"]`
156
+ // identity host suites locate it by — only its parent changed.
157
+ _jsx("span", { className: "jini-field-hint is-error", role: "status", children: t('Could not load live models: {message}', { message: t(modelDiscovery.message) }) }));
158
+ }
159
+ /** A host slot's wrapper `<div>`, rendered only when the host supplied content for it. */
160
+ function ByokSlot({ className, children }) {
161
+ return children ? _jsx("div", { className: className, children: children }) : null;
162
+ }
163
+ /** The collapsed "Advanced" disclosure for Base URL and Max tokens. Opens on its own while Base URL
164
+ * is invalid or missing, so a required-field problem is never hidden. */
165
+ function ByokAdvanced({ validation, children }) {
166
+ const t = useT();
167
+ return (_jsxs("details", { className: "jini-byok-advanced", open: validation.baseUrlInvalid || validation.missing.has('baseUrl') || undefined, children: [_jsx("summary", { children: t('Advanced') }), children] }));
168
+ }
169
+ /** The live-model picker. "Custom…" opens the free-text box beside it; any real pick closes it. */
170
+ function ByokLiveModelSelect({ className, agentHandle, value, liveModels, patch, setExplicitCustomModel, }) {
171
+ const t = useT();
172
+ return (_jsx(SearchableModelSelect, { className: className, ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: "jini-byok-model-select", searchInputTestId: "jini-byok-model-search", ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {}), value: value, models: liveModels.map((model) => ({ id: model, label: model })), additionalOptions: [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }], onChange: (next) => {
173
+ if (next === CUSTOM_MODEL_SENTINEL) {
174
+ // Open the free-text box WITHOUT clearing `config.model`. Blanking it here would
175
+ // discard a working model the moment someone opened the picker to look at it.
176
+ setExplicitCustomModel(true);
177
+ return;
178
+ }
179
+ setExplicitCustomModel(false);
180
+ patch({ model: next });
181
+ } }));
182
+ }
183
+ /** "Test connection", beside the Model field. Disabled while a probe runs or a required field is
184
+ * blank; absent for hosts with no probe endpoint. */
185
+ function ByokTestConnectionButton({ agentHandle, validation, connectionTest, onTestConnection, canTestConnection, }) {
186
+ const t = useT();
187
+ if (!canTestConnection)
188
+ return null;
189
+ return (_jsx("button", { type: "button", className: "jini-btn jini-byok-test-btn", disabled: connectionTest.status === 'testing' || validation.missing.size > 0, onClick: onTestConnection, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'test-connection', role: 'button', label: t('Test connection') }) }), children: connectionTest.status === 'testing' ? t('Testing…') : t('Test connection') }));
190
+ }
191
+ /** Autocomplete hints for the free-text Model box: the live list when discovery succeeded, else the
192
+ * preset's static `preferredModels`. */
193
+ function modelSuggestions(modelDiscovery, preset) {
194
+ return modelDiscovery.status === 'ok' ? modelDiscovery.models : (preset?.preferredModels ?? []);
195
+ }
196
+ /** The free-text Model box, with a `<datalist>` autocomplete when there are suggestions. */
197
+ function ByokModelTextInput({ className, value, patch, agentHandle, suggestions, }) {
198
+ const t = useT();
199
+ const modelListId = 'jini-byok-model-options';
200
+ const showDatalist = suggestions.length > 0;
201
+ return (_jsxs(_Fragment, { children: [_jsx("input", { className: className, list: showDatalist ? modelListId : undefined, spellCheck: false, value: value, onChange: (event) => patch({ model: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'model', role: 'field', label: t('Model') }) }) }), showDatalist ? (_jsx("datalist", { id: modelListId, children: suggestions.map((model) => (_jsx("option", { value: model }, model))) })) : null] }));
105
202
  }
106
203
  //# sourceMappingURL=ByokProviderForm.js.map
@@ -5,8 +5,7 @@ export interface ExecutionTabProps {
5
5
  config: ExecutionConfig;
6
6
  onConfigChange: (config: ExecutionConfig) => void;
7
7
  port: ExecutionPort;
8
- /** Endpoint catalog. Defaults to this package's small starter set — a host
9
- * with its own provider list passes it here rather than editing ours. */
8
+ /** Host-owned endpoint catalog. Omitted means manual endpoint entry. */
10
9
  presets?: readonly ProviderPreset[];
11
10
  /** Per-agent CLI env-var catalog (proxy URLs, custom config dirs, a
12
11
  * binary-path override). Defaults to this package's starter set for the
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
4
4
  import { useT } from '../../../i18n/index.js';
5
- import { DEFAULT_AGENT_CLI_ENV_FIELDS, DEFAULT_PROVIDER_PRESETS } from '../../constants.js';
5
+ import { DEFAULT_AGENT_CLI_ENV_FIELDS } from '../../constants.js';
6
6
  import { groupPresets, isProviderConfigured, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, resolveSelectedPreset, selectedAgentModel, } from '../../rules.js';
7
7
  import { useExecutionTab } from '../hooks/useExecutionTab.js';
8
8
  import { ByokProviderForm } from './ByokProviderForm.js';
@@ -20,13 +20,14 @@ import { ProviderChipGroup } from './ProviderChipGroup.js';
20
20
  * wallet — balance polling, top-up, sign-in coachmarks — is deliberately not
21
21
  * ported; it is product-bound, not generic.
22
22
  */
23
- export function ExecutionTab({ config, onConfigChange, port, presets = DEFAULT_PROVIDER_PRESETS, cliEnvFields = DEFAULT_AGENT_CLI_ENV_FIELDS, localCliUnavailableReason, renderAgentIcon, localCliScopeLabel, autoDetect = true, ariaLabel, apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder, canDiscoverModels = true, describeProbeError, agentHandle, }) {
23
+ export function ExecutionTab({ config, onConfigChange, port, presets = [], cliEnvFields = DEFAULT_AGENT_CLI_ENV_FIELDS, localCliUnavailableReason, renderAgentIcon, localCliScopeLabel, autoDetect = true, ariaLabel, apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder, canDiscoverModels = true, describeProbeError, agentHandle, }) {
24
24
  const t = useT();
25
- const { agents, scan, connectionTest, modelDiscovery, agentTest, rescan, testConnection, testAgent, loadModels, resetModelDiscovery, canRescan, canTestAgent, } = useExecutionTab({
25
+ const tab = useExecutionTab({
26
26
  port,
27
27
  autoDetect: autoDetect && config.mode === 'local-cli',
28
28
  ...(describeProbeError ? { describeProbeError } : {}),
29
29
  });
30
+ const { loadModels, resetModelDiscovery } = tab;
30
31
  const selectedPreset = resolveSelectedPreset(presets, config.byok);
31
32
  /**
32
33
  * Whether a key EXISTS, never what it is. Keyed on the boolean specifically so the effect below
@@ -68,39 +69,60 @@ export function ExecutionTab({ config, onConfigChange, port, presets = DEFAULT_P
68
69
  if (!canDiscoverModels)
69
70
  resetModelDiscovery();
70
71
  }, [canDiscoverModels, resetModelDiscovery]);
71
- const { protocols, gateways } = groupPresets(presets);
72
- const configuredPresetIds = new Set(presets.filter((preset) => isProviderConfigured(config.byok, preset)).map((preset) => preset.id));
73
- const localCliDisabled = Boolean(localCliUnavailableReason);
72
+ // No "local-cli while unavailable" guard here: `ExecutionModeSwitch` renders that tab `disabled`
73
+ // whenever `localCliUnavailableReason` is set, and React never dispatches `onClick` for a disabled
74
+ // button, so `setMode('local-cli')` cannot be reached in that state.
74
75
  const setMode = (mode) => {
75
- if (mode === 'local-cli' && localCliDisabled)
76
- return;
77
76
  onConfigChange(nextConfigForModeChange(config, mode));
78
77
  };
79
- const selectPreset = (preset) => onConfigChange({ ...config, byok: nextConfigForPresetSelect(config.byok, preset) });
80
78
  const modes = [
81
79
  { id: 'local-cli', title: t('Local CLI'), meta: t('Run an agent installed on this machine') },
82
80
  { id: 'byok', title: t('BYOK'), meta: t('Use your own API credentials') },
83
81
  ];
84
- return (_jsxs("div", { className: "jini-settings-execution", "aria-label": ariaLabel ?? t('Execution mode'), children: [_jsx("div", { className: "jini-seg-control", role: "tablist", "aria-label": t('Execution mode'), style: { '--seg-cols': modes.length }, children: modes.map((mode) => {
85
- const disabled = mode.id === 'local-cli' && localCliDisabled;
86
- return (_jsxs("button", { type: "button", role: "tab", "aria-selected": config.mode === mode.id, disabled: disabled, title: disabled ? localCliUnavailableReason : undefined, className: 'jini-seg-btn' + (config.mode === mode.id ? ' active' : ''), onClick: () => setMode(mode.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: `mode-${mode.id}`, role: 'button', label: mode.title }) }), children: [_jsx("span", { className: "jini-seg-title", children: mode.title }), _jsx("span", { className: "jini-seg-meta", children: disabled ? localCliUnavailableReason : mode.meta })] }, mode.id));
87
- }) }), config.mode === 'local-cli' ? (_jsx(LocalCliAgentList, { agents: agents, config: config.localCli, scan: scan, agentTest: agentTest, onSelect: (agentId) => onConfigChange(nextConfigForAgentSelect(config, agentId)), onModelChange: (agentId, model) => onConfigChange(nextConfigForAgentModel(config, agentId, model)), onReasoningChange: (agentId, reasoning) => onConfigChange(nextConfigForAgentReasoning(config, agentId, reasoning)), onEnvChange: (agentId, envKey, value) => onConfigChange(nextConfigForAgentCliEnvChange(config, agentId, envKey, value)), cliEnvFields: cliEnvFields, onRescan: canRescan ? rescan : undefined, onTest: canTestAgent
88
- ? (agent) => testAgent(agent.id, selectedAgentModel(config.localCli, agent) || undefined)
89
- : undefined, renderAgentIcon: renderAgentIcon, scopeLabel: localCliScopeLabel, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'local-cli' }) } : {}) })) : (_jsxs("section", { className: "jini-settings-section jini-settings-byok", children: [_jsx(ProviderChipGroup, { label: t('Protocols'), presets: protocols, selectedPresetId: selectedPreset?.id ?? null, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'protocol' }) } : {}) }), _jsx(ProviderChipGroup, { label: t('Gateways'), presets: gateways, selectedPresetId: selectedPreset?.id ?? null, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'gateway' }) } : {}) }), _jsx(ByokProviderForm, { config: config.byok, onConfigChange: (byok) => onConfigChange({ ...config, byok }), preset: selectedPreset, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'byok' }) } : {}),
90
- // The WHOLE catalog, not the `protocols`/`gateways` halves the chip rows take: the card
91
- // only reads it to recognise a pasted key as some other row's, and a key pasted from a
92
- // gateway into a protocol field is exactly the mistake worth naming.
93
- presets: presets, modelDiscovery: modelDiscovery, connectionTest: connectionTest,
94
- // Also re-runs discovery with the CURRENT byok config, not just the
95
- // connectivity probe — see this file's model-discovery effect above
96
- // for why that matters: a stale "could not load live models" error
97
- // from before this key existed (or from any earlier transient
98
- // failure) would otherwise never clear, even once Test Connection
99
- // itself goes green with the same config.
100
- onTestConnection: () => {
101
- testConnection(config.byok);
102
- if (canDiscoverModels && typeof port.listModels === 'function')
103
- loadModels(config.byok);
104
- }, ...(apiKeyFooter !== undefined ? { apiKeyFooter } : {}), ...(formFooter !== undefined ? { formFooter } : {}), ...(apiKeyStoredExternally !== undefined ? { apiKeyStoredExternally } : {}), ...(apiKeyPlaceholder !== undefined ? { apiKeyPlaceholder } : {}) })] }))] }));
82
+ return (_jsxs("div", { className: "jini-settings-execution", "aria-label": ariaLabel ?? t('Execution mode'), children: [_jsx(ExecutionModeSwitch, { modes: modes, activeMode: config.mode, localCliUnavailableReason: localCliUnavailableReason, onSelect: setMode, agentHandle: agentHandle }), config.mode === 'local-cli' ? (_jsx(LocalCliPane, { config: config, onConfigChange: onConfigChange, tab: tab, cliEnvFields: cliEnvFields, renderAgentIcon: renderAgentIcon, localCliScopeLabel: localCliScopeLabel, agentHandle: agentHandle })) : (_jsx(ByokPane, { config: config, onConfigChange: onConfigChange, presets: presets, selectedPreset: selectedPreset, tab: tab, canDiscoverModels: canDiscoverModels, canListModels: typeof port.listModels === 'function', formProps: { apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder }, agentHandle: agentHandle }))] }));
83
+ }
84
+ /** The Local CLI ↔ BYOK segmented control. Local CLI is disabled, with the host's reason as its
85
+ * tooltip and caption, while the host says it is unavailable. */
86
+ function ExecutionModeSwitch({ modes, activeMode, localCliUnavailableReason, onSelect, agentHandle }) {
87
+ const t = useT();
88
+ const localCliDisabled = Boolean(localCliUnavailableReason);
89
+ return (_jsx("div", { className: "jini-seg-control", role: "tablist", "aria-label": t('Execution mode'), style: { '--seg-cols': modes.length }, children: modes.map((mode) => {
90
+ const disabled = mode.id === 'local-cli' && localCliDisabled;
91
+ return (_jsxs("button", { type: "button", role: "tab", "aria-selected": activeMode === mode.id, disabled: disabled, title: disabled ? localCliUnavailableReason : undefined, className: 'jini-seg-btn' + (activeMode === mode.id ? ' active' : ''), onClick: () => onSelect(mode.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: `mode-${mode.id}`, role: 'button', label: mode.title }) }), children: [_jsx("span", { className: "jini-seg-title", children: mode.title }), _jsx("span", { className: "jini-seg-meta", children: disabled ? localCliUnavailableReason : mode.meta })] }, mode.id));
92
+ }) }));
93
+ }
94
+ /** The BYOK half of the tab: protocol and gateway chip rows over the credential card. */
95
+ function ByokPane({ config, onConfigChange, presets, selectedPreset, tab, canDiscoverModels, canListModels, formProps, agentHandle, }) {
96
+ const t = useT();
97
+ const { protocols, gateways } = groupPresets(presets);
98
+ const configuredPresetIds = new Set(presets.filter((preset) => isProviderConfigured(config.byok, preset)).map((preset) => preset.id));
99
+ const selectPreset = (preset) => onConfigChange({ ...config, byok: nextConfigForPresetSelect(config.byok, preset) });
100
+ const selectedPresetId = selectedPreset?.id ?? null;
101
+ return (_jsxs("section", { className: "jini-settings-section jini-settings-byok", children: [_jsx(ProviderChipGroup, { label: t('Protocols'), presets: protocols, selectedPresetId: selectedPresetId, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...subHandleProps(agentHandle, 'protocol') }), _jsx(ProviderChipGroup, { label: t('Gateways'), presets: gateways, selectedPresetId: selectedPresetId, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...subHandleProps(agentHandle, 'gateway') }), _jsx(ByokProviderForm, { config: config.byok, onConfigChange: (byok) => onConfigChange({ ...config, byok }), preset: selectedPreset, ...subHandleProps(agentHandle, 'byok'),
102
+ // The WHOLE catalog, not the `protocols`/`gateways` halves the chip rows take: the card
103
+ // only reads it to recognise a pasted key as some other row's, and a key pasted from a
104
+ // gateway into a protocol field is exactly the mistake worth naming.
105
+ presets: presets, modelDiscovery: tab.modelDiscovery, connectionTest: tab.connectionTest,
106
+ // Also re-runs discovery with the CURRENT byok config, not just the
107
+ // connectivity probe — see this file's model-discovery effect above
108
+ // for why that matters: a stale "could not load live models" error
109
+ // from before this key existed (or from any earlier transient
110
+ // failure) would otherwise never clear, even once Test Connection
111
+ // itself goes green with the same config.
112
+ onTestConnection: () => {
113
+ tab.testConnection(config.byok);
114
+ if (canDiscoverModels && canListModels)
115
+ tab.loadModels(config.byok);
116
+ }, ...(formProps.apiKeyFooter !== undefined ? { apiKeyFooter: formProps.apiKeyFooter } : {}), ...(formProps.formFooter !== undefined ? { formFooter: formProps.formFooter } : {}), ...(formProps.apiKeyStoredExternally !== undefined ? { apiKeyStoredExternally: formProps.apiKeyStoredExternally } : {}), ...(formProps.apiKeyPlaceholder !== undefined ? { apiKeyPlaceholder: formProps.apiKeyPlaceholder } : {}) })] }));
117
+ }
118
+ /** The Local CLI half of the tab: detected agents, wired to the config's next-state rules. */
119
+ function LocalCliPane({ config, onConfigChange, tab, cliEnvFields, renderAgentIcon, localCliScopeLabel, agentHandle }) {
120
+ return (_jsx(LocalCliAgentList, { agents: tab.agents, config: config.localCli, scan: tab.scan, agentTest: tab.agentTest, onSelect: (agentId) => onConfigChange(nextConfigForAgentSelect(config, agentId)), onModelChange: (agentId, model) => onConfigChange(nextConfigForAgentModel(config, agentId, model)), onReasoningChange: (agentId, reasoning) => onConfigChange(nextConfigForAgentReasoning(config, agentId, reasoning)), onEnvChange: (agentId, envKey, value) => onConfigChange(nextConfigForAgentCliEnvChange(config, agentId, envKey, value)), cliEnvFields: cliEnvFields, onRescan: tab.canRescan ? tab.rescan : undefined, onTest: tab.canTestAgent
121
+ ? (agent) => tab.testAgent(agent.id, selectedAgentModel(config.localCli, agent) || undefined)
122
+ : undefined, renderAgentIcon: renderAgentIcon, scopeLabel: localCliScopeLabel, ...subHandleProps(agentHandle, 'local-cli') }));
123
+ }
124
+ /** `{ agentHandle }` for a child's sub-handle under this tab's base, or nothing when no base is set. */
125
+ function subHandleProps(agentHandle, action) {
126
+ return agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action }) } : {};
105
127
  }
106
128
  //# sourceMappingURL=ExecutionTab.js.map