@jini-ai/ui 0.5.4 → 0.5.6

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 (270) hide show
  1. package/CHANGELOG.md +10 -0
  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/Select/Select.hooks.d.ts +21 -0
  29. package/dist/features/admin-widgets/components/Select/Select.hooks.js +24 -2
  30. package/dist/features/admin-widgets/components/Select/Select.js +4 -3
  31. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
  32. package/dist/features/admin-widgets/index.d.ts +14 -2
  33. package/dist/features/admin-widgets/index.js +7 -1
  34. package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
  35. package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
  36. package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
  37. package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
  38. package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
  39. package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
  40. package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
  41. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
  42. package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
  43. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
  44. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
  45. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
  46. package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
  47. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
  48. package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
  49. package/dist/features/asset-tree-browser/rules.js +40 -22
  50. package/dist/features/browser-chrome/rules.js +74 -70
  51. package/dist/features/charts/browser.js +11 -5
  52. package/dist/features/charts/react.js +2 -2
  53. package/dist/features/charts/table.js +2 -2
  54. package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
  55. package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
  56. package/dist/features/connectors/ConnectorsBrowser.js +26 -14
  57. package/dist/features/connectors/components/ConnectorCard.js +67 -38
  58. package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
  59. package/dist/features/connectors/components/ConnectorGrid.js +11 -6
  60. package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
  61. package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
  62. package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
  63. package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
  64. package/dist/features/connectors/rules.d.ts +8 -1
  65. package/dist/features/connectors/rules.js +42 -20
  66. package/dist/features/connectors/types.d.ts +11 -0
  67. package/dist/features/execution/constants.d.ts +4 -10
  68. package/dist/features/execution/constants.js +9 -94
  69. package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
  70. package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
  71. package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
  72. package/dist/features/execution/react/components/ExecutionTab.js +52 -30
  73. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
  74. package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
  75. package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
  76. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
  77. package/dist/features/execution/react/hooks/useReasoningControl.d.ts +1 -1
  78. package/dist/features/execution/react/hooks/useReasoningControl.js +116 -75
  79. package/dist/features/execution/rules.d.ts +13 -2
  80. package/dist/features/execution/rules.js +51 -11
  81. package/dist/features/execution/types.d.ts +6 -42
  82. package/dist/features/execution/types.js +0 -17
  83. package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
  84. package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
  85. package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
  86. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
  87. package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
  88. package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
  89. package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
  90. package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
  91. package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
  92. package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
  93. package/dist/features/html-editor/css.js +76 -69
  94. package/dist/features/html-editor/editor-blocks.js +47 -33
  95. package/dist/features/html-editor/editor-serialization.js +6 -5
  96. package/dist/features/html-editor/protected-elements.js +39 -28
  97. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
  98. package/dist/features/html-editor/source-splice.js +51 -30
  99. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
  100. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
  101. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
  102. package/dist/features/i18n/locale.js +20 -16
  103. package/dist/features/iframe-pool/react/dom-sync.js +8 -6
  104. package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
  105. package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
  106. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  107. package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +6 -6
  108. package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
  109. package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +6 -6
  110. package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
  111. package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
  112. package/dist/features/lexical-rich-text-editor/rules.js +87 -74
  113. package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
  114. package/dist/features/mcp-ui/secret-card.js +111 -61
  115. package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
  116. package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
  117. package/dist/features/mcp-ui/surfaces/document.js +10 -2
  118. package/dist/features/mcp-ui/surfaces/fields.js +30 -50
  119. package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
  120. package/dist/features/mcp-ui/surfaces/form.js +43 -21
  121. package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
  122. package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
  123. package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
  124. package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
  125. package/dist/features/media-providers/constants.d.ts +1 -0
  126. package/dist/features/media-providers/constants.js +2 -81
  127. package/dist/features/media-providers/index.d.ts +1 -1
  128. package/dist/features/media-providers/ports.d.ts +2 -0
  129. package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
  130. package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
  131. package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
  132. package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
  133. package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
  134. package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
  135. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
  136. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
  137. package/dist/features/media-providers/rules.js +17 -10
  138. package/dist/features/media-providers/types.d.ts +4 -22
  139. package/dist/features/media-providers/types.js +3 -8
  140. package/dist/features/memory/formatters.js +127 -92
  141. package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
  142. package/dist/features/memory/react/components/MemoryList.js +37 -18
  143. package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
  144. package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
  145. package/dist/features/observability/long-task.d.ts +0 -12
  146. package/dist/features/observability/long-task.js +15 -19
  147. package/dist/features/observability/resource-error.d.ts +0 -10
  148. package/dist/features/observability/resource-error.js +18 -13
  149. package/dist/features/observability/stuck-run.js +10 -6
  150. package/dist/features/observability/telemetry-url.d.ts +6 -0
  151. package/dist/features/observability/telemetry-url.js +16 -0
  152. package/dist/features/observability/white-screen.js +12 -24
  153. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  154. package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
  155. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
  156. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
  157. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
  158. package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
  159. package/dist/features/progress-card/components/ProgressCard.js +7 -3
  160. package/dist/features/progress-card/reference-adapters.js +59 -54
  161. package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
  162. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
  163. package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
  164. package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
  165. package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
  166. package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
  167. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
  168. package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
  169. package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
  170. package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
  171. package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
  172. package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
  173. package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
  174. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
  175. package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
  176. package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
  177. package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
  178. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
  179. package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
  180. package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
  181. package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
  182. package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
  183. package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
  184. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
  185. package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
  186. package/dist/features/resource-dashboard/rules.js +2 -8
  187. package/dist/features/sketch-editor/dom.js +150 -99
  188. package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
  189. package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
  190. package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
  191. package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
  192. package/dist/features/skills/react/components/SkillRow.js +38 -18
  193. package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
  194. package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
  195. package/dist/features/skills/react/components/SkillsTab.js +24 -14
  196. package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
  197. package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
  198. package/dist/features/skills/rules.js +32 -23
  199. package/dist/features/source-config-list/agent-handles.js +2 -1
  200. package/dist/features/source-config-list/dependencies.js +53 -49
  201. package/dist/features/source-config-list/index.d.ts +3 -3
  202. package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
  203. package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
  204. package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
  205. package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
  206. package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
  207. package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
  208. package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
  209. package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
  210. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
  211. package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
  212. package/dist/features/source-config-list/types.d.ts +14 -0
  213. package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
  214. package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
  215. package/dist/features/tab-strip/react/components/TabBar.js +8 -1
  216. package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
  217. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
  218. package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
  219. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
  220. package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
  221. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
  222. package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
  223. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
  224. package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
  225. package/dist/features/version-manager/dependencies.d.ts +3 -1
  226. package/dist/features/version-manager/dependencies.js +3 -2
  227. package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
  228. package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
  229. package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
  230. package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
  231. package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
  232. package/dist/features/viewer-shell/rules.js +19 -23
  233. package/dist/react/components/CustomSelect.d.ts +38 -1
  234. package/dist/react/components/CustomSelect.js +94 -38
  235. package/dist/react/components/OnboardingDropdown.js +20 -8
  236. package/dist/react/components/RemixIcon.stylesheet.js +3 -1
  237. package/dist/react/components/Toast.js +13 -2
  238. package/dist/react/components/TooltipLayer.js +5 -14
  239. package/dist/react/components/WorkingDirPicker.js +78 -50
  240. package/dist/react/components/remixicon-font-recovery.js +38 -30
  241. package/dist/react/components/select-popover.d.ts +8 -6
  242. package/dist/react/components/select-popover.js +33 -29
  243. package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
  244. package/dist/react/mcp-ui/McpUiHost.js +10 -11
  245. package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
  246. package/dist/react/mcp-ui/host-message-source.js +15 -5
  247. package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
  248. package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
  249. package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
  250. package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
  251. package/dist/renderers/preview-modal-shell/react/components/icons.js +4 -1
  252. package/dist/renderers/react/components/ArtifactView.js +9 -6
  253. package/dist/renderers/srcdoc/build.d.ts +4 -1
  254. package/dist/renderers/srcdoc/build.js +62 -48
  255. package/dist/styles/admin.css +29 -14
  256. package/dist/styles/lightbox.css +142 -0
  257. package/dist/styles/variables.css +128 -0
  258. package/dist/theme/apply.js +9 -5
  259. package/dist/utils/auto-open-file.d.ts +2 -1
  260. package/dist/utils/auto-open-file.js +69 -85
  261. package/dist/utils/markdown-scroll-sync.js +108 -87
  262. package/dist/utils/polygon-selection.js +25 -26
  263. package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
  264. package/dist/utils/smooth-scroll-to-top.js +2 -32
  265. package/dist/utils/sse.js +26 -26
  266. package/dist/utils/unit-bezier.d.ts +7 -0
  267. package/dist/utils/unit-bezier.js +38 -0
  268. package/package.json +7 -10
  269. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
  270. package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
@@ -22,42 +22,8 @@ export function useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, o
22
22
  explicitCustomMode,
23
23
  onReasoningChange: (next) => onReasoningChange(agent.id, next),
24
24
  });
25
- // `modelsSource` is optional in the contract, so an absent value means the
26
- // host did not say where the list came from — NOT that it came from a
27
- // built-in fallback. Defaulting to 'fallback' rendered "Built-in list" over
28
- // a list that may well be live, and told the operator to Rescan to fix a
29
- // problem they do not have. Unknown provenance shows no badge and no claim.
30
- const source = agent.modelsSource;
31
- const sourceLabel = source === 'live' ? t('Live from CLI') : source === 'fallback' ? t('Built-in list') : '';
32
- const sourceHint = source === 'live'
33
- ? t("Model list comes from this CLI. Default uses the CLI's own config.")
34
- : source === 'fallback'
35
- ? t('Showing built-in defaults. Click Rescan to pull live models from the CLI.')
36
- : '';
37
- // The test result belongs to whichever agent was probed, so a stale verdict
38
- // from a previously-tested CLI never renders under this one.
39
- const testing = agentTest.status === 'testing' && agentTest.agentId === agent.id;
40
- const result = (agentTest.status === 'ok' || agentTest.status === 'error') && agentTest.agentId === agent.id
41
- ? agentTest
42
- : null;
43
- // The "use the binary that actually ran" / "clear the override" repair
44
- // affordance — only meaningful when a successful test silently fell back
45
- // off a configured override, AND this agent's catalog names which field
46
- // that override lives in. See `agentExecutableRepairState`'s doc.
47
- //
48
- // `AgentTestState`'s 'ok' variant spells success as `status: 'ok'`, not an
49
- // `ok: true` field — `agentExecutableRepairState` takes the latter (it
50
- // mirrors `ExecutionPort.testAgent`'s raw resolved shape, one layer below
51
- // this state union), so the two must be translated explicitly here rather
52
- // than passing `result` straight through.
53
- const repair = result?.status === 'ok'
54
- ? agentExecutableRepairState({
55
- ok: true,
56
- usedExecutableSource: result.usedExecutableSource,
57
- detectedExecutablePath: result.detectedExecutablePath,
58
- })
59
- : null;
60
- const repairField = repair ? binPathEnvField(cliEnvFields, agent.id) : null;
25
+ const { source, sourceLabel, sourceHint } = modelSourcePresentation({ source: agent.modelsSource, t });
26
+ const { testing, result, repair, repairField } = agentTestPresentation({ agent, agentTest, cliEnvFields });
61
27
  const openUrl = (url) => {
62
28
  if (!url)
63
29
  return;
@@ -107,4 +73,56 @@ export function useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, o
107
73
  selectReasoning,
108
74
  };
109
75
  }
76
+ /** Model-list provenance claims, translated only when the host supplied a source. */
77
+ function modelSourcePresentation({ source, t }, _options = {}) {
78
+ // `modelsSource` is optional in the contract, so an absent value means the
79
+ // host did not say where the list came from — NOT that it came from a
80
+ // built-in fallback. Defaulting to 'fallback' rendered "Built-in list" over
81
+ // a list that may well be live, and told the operator to Rescan to fix a
82
+ // problem they do not have. Unknown provenance shows no badge and no claim.
83
+ switch (source) {
84
+ case 'live':
85
+ return {
86
+ source,
87
+ sourceLabel: t('Live from CLI'),
88
+ sourceHint: t("Model list comes from this CLI. Default uses the CLI's own config."),
89
+ };
90
+ case 'fallback':
91
+ return {
92
+ source,
93
+ sourceLabel: t('Built-in list'),
94
+ sourceHint: t('Showing built-in defaults. Click Rescan to pull live models from the CLI.'),
95
+ };
96
+ default:
97
+ return { source, sourceLabel: '', sourceHint: '' };
98
+ }
99
+ }
100
+ /** Scope probe evidence and its executable repair affordance to the card being rendered. */
101
+ function agentTestPresentation({ agent, agentTest, cliEnvFields }, _options = {}) {
102
+ // The test result belongs to whichever agent was probed, so a stale verdict
103
+ // from a previously-tested CLI never renders under this one.
104
+ const testing = agentTest.status === 'testing' && agentTest.agentId === agent.id;
105
+ const result = (agentTest.status === 'ok' || agentTest.status === 'error') && agentTest.agentId === agent.id
106
+ ? agentTest
107
+ : null;
108
+ // The "use the binary that actually ran" / "clear the override" repair
109
+ // affordance — only meaningful when a successful test silently fell back
110
+ // off a configured override, AND this agent's catalog names which field
111
+ // that override lives in. See `agentExecutableRepairState`'s doc.
112
+ //
113
+ // `AgentTestState`'s 'ok' variant spells success as `status: 'ok'`, not an
114
+ // `ok: true` field — `agentExecutableRepairState` takes the latter (it
115
+ // mirrors `ExecutionPort.testAgent`'s raw resolved shape, one layer below
116
+ // this state union), so the two must be translated explicitly here rather
117
+ // than passing `result` straight through.
118
+ const repair = result?.status === 'ok'
119
+ ? agentExecutableRepairState({
120
+ ok: true,
121
+ usedExecutableSource: result.usedExecutableSource,
122
+ detectedExecutablePath: result.detectedExecutablePath,
123
+ })
124
+ : null;
125
+ const repairField = repair ? binPathEnvField(cliEnvFields, agent.id) : null;
126
+ return { testing, result, repair, repairField };
127
+ }
110
128
  //# sourceMappingURL=useLocalCliAgentCard.js.map
@@ -54,7 +54,7 @@ export interface UseReasoningControlResult {
54
54
  knownModelIds: readonly string[];
55
55
  /** Whether the custom model input is currently active. */
56
56
  customActive: boolean;
57
- /** Options for the model select: base models for suffix agents, or raw models list. */
57
+ /** Options for the model select: base models for suffix agents, or deduped catalog rows. */
58
58
  modelChoices: readonly AgentModelOption[];
59
59
  /** Selected value for the model select element. */
60
60
  selectValue: string;
@@ -1,102 +1,37 @@
1
1
  import { useCallback, useEffect } from 'react';
2
2
  import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
3
- import { modelIdForReasoningLevel, reasoningModelGroupFor, reasoningModelGroups, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, splitReasoningModelId, } from '../../rules.js';
3
+ import { modelIdForReasoningLevel, pickerModelOptions, reasoningModelGroupFor, reasoningModelGroups, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, splitReasoningModelId, } from '../../rules.js';
4
4
  export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCustomMode) {
5
- const { agent, config, explicitCustomMode = false, onReasoningChange } = 'agent' in optionsOrAgent && 'config' in optionsOrAgent
6
- ? optionsOrAgent
7
- : {
8
- agent: optionsOrAgent,
9
- config: maybeConfig,
10
- explicitCustomMode: maybeExplicitCustomMode ?? false,
11
- };
5
+ const { agent, config, explicitCustomMode = false, onReasoningChange } = reasoningControlOptions(optionsOrAgent, maybeConfig, maybeExplicitCustomMode);
12
6
  const models = agent.models ?? [];
13
7
  const hasModels = models.length > 0;
14
8
  const resolvedModel = selectedAgentModel(config, agent);
15
9
  const rawModel = config.modelByAgentId?.[agent.id] ?? '';
16
- // Two ways an agent can expose a reasoning-effort axis, and the card branches on the DECLARATION
17
- // rather than on the agent's id — adding a third runtime with either shape is a def change, not
18
- // an edit here.
19
- //
20
- // `reasoningOptions` — one flat vocabulary that travels to the CLI as its own flag
21
- // (`claude --effort high`, codex's `-c model_reasoning_effort=...`).
22
- // The choice is its own persisted value; `onReasoningChange` owns it.
23
- // `reasoningInModelId` — no effort flag exists; the level is a suffix on the model id itself
24
- // (antigravity's `gemini-3.1-pro-high`). The choice IS the model, so
25
- // it goes through `onModelChange`, and the available levels are
26
- // derived PER BASE MODEL from the agent's own catalog — they are not
27
- // uniform (`gemini-3.1-pro` has no `-medium`, and `agy --model`
28
- // rejects one), so a fixed dropdown would offer ids the CLI refuses.
29
- const suffixLevels = agent.reasoningInModelId?.levels ?? [];
30
- const suffixLevelIds = suffixLevels.map((level) => level.id);
31
- const modelGroups = suffixLevels.length > 0 ? reasoningModelGroups(models, suffixLevels) : null;
32
- const activeGroup = modelGroups ? reasoningModelGroupFor(modelGroups, resolvedModel, suffixLevelIds) : null;
33
- const activeLevel = modelGroups ? splitReasoningModelId(resolvedModel, suffixLevelIds).level : null;
10
+ const { suffixLevels, suffixLevelIds, modelGroups, activeGroup, activeLevel } = suffixReasoning({ agent, models, resolvedModel });
34
11
  const flatReasoningOptions = agent.reasoningOptions ?? [];
35
- // Per-model narrowing: a runtime whose catalog reports reasoning levels PER MODEL rather than
36
- // uniformly across the agent (Codex — see `AgentModelOption.reasoning`'s doc) declares them on the
37
- // resolved model's own catalog row. `undefined` there means "this model's entry has nothing
38
- // model-specific to say", so it falls back to the agent-wide union below — the same list every
39
- // model rendered before this field existed. An explicit `[]` is honored AS-IS, even though a model
40
- // with genuinely zero levels then hides the control: falling back to the union for `[]` too would
41
- // just move today's "every model sees every level" bug one layer down instead of fixing it.
42
- const selectedModelReasoning = models.find((model) => model.id === resolvedModel)?.reasoning;
43
- // Exactly one effort control, whichever shape declared it. `reasoningOptions` wins if a def ever
44
- // declares both, rather than rendering two controls that disagree about where the choice lands.
45
- const reasoningOptions = flatReasoningOptions.length === 0
46
- ? (activeGroup?.levels ?? [])
47
- : selectedModelReasoning === undefined
48
- ? flatReasoningOptions
49
- : selectedModelReasoning;
12
+ const reasoningOptions = effortOptions({ flatReasoningOptions, activeGroup, models, resolvedModel });
50
13
  const hasReasoning = reasoningOptions.length > 0;
51
- const rawReasoningValue = flatReasoningOptions.length > 0
52
- ? selectedAgentReasoning(config, agent)
53
- : (activeLevel ?? reasoningOptions[0]?.id ?? '');
54
- // The persisted (or agent-wide-default) pick can name a level the narrowed list above does not
55
- // actually offer for the CURRENT model — that is the whole point of narrowing. Rendering it anyway
56
- // would just relocate today's bug: the `<select>` below has no matching `<option>`, so it would
57
- // silently show whatever the browser highlights first while the underlying value stays wrong. This
58
- // guards what actually renders; the effect further down clears the persisted copy so every other
59
- // reader of `config.reasoningByAgentId` (not just this hook) sees the correction too.
60
- const reasoningSupported = reasoningOptions.some((option) => option.id === rawReasoningValue);
61
- const reasoningValue = reasoningSupported ? rawReasoningValue : (reasoningOptions[0]?.id ?? '');
14
+ const reasoningValue = effortValue({ agent, config, flatReasoningOptions, activeLevel, reasoningOptions });
62
15
  // A base model with exactly one variant has a real, fixed level worth showing — but nothing to
63
16
  // choose between, so the control is shown disabled rather than offering an illusion of choice.
64
17
  const reasoningDisabled = flatReasoningOptions.length === 0 && reasoningOptions.length < 2;
65
18
  const reasoningOptionIdsKey = reasoningOptions.map((option) => option.id).join('|');
66
19
  useEffect(() => {
67
- // The reasoningInModelId path has no reasoning value of its own to clear — the level lives
68
- // inside the model id itself, and picking a new base model already replaces it.
69
- if (flatReasoningOptions.length === 0)
70
- return;
71
20
  if (!onReasoningChange)
72
21
  return;
73
- const persisted = config.reasoningByAgentId?.[agent.id]?.trim();
74
- if (!persisted)
75
- return;
76
- if (reasoningOptions.some((option) => option.id === persisted))
77
- return;
78
- onReasoningChange('');
22
+ if (isStaleReasoningPick({ agent, config, flatReasoningOptions, reasoningOptions }))
23
+ onReasoningChange('');
79
24
  // `reasoningOptionIdsKey` stands in for `reasoningOptions` (a fresh array every render) so this
80
25
  // only re-runs when the narrowed vocabulary actually changes, not on every unrelated re-render.
81
26
  // eslint-disable-next-line react-hooks/exhaustive-deps
82
27
  }, [flatReasoningOptions.length, onReasoningChange, config.reasoningByAgentId, agent.id, reasoningOptionIdsKey]);
83
- // Adapters opt out via `supportsCustomModel: false` when their CLI has no
84
- // free-text model flag, or validates the id against a live catalog and
85
- // rejects unknown ones. `undefined` allows it, matching every adapter's
86
- // default before this field existed.
87
- const allowCustomModel = agent.supportsCustomModel !== false && modelGroups === null;
88
- const knownModelIds = models.map((model) => model.id);
89
- const customActive = allowCustomModel && hasModels && shouldShowCustomModelInput(resolvedModel, knownModelIds, explicitCustomMode);
28
+ const { allowCustomModel, knownModelIds, customActive } = customModelState({ agent, modelGroups, models, resolvedModel, explicitCustomMode });
90
29
  // A model-suffix agent picks a BASE model here; the effort control below supplies the rest of the
91
30
  // id. Everything else about the field — search, labels, provenance badge — is unchanged.
92
31
  const modelChoices = modelGroups
93
32
  ? modelGroups.map((group) => ({ id: group.baseId, label: group.label }))
94
- : models;
95
- const selectValue = customActive
96
- ? CUSTOM_MODEL_SENTINEL
97
- : modelGroups
98
- ? (activeGroup?.baseId ?? '')
99
- : resolvedModel;
33
+ : pickerModelOptions({ models });
34
+ const selectValue = modelSelectValue({ customActive, modelGroups, activeGroup, models, resolvedModel });
100
35
  // While the custom box is open, the text field must show the operator's raw
101
36
  // typed text (which may not resolve to anything yet) rather than the
102
37
  // resolved-with-fallback value — otherwise every keystroke would show a
@@ -146,4 +81,110 @@ export function useReasoningControl(optionsOrAgent, maybeConfig, maybeExplicitCu
146
81
  resolveReasoningModelId,
147
82
  };
148
83
  }
84
+ /** The two call shapes (options object, or the older positional `agent, config, explicitCustomMode`)
85
+ * as one options object. */
86
+ function reasoningControlOptions(optionsOrAgent, maybeConfig, maybeExplicitCustomMode) {
87
+ if ('agent' in optionsOrAgent && 'config' in optionsOrAgent)
88
+ return optionsOrAgent;
89
+ return {
90
+ agent: optionsOrAgent,
91
+ config: maybeConfig,
92
+ explicitCustomMode: maybeExplicitCustomMode ?? false,
93
+ };
94
+ }
95
+ /**
96
+ * The model-suffix reasoning shape: the agent's declared suffix levels, its catalog grouped by base
97
+ * model, and where the resolved model sits in those groups. All `null` (and empty) for an agent
98
+ * without `reasoningInModelId`.
99
+ *
100
+ * Two ways an agent can expose a reasoning-effort axis, and the card branches on the DECLARATION
101
+ * rather than on the agent's id — adding a third runtime with either shape is a def change, not
102
+ * an edit here.
103
+ *
104
+ * `reasoningOptions` — one flat vocabulary that travels to the CLI as its own flag
105
+ * (`claude --effort high`, codex's `-c model_reasoning_effort=...`).
106
+ * The choice is its own persisted value; `onReasoningChange` owns it.
107
+ * `reasoningInModelId` — no effort flag exists; the level is a suffix on the model id itself
108
+ * (antigravity's `gemini-3.1-pro-high`). The choice IS the model, so
109
+ * it goes through `onModelChange`, and the available levels are
110
+ * derived PER BASE MODEL from the agent's own catalog — they are not
111
+ * uniform (`gemini-3.1-pro` has no `-medium`, and `agy --model`
112
+ * rejects one), so a fixed dropdown would offer ids the CLI refuses.
113
+ */
114
+ function suffixReasoning({ agent, models, resolvedModel }) {
115
+ const suffixLevels = agent.reasoningInModelId?.levels ?? [];
116
+ const suffixLevelIds = suffixLevels.map((level) => level.id);
117
+ if (suffixLevels.length === 0) {
118
+ return { suffixLevels, suffixLevelIds, modelGroups: null, activeGroup: null, activeLevel: null };
119
+ }
120
+ const modelGroups = reasoningModelGroups(models, suffixLevels);
121
+ const activeGroup = reasoningModelGroupFor(modelGroups, resolvedModel, suffixLevelIds);
122
+ const activeLevel = splitReasoningModelId(resolvedModel, suffixLevelIds).level;
123
+ return { suffixLevels, suffixLevelIds, modelGroups, activeGroup, activeLevel };
124
+ }
125
+ /** The effort options the reasoning dropdown offers for the resolved model. */
126
+ function effortOptions({ flatReasoningOptions, activeGroup, models, resolvedModel }) {
127
+ // Exactly one effort control, whichever shape declared it. `reasoningOptions` wins if a def ever
128
+ // declares both, rather than rendering two controls that disagree about where the choice lands.
129
+ if (flatReasoningOptions.length === 0)
130
+ return activeGroup?.levels ?? [];
131
+ // Per-model narrowing: a runtime whose catalog reports reasoning levels PER MODEL rather than
132
+ // uniformly across the agent (Codex — see `AgentModelOption.reasoning`'s doc) declares them on the
133
+ // resolved model's own catalog row. `undefined` there means "this model's entry has nothing
134
+ // model-specific to say", so it falls back to the agent-wide union below — the same list every
135
+ // model rendered before this field existed. An explicit `[]` is honored AS-IS, even though a model
136
+ // with genuinely zero levels then hides the control: falling back to the union for `[]` too would
137
+ // just move today's "every model sees every level" bug one layer down instead of fixing it.
138
+ const selectedModelReasoning = models.find((model) => model.id === resolvedModel)?.reasoning;
139
+ return selectedModelReasoning === undefined ? flatReasoningOptions : selectedModelReasoning;
140
+ }
141
+ /** The value the reasoning select renders: the persisted / derived pick, or the first offered level
142
+ * when that pick is not among the options. */
143
+ function effortValue({ agent, config, flatReasoningOptions, activeLevel, reasoningOptions }) {
144
+ const rawReasoningValue = flatReasoningOptions.length > 0
145
+ ? selectedAgentReasoning(config, agent)
146
+ : (activeLevel ?? reasoningOptions[0]?.id ?? '');
147
+ // The persisted (or agent-wide-default) pick can name a level the narrowed list above does not
148
+ // actually offer for the CURRENT model — that is the whole point of narrowing. Rendering it anyway
149
+ // would just relocate today's bug: the `<select>` below has no matching `<option>`, so it would
150
+ // silently show whatever the browser highlights first while the underlying value stays wrong. This
151
+ // guards what actually renders; the effect further down clears the persisted copy so every other
152
+ // reader of `config.reasoningByAgentId` (not just this hook) sees the correction too.
153
+ const reasoningSupported = reasoningOptions.some((option) => option.id === rawReasoningValue);
154
+ return reasoningSupported ? rawReasoningValue : (reasoningOptions[0]?.id ?? '');
155
+ }
156
+ /** Whether the agent's persisted flat-reasoning pick names a level the current options no longer
157
+ * offer, so the hook should ask the host to clear it. */
158
+ function isStaleReasoningPick({ agent, config, flatReasoningOptions, reasoningOptions }) {
159
+ // The reasoningInModelId path has no reasoning value of its own to clear — the level lives
160
+ // inside the model id itself, and picking a new base model already replaces it.
161
+ if (flatReasoningOptions.length === 0)
162
+ return false;
163
+ const persisted = config.reasoningByAgentId?.[agent.id]?.trim();
164
+ if (!persisted)
165
+ return false;
166
+ return !reasoningOptions.some((option) => option.id === persisted);
167
+ }
168
+ /** The model select's value: the Custom sentinel, a suffix agent's base id, or the resolved model. */
169
+ function modelSelectValue({ customActive, modelGroups, activeGroup, models, resolvedModel }) {
170
+ if (customActive)
171
+ return CUSTOM_MODEL_SENTINEL;
172
+ if (modelGroups)
173
+ return activeGroup?.baseId ?? '';
174
+ // Persisted aliases stay known in the full catalog above, but the picker selects their concrete
175
+ // row. This is display-only: the saved model and dispatch resolution keep the operator's pick.
176
+ const selectedModel = models.find((model) => model.id === resolvedModel);
177
+ return selectedModel?.identityKind === 'alias' ? (selectedModel.resolvedId || resolvedModel) : resolvedModel;
178
+ }
179
+ /** Whether this agent takes a free-text model id at all, and whether the free-text box is open now. */
180
+ function customModelState({ agent, modelGroups, models, resolvedModel, explicitCustomMode }) {
181
+ // Adapters opt out via `supportsCustomModel: false` when their CLI has no
182
+ // free-text model flag, or validates the id against a live catalog and
183
+ // rejects unknown ones. `undefined` allows it, matching every adapter's
184
+ // default before this field existed.
185
+ const allowCustomModel = agent.supportsCustomModel !== false && modelGroups === null;
186
+ const knownModelIds = models.map((model) => model.id);
187
+ const customActive = allowCustomModel && models.length > 0 && shouldShowCustomModelInput(resolvedModel, knownModelIds, explicitCustomMode);
188
+ return { allowCustomModel, knownModelIds, customActive };
189
+ }
149
190
  //# sourceMappingURL=useReasoningControl.js.map
@@ -109,8 +109,8 @@ export declare function isBaseUrlInvalid(config: ByokConfig): boolean;
109
109
  */
110
110
  export declare function nextConfigForPresetSelect(config: ByokConfig, preset: ProviderPreset): ByokConfig;
111
111
  /** Switching protocol by hand drops the preset selection (the old preset no
112
- * longer speaks this protocol) and adopts the protocol's default endpoint
113
- * only when the operator hasn't typed their own. */
112
+ * longer speaks this protocol) and keeps operator-entered endpoints. Hosts
113
+ * seed endpoints through presets; protocol selection never chooses a vendor. */
114
114
  export declare function nextConfigForProtocolSelect(config: ByokConfig, protocol: ByokConfig['protocol']): ByokConfig;
115
115
  /** Mode changes never discard BYOK credentials — flipping to Local CLI and
116
116
  * back must not make the operator retype their key. */
@@ -243,6 +243,17 @@ export declare function agentExecutableRepairState(result: {
243
243
  usedExecutableSource?: AgentExecutableSource | undefined;
244
244
  detectedExecutablePath?: string | undefined;
245
245
  }): AgentExecutableRepair | null;
246
+ /**
247
+ * Builds picker rows from catalog evidence without changing the source catalog. Resolved aliases
248
+ * share a concrete row; unresolved aliases and routing modes remain selectable as reported.
249
+ * Keeps the first position and prefers a human label over a raw ID, including the CLI default's
250
+ * raw-label row. No names or context variants are inferred from model IDs.
251
+ * @returns Distinct picker rows in catalog order. No side effects or errors.
252
+ * @complexity O(n) time and space in catalog rows, using insertion-ordered Map keys.
253
+ */
254
+ export declare function pickerModelOptions({ models }: {
255
+ models: readonly AgentModelOption[];
256
+ }, _optional?: Record<string, never>): AgentModelOption[];
246
257
  /**
247
258
  * Filters a searchable model list down to what matches the operator's typed
248
259
  * query. The currently-selected value's own option is always kept even when
@@ -1,5 +1,5 @@
1
1
  import { isAllowedEndpointUrl } from '../../utils/endpoint-policy.js';
2
- import { API_KEY_CROSS_VENDOR_WARNING, API_KEY_TOO_SHORT_WARNING, CUSTOM_PRESET_ID, DEFAULT_BASE_URL_BY_PROTOCOL, MIN_PLAUSIBLE_API_KEY_LENGTH, } from './constants.js';
2
+ import { API_KEY_CROSS_VENDOR_WARNING, API_KEY_TOO_SHORT_WARNING, CUSTOM_PRESET_ID, MIN_PLAUSIBLE_API_KEY_LENGTH, } from './constants.js';
3
3
  /**
4
4
  * Pure decision logic for the execution tab. Everything here is total and
5
5
  * side-effect free so the React layer stays a thin renderer — same split the
@@ -231,21 +231,26 @@ export function nextConfigForPresetSelect(config, preset) {
231
231
  if (preset.custom) {
232
232
  return { ...config, providerId: null, savedByProviderId };
233
233
  }
234
- const saved = savedByProviderId[preset.id];
235
234
  return {
236
235
  ...config,
237
236
  providerId: preset.id,
238
237
  protocol: preset.protocol,
238
+ ...presetCredentials(savedByProviderId[preset.id], preset),
239
+ savedByProviderId,
240
+ };
241
+ }
242
+ /** A preset's saved draft, or blank credentials seeded with its base URL and top preferred model. */
243
+ function presetCredentials(saved, preset) {
244
+ return {
239
245
  apiKey: saved?.apiKey ?? '',
240
- baseUrl: saved?.baseUrl ?? (preset.baseUrl || DEFAULT_BASE_URL_BY_PROTOCOL[preset.protocol]),
246
+ baseUrl: saved?.baseUrl ?? preset.baseUrl,
241
247
  model: saved?.model ?? preset.preferredModels[0] ?? '',
242
248
  maxTokens: saved?.maxTokens,
243
- savedByProviderId,
244
249
  };
245
250
  }
246
251
  /** Switching protocol by hand drops the preset selection (the old preset no
247
- * longer speaks this protocol) and adopts the protocol's default endpoint
248
- * only when the operator hasn't typed their own. */
252
+ * longer speaks this protocol) and keeps operator-entered endpoints. Hosts
253
+ * seed endpoints through presets; protocol selection never chooses a vendor. */
249
254
  export function nextConfigForProtocolSelect(config, protocol) {
250
255
  if (config.protocol === protocol)
251
256
  return config;
@@ -253,7 +258,7 @@ export function nextConfigForProtocolSelect(config, protocol) {
253
258
  ...config,
254
259
  protocol,
255
260
  providerId: null,
256
- baseUrl: config.baseUrl.trim() ? config.baseUrl : DEFAULT_BASE_URL_BY_PROTOCOL[protocol],
261
+ baseUrl: config.baseUrl.trim() ? config.baseUrl : '',
257
262
  };
258
263
  }
259
264
  /** Mode changes never discard BYOK credentials — flipping to Local CLI and
@@ -476,6 +481,37 @@ export function agentExecutableRepairState(result) {
476
481
  return null;
477
482
  return { detectedPath, canUseDetected: true };
478
483
  }
484
+ /** Resolves alias evidence to a concrete picker row; other rows retain their identity. */
485
+ function resolvedPickerModelOption({ model }, _optional = {}) {
486
+ const resolvedId = model.identityKind === 'alias' ? model.resolvedId : undefined;
487
+ if (!resolvedId)
488
+ return model;
489
+ const label = model.label === model.id ? resolvedId : model.label;
490
+ return { ...model, id: resolvedId, label, identityKind: 'concrete' };
491
+ }
492
+ /**
493
+ * Builds picker rows from catalog evidence without changing the source catalog. Resolved aliases
494
+ * share a concrete row; unresolved aliases and routing modes remain selectable as reported.
495
+ * Keeps the first position and prefers a human label over a raw ID, including the CLI default's
496
+ * raw-label row. No names or context variants are inferred from model IDs.
497
+ * @returns Distinct picker rows in catalog order. No side effects or errors.
498
+ * @complexity O(n) time and space in catalog rows, using insertion-ordered Map keys.
499
+ */
500
+ export function pickerModelOptions({ models }, _optional = {}) {
501
+ const choices = new Map();
502
+ for (const model of models) {
503
+ const option = resolvedPickerModelOption({ model });
504
+ const { id, label } = option;
505
+ const existing = choices.get(id);
506
+ if (!existing) {
507
+ choices.set(id, option);
508
+ }
509
+ else if (existing.label === id && label !== id) {
510
+ choices.set(id, { ...existing, label });
511
+ }
512
+ }
513
+ return [...choices.values()];
514
+ }
479
515
  /** Case-insensitive substring match against a model's id or label, mirroring
480
516
  * the origin's search box. Matches on both fields (not just the visible
481
517
  * label) so an operator can search by the raw model id too. */
@@ -542,16 +578,20 @@ export function splitReasoningModelId(modelId, levelIds) {
542
578
  * has no level, so nothing is stripped, and a vendor label that happens to end in an unrelated
543
579
  * parenthetical keeps it.
544
580
  */
581
+ /** `Head (Inner)` with optional surrounding whitespace; group 1 is the head, group 2 the qualifier. */
582
+ const TRAILING_PARENTHETICAL = /^(.*?)\s*\(([^()]*)\)\s*$/;
545
583
  function baseLabelForLevel(label, level) {
546
584
  if (!level)
547
585
  return label;
548
- const match = /^(.*?)\s*\(([^()]*)\)\s*$/.exec(label);
549
- if (!match)
586
+ if (!TRAILING_PARENTHETICAL.test(label))
550
587
  return label;
551
- const inner = (match[2] ?? '').trim().toLowerCase();
588
+ // Both capture groups always participate once the pattern matches, so reading them through
589
+ // `replace` (which yields a plain string) replaces the old `match[n] ?? ''` fallbacks, which no
590
+ // matching label could ever reach.
591
+ const inner = label.replace(TRAILING_PARENTHETICAL, '$2').trim().toLowerCase();
552
592
  if (inner !== level.id.toLowerCase() && inner !== level.label.toLowerCase())
553
593
  return label;
554
- const stripped = (match[1] ?? '').trim();
594
+ const stripped = label.replace(TRAILING_PARENTHETICAL, '$1').trim();
555
595
  return stripped || label;
556
596
  }
557
597
  /**
@@ -1,3 +1,4 @@
1
+ import type { ProviderPreset as SharedProviderPreset } from '@jini-ai/core';
1
2
  /**
2
3
  * Origin: the inline `execution` tab body in `SettingsDialog.tsx`
3
4
  * (lines 4080-5336), classified MIXED by
@@ -26,48 +27,9 @@ export type ApiProtocol = 'anthropic' | 'openai' | 'azure' | 'google';
26
27
  /** Where a preset renders in the chip strip. The origin drew one row of
27
28
  * protocol presets and one of gateway presets; hosts that don't care can
28
29
  * leave this unset and get a single row. */
29
- export type ProviderPresetKind = 'protocol' | 'gateway';
30
- /** One selectable endpoint preset. A host supplies its own catalog — this
31
- * package ships `DEFAULT_PROVIDER_PRESETS` as a starting point, not as a
32
- * fixed list (same convention as `LanguageTab`'s host-supplied locales). */
33
- export interface ProviderPreset {
34
- id: string;
35
- title: string;
36
- protocol: ApiProtocol;
37
- baseUrl: string;
38
- /** Ranked suggestions shown in the model field. Never authoritative — a
39
- * host with live model discovery should pass `ExecutionPort.listModels`. */
40
- preferredModels: readonly string[];
41
- kind?: ProviderPresetKind;
42
- /** Fixed-origin gateways resolve their base URL themselves, so the Base URL
43
- * field is hidden rather than shown-and-ignored (origin: `isFixedOriginGateway`). */
44
- fixedOrigin?: boolean;
45
- /** Some local/self-hosted endpoints need no bearer credential. Defaults to
46
- * `true` — i.e. an API key is required unless a preset opts out. */
47
- requiresApiKey?: boolean;
48
- /** Optional "Get key" deep link rendered beside the API-key field. */
49
- apiKeyConsoleUrl?: string;
50
- /**
51
- * The literal prefix that positively identifies a pasted key as belonging to THIS provider —
52
- * `'AIza'` for Google, `'sk-ant-'` for Anthropic. Omit it for a vendor whose keys carry no
53
- * distinguishing prefix (Azure OpenAI's are bare hex) rather than inventing one.
54
- *
55
- * Read in exactly one direction: to recognise a key as SOMEONE ELSE'S. It is deliberately NOT an
56
- * allowlist, and no code may ask "does the selected preset's key match this?" as a validity test.
57
- * That is the shape this field replaced, and it shipped a false positive within hours of landing:
58
- * a real Google Gemini key in a newer shape than `/^AIza/` was reported to its owner as not
59
- * looking like a Google key. Vendors add key formats without telling anyone, so a catalog can
60
- * prove a string belongs to vendor X, but never that it belongs to nobody. See
61
- * `apiKeyFormatWarning`.
62
- *
63
- * Data rather than an `if (providerId === …)` chain in the rule, so adding a provider stays a row
64
- * in the preset catalog — including for a host that supplies its own `presets` and never touches
65
- * this package.
66
- */
67
- apiKeyPrefix?: string;
68
- /** The synthetic "Custom" preset. Exactly one may carry this flag; it is
69
- * never treated as configured-by-preset and never hides the Base URL. */
70
- custom?: boolean;
30
+ export type { ProviderPresetKind } from '@jini-ai/core';
31
+ /** One selectable endpoint preset supplied by the host. */
32
+ export interface ProviderPreset extends SharedProviderPreset<ApiProtocol> {
71
33
  }
72
34
  /**
73
35
  * One advisory remark about the API key currently typed into the form, ready to translate.
@@ -138,6 +100,8 @@ export interface ExecutionConfig {
138
100
  export interface AgentModelOption {
139
101
  id: string;
140
102
  label: string;
103
+ identityKind?: 'concrete' | 'alias' | 'routing-mode';
104
+ resolvedId?: string;
141
105
  /**
142
106
  * The reasoning-effort levels THIS model supports, when the runtime's own catalog reports them
143
107
  * per model rather than uniformly across the agent. Mirrors `@jini-ai/agent-runtime`'s
@@ -1,19 +1,2 @@
1
- /**
2
- * Origin: the inline `execution` tab body in `SettingsDialog.tsx`
3
- * (lines 4080-5336), classified MIXED by
4
- * `ADS-memory/reports/jini-port/recon/r6-god-component-internals.md` §1.3 — a generic
5
- * "Local CLI vs. bring-your-own-key" execution switch wrapped in
6
- * origin-specific managed-runtime/wallet chrome.
7
- *
8
- * What is ported: the mode switch, the provider/gateway chip groups, and the
9
- * BYOK credential form (the origin had already factored these fields into
10
- * `components/byok/*`, which is why this slice is assembly rather than
11
- * surgery).
12
- *
13
- * What is deliberately left behind: the origin's managed-runtime wallet
14
- * (balance polling, top-up, sign-in coachmarks, the `SettingsHighlight`
15
- * one-shot focus hint) and its analytics tracking calls — all product-bound,
16
- * none of it generic.
17
- */
18
1
  export {};
19
2
  //# sourceMappingURL=types.js.map
@@ -1,5 +1,5 @@
1
1
  import type { SourceConfigDependencies } from '../../../source-config-list/ports.js';
2
- import type { SourceConfigItem, SourceFieldSpec } from '../../../source-config-list/types.js';
2
+ import type { SourceConfigItem, SourceFieldSpec, SourceTrustOption } from '../../../source-config-list/types.js';
3
3
  export interface ExternalMcpTabProps {
4
4
  /** Host-supplied transport adapter — required, same as the generic primitive (no zero-config default is possible for an arbitrary `addSource`). */
5
5
  dependencies: SourceConfigDependencies<SourceConfigItem>;
@@ -11,6 +11,10 @@ export interface ExternalMcpTabProps {
11
11
  saveStatusLabel?: string;
12
12
  /** Footer "Stored at <path>" text. Shown only when `saveStatusLabel` is also given, matching the footer being one unit in the OD reference. */
13
13
  configPath?: string;
14
+ /** The trust levels each server card offers. The card renders its trust picker only when this is
15
+ * non-empty AND the port implements `setTrust`; without it the tab's `setTrust` wiring had no
16
+ * control that could ever reach it. Omit for no trust picker (the previous, and still default, look). */
17
+ trustOptions?: readonly SourceTrustOption[];
14
18
  }
15
19
  /**
16
20
  * OD as an MCP *client* — distinct from the "MCP server" tab, where the host
@@ -19,5 +23,5 @@ export interface ExternalMcpTabProps {
19
23
  * composition only: every list/add mutation is the two source-config-list
20
24
  * hooks' own, already-tested logic.
21
25
  */
22
- export declare function ExternalMcpTab({ dependencies, fieldSpecs, connectionError, saveStatusLabel, configPath, }: ExternalMcpTabProps): import("react").JSX.Element;
26
+ export declare function ExternalMcpTab({ dependencies, fieldSpecs, connectionError, saveStatusLabel, configPath, trustOptions, }: ExternalMcpTabProps): import("react").JSX.Element;
23
27
  //# sourceMappingURL=ExternalMcpTab.d.ts.map
@@ -33,7 +33,7 @@ import { MCP_SOURCE_FIELD_SPECS } from '../../constants.js';
33
33
  * composition only: every list/add mutation is the two source-config-list
34
34
  * hooks' own, already-tested logic.
35
35
  */
36
- export function ExternalMcpTab({ dependencies, fieldSpecs = MCP_SOURCE_FIELD_SPECS, connectionError = null, saveStatusLabel, configPath, }) {
36
+ export function ExternalMcpTab({ dependencies, fieldSpecs = MCP_SOURCE_FIELD_SPECS, connectionError = null, saveStatusLabel, configPath, trustOptions, }) {
37
37
  const t = useT();
38
38
  const [formOpen, setFormOpen] = useState(false);
39
39
  const list = useWiredSourceConfigList({ dependencies });
@@ -48,6 +48,13 @@ export function ExternalMcpTab({ dependencies, fieldSpecs = MCP_SOURCE_FIELD_SPE
48
48
  // A real load failure always wins over the host's static/decorative banner
49
49
  // text — showing both would just be two banners saying overlapping things.
50
50
  const banner = list.error ?? connectionError;
51
- return (_jsxs("section", { className: "external-mcp-tab", children: [_jsxs("div", { className: "external-mcp-head", children: [_jsxs("div", { children: [_jsx("h3", { children: t('External MCP servers') }), _jsx("p", { className: "external-mcp-subtitle", children: t('Third-party tools for your coding agent.') })] }), _jsxs("button", { type: "button", className: "external-mcp-add-button", onClick: () => setFormOpen((open) => !open), "aria-expanded": formOpen, children: [_jsx(Icon, { name: "plus", size: 14 }), _jsx("span", { children: t('Add server') })] })] }), banner ? (_jsx("div", { className: "external-mcp-banner", role: "alert", children: t(banner) })) : null, formOpen ? (_jsx(SourceConfigAddForm, { fieldSpecs: fieldSpecs, values: addForm.values, validation: addForm.validation, submitAttempted: addForm.submitAttempted, submitting: addForm.submitting, ...(addForm.submitError ? { submitError: addForm.submitError } : {}), onFieldChange: addForm.setField, onTrustChange: addForm.setTrust, onSubmit: () => void addForm.submit(), canTest: list.capabilities.canTest, testing: list.isPending(DRAFT_TEST_SCOPE, 'test'), ...(list.testResults[DRAFT_TEST_SCOPE] ? { testResult: list.testResults[DRAFT_TEST_SCOPE] } : {}), onTest: () => void list.test(undefined, addForm.values), addLabel: "Add server" })) : null, list.loading ? (_jsx("div", { className: "source-config-list-loading", role: "status", children: t('Loading…') })) : list.sources.length === 0 ? (_jsxs("div", { className: "external-mcp-empty", children: [_jsx("p", { className: "external-mcp-empty-title", children: t('No MCP servers configured.') }), _jsx("p", { className: "external-mcp-empty-hint", children: t('Click "Add server" to get started — pick a template or set up a custom stdio / HTTP server.') })] })) : (_jsx("div", { className: "source-config-list-items", children: list.sources.map((source) => (_jsx(SourceConfigItemCard, { source: source, fieldSpecs: fieldSpecs, capabilities: list.capabilities, removing: list.isPending(source.id, 'remove'), refreshing: list.isPending(source.id, 'refresh'), settingTrust: list.isPending(source.id, 'trust'), testing: list.isPending(source.id, 'test'), updating: list.isPending(source.id, 'update'), onRefresh: () => void list.refresh(source.id), onRemove: () => void list.remove(source.id), onTrustChange: (trust) => void list.setTrust(source.id, trust), onTest: () => void list.test(source.id), onUpdate: (patch) => void list.update(source.id, patch), ...(list.testResults[source.id] ? { testResult: list.testResults[source.id] } : {}) }, source.id))) })), saveStatusLabel ? (_jsxs("div", { className: "external-mcp-footer", children: [_jsx("span", { className: "external-mcp-save-pill", children: t(saveStatusLabel) }), configPath ? (_jsxs("span", { className: "external-mcp-stored-at", children: [t('Stored at'), " ", _jsx("code", { children: configPath })] })) : null] })) : null] }));
51
+ return (_jsxs("section", { className: "external-mcp-tab", children: [_jsxs("div", { className: "external-mcp-head", children: [_jsxs("div", { children: [_jsx("h3", { children: t('External MCP servers') }), _jsx("p", { className: "external-mcp-subtitle", children: t('Third-party tools for your coding agent.') })] }), _jsxs("button", { type: "button", className: "external-mcp-add-button", onClick: () => setFormOpen((open) => !open), "aria-expanded": formOpen, children: [_jsx(Icon, { name: "plus", size: 14 }), _jsx("span", { children: t('Add server') })] })] }), banner ? (_jsx("div", { className: "external-mcp-banner", role: "alert", children: t(banner) })) : null, formOpen ? (_jsx(SourceConfigAddForm, { fieldSpecs: fieldSpecs, values: addForm.values, validation: addForm.validation, submitAttempted: addForm.submitAttempted, submitting: addForm.submitting, submitError: addForm.submitError, onFieldChange: addForm.setField, onTrustChange: addForm.setTrust, onSubmit: () => void addForm.submit(), canTest: list.capabilities.canTest, testing: list.isPending(DRAFT_TEST_SCOPE, 'test'), ...(list.testResults[DRAFT_TEST_SCOPE] ? { testResult: list.testResults[DRAFT_TEST_SCOPE] } : {}), onTest: () => void list.test(undefined, addForm.values), addLabel: "Add server" })) : null, list.loading ? (_jsx("div", { className: "source-config-list-loading", role: "status", children: t('Loading…') })) : list.sources.length === 0 ? (_jsxs("div", { className: "external-mcp-empty", children: [_jsx("p", { className: "external-mcp-empty-title", children: t('No MCP servers configured.') }), _jsx("p", { className: "external-mcp-empty-hint", children: t('Click "Add server" to get started — pick a template or set up a custom stdio / HTTP server.') })] })) : (_jsx("div", { className: "source-config-list-items", children: list.sources.map((source) => (_jsx(SourceConfigItemCard, { source: source, fieldSpecs: fieldSpecs, ...(trustOptions ? { trustOptions } : {}), capabilities: list.capabilities, removing: list.isPending(source.id, 'remove'), refreshing: list.isPending(source.id, 'refresh'), settingTrust: list.isPending(source.id, 'trust'), testing: list.isPending(source.id, 'test'), updating: list.isPending(source.id, 'update'), onRefresh: () => void list.refresh(source.id), onRemove: () => void list.remove(source.id), onTrustChange: (trust) => void list.setTrust(source.id, trust), onTest: () => void list.test(source.id), onUpdate: (patch) => void list.update(source.id, patch), ...(list.testResults[source.id] ? { testResult: list.testResults[source.id] } : {}) }, source.id))) })), _jsx(ExternalMcpFooter, { saveStatusLabel: saveStatusLabel, configPath: configPath })] }));
52
+ }
53
+ /** Renders the save-status footer as one unit; a path alone does not show it. */
54
+ function ExternalMcpFooter({ saveStatusLabel, configPath }) {
55
+ const t = useT();
56
+ if (!saveStatusLabel)
57
+ return null;
58
+ return (_jsxs("div", { className: "external-mcp-footer", children: [_jsx("span", { className: "external-mcp-save-pill", children: t(saveStatusLabel) }), configPath ? (_jsxs("span", { className: "external-mcp-stored-at", children: [t('Stored at'), " ", _jsx("code", { children: configPath })] })) : null] }));
52
59
  }
53
60
  //# sourceMappingURL=ExternalMcpTab.js.map