@jini-ai/ui 0.4.7 → 0.4.9

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 (128) hide show
  1. package/README.md +2 -0
  2. package/dist/features/admin-widgets/components/InfoTip.d.ts +23 -2
  3. package/dist/features/admin-widgets/components/InfoTip.hooks.d.ts +22 -2
  4. package/dist/features/admin-widgets/components/InfoTip.hooks.js +75 -28
  5. package/dist/features/admin-widgets/components/InfoTip.js +12 -4
  6. package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +72 -0
  7. package/dist/features/admin-widgets/components/InfoTip.rules.js +68 -0
  8. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.d.ts +36 -0
  9. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.d.ts +58 -0
  10. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +66 -0
  11. package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +24 -0
  12. package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.d.ts +23 -0
  13. package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +28 -0
  14. package/dist/features/admin-widgets/index.d.ts +9 -0
  15. package/dist/features/admin-widgets/index.js +5 -0
  16. package/dist/features/execution/constants.js +11 -10
  17. package/dist/features/execution/http-adapter.d.ts +3 -1
  18. package/dist/features/execution/http-adapter.js +9 -4
  19. package/dist/features/execution/index.d.ts +1 -1
  20. package/dist/features/execution/index.js +1 -1
  21. package/dist/features/execution/react/components/ByokProviderForm.d.ts +18 -1
  22. package/dist/features/execution/react/components/ByokProviderForm.js +14 -16
  23. package/dist/features/execution/react/components/LocalCliAgentCard.d.ts +5 -1
  24. package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -87
  25. package/dist/features/execution/react/components/LocalCliAgentList.d.ts +4 -1
  26. package/dist/features/execution/react/components/LocalCliAgentList.js +13 -4
  27. package/dist/features/execution/react/components/LocalCliAgentSettings.d.ts +20 -0
  28. package/dist/features/execution/react/components/LocalCliAgentSettings.js +20 -0
  29. package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.d.ts +24 -0
  30. package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +27 -0
  31. package/dist/features/execution/react/hooks/useExecutionTab.js +10 -1
  32. package/dist/features/execution/react/hooks/useLocalCliAgentCard.d.ts +48 -0
  33. package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +110 -0
  34. package/dist/features/execution/react/hooks/useLocalCliAgentList.d.ts +19 -0
  35. package/dist/features/execution/react/hooks/useLocalCliAgentList.js +13 -0
  36. package/dist/features/execution/rules.d.ts +13 -0
  37. package/dist/features/execution/rules.js +14 -0
  38. package/dist/features/execution/types.d.ts +2 -0
  39. package/dist/features/html-editor/canvas-embed-placeholders.d.ts +13 -1
  40. package/dist/features/html-editor/canvas-embed-placeholders.js +61 -12
  41. package/dist/features/html-editor/dom.d.ts +5 -0
  42. package/dist/features/html-editor/dom.js +10 -0
  43. package/dist/features/html-editor/editor-blocks.d.ts +71 -0
  44. package/dist/features/html-editor/editor-blocks.js +185 -0
  45. package/dist/features/html-editor/editor-serialization.d.ts +24 -0
  46. package/dist/features/html-editor/editor-serialization.js +110 -0
  47. package/dist/features/html-editor/index.d.ts +5 -0
  48. package/dist/features/html-editor/index.js +3 -0
  49. package/dist/features/html-editor/protected-elements.d.ts +64 -0
  50. package/dist/features/html-editor/protected-elements.js +189 -0
  51. package/dist/features/html-editor/react/components/InteractiveHtmlEditor.d.ts +6 -4
  52. package/dist/features/html-editor/react/components/InteractiveHtmlEditor.js +3 -3
  53. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.d.ts +9 -1
  54. package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +107 -196
  55. package/dist/features/html-editor/source-splice.d.ts +23 -0
  56. package/dist/features/html-editor/source-splice.js +45 -0
  57. package/dist/features/i18n/dictionaries/settings-dialog.ar.js +4 -0
  58. package/dist/features/i18n/dictionaries/settings-dialog.bn.js +4 -0
  59. package/dist/features/i18n/dictionaries/settings-dialog.de.js +4 -0
  60. package/dist/features/i18n/dictionaries/settings-dialog.en.js +4 -0
  61. package/dist/features/i18n/dictionaries/settings-dialog.es.js +4 -0
  62. package/dist/features/i18n/dictionaries/settings-dialog.fa.js +4 -0
  63. package/dist/features/i18n/dictionaries/settings-dialog.fr.js +4 -0
  64. package/dist/features/i18n/dictionaries/settings-dialog.hi.js +4 -0
  65. package/dist/features/i18n/dictionaries/settings-dialog.hu.js +4 -0
  66. package/dist/features/i18n/dictionaries/settings-dialog.id.js +4 -0
  67. package/dist/features/i18n/dictionaries/settings-dialog.it.js +4 -0
  68. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +4 -0
  69. package/dist/features/i18n/dictionaries/settings-dialog.ko.js +4 -0
  70. package/dist/features/i18n/dictionaries/settings-dialog.pl.js +4 -0
  71. package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +4 -0
  72. package/dist/features/i18n/dictionaries/settings-dialog.ru.js +4 -0
  73. package/dist/features/i18n/dictionaries/settings-dialog.th.js +4 -0
  74. package/dist/features/i18n/dictionaries/settings-dialog.tr.js +4 -0
  75. package/dist/features/i18n/dictionaries/settings-dialog.uk.js +4 -0
  76. package/dist/features/i18n/dictionaries/settings-dialog.ur.js +4 -0
  77. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +4 -0
  78. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +4 -0
  79. package/dist/features/interactive-ui/interactive-ui.css +1 -1
  80. package/dist/features/interactive-ui/providers/recharts/bar-chart.js +1 -1
  81. package/dist/features/interactive-ui/providers/recharts/line-chart.js +1 -1
  82. package/dist/features/interactive-ui/providers/shadcn/button.d.ts +1 -1
  83. package/dist/features/mcp-ui/escape.js +2 -6
  84. package/dist/features/mcp-ui/surfaces/bridge.js +4 -2
  85. package/dist/features/mcp-ui/surfaces/document.d.ts +11 -4
  86. package/dist/features/mcp-ui/surfaces/document.js +25 -3
  87. package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
  88. package/dist/features/media-providers/constants.d.ts +0 -7
  89. package/dist/features/media-providers/constants.js +7 -15
  90. package/dist/features/media-providers/react/components/MediaProvidersTab.js +3 -2
  91. package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +6 -3
  92. package/dist/features/memory/dependencies.d.ts +2 -0
  93. package/dist/features/memory/dependencies.js +9 -25
  94. package/dist/features/memory/index.d.ts +1 -0
  95. package/dist/features/memory/index.js +1 -0
  96. package/dist/features/memory/react/MemoryPorts.d.ts +17 -0
  97. package/dist/features/memory/react/MemoryPorts.js +15 -0
  98. package/dist/features/memory/react/hooks/useMemoryConfig.hooks.d.ts +2 -2
  99. package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +7 -7
  100. package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.d.ts +2 -3
  101. package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +4 -5
  102. package/dist/features/memory/react/hooks/useMemoryEntries.hooks.d.ts +2 -3
  103. package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -5
  104. package/dist/features/memory/react/hooks/useMemoryExtractions.hooks.js +2 -2
  105. package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
  106. package/dist/features/panel-kit/fetch-query/adapter.react.js +2 -2
  107. package/dist/features/panel-kit/fetch-query/adapter.tanstack.d.ts +1 -1
  108. package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +35 -13
  109. package/dist/features/panel-kit/fetch-query/binding.react.d.ts +1 -1
  110. package/dist/features/panel-kit/fetch-query/environment.browser.d.ts +1 -1
  111. package/dist/features/panel-kit/fetch-query/index.d.ts +4 -2
  112. package/dist/features/panel-kit/fetch-query/tanstack.d.ts +4 -2
  113. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +3 -1
  114. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +9 -2
  115. package/dist/features/settings/dialog/styles/settings-dialog.css +169 -21
  116. package/dist/features/tabbed-dialog/styles/tabbed-dialog.css +91 -0
  117. package/dist/react/components/RemixIcon.d.ts +1 -41
  118. package/dist/react/components/RemixIcon.js +3 -176
  119. package/dist/react/components/RemixIcon.stylesheet.d.ts +84 -0
  120. package/dist/react/components/RemixIcon.stylesheet.js +208 -0
  121. package/dist/react/components/remixicon-font-recovery.d.ts +55 -0
  122. package/dist/react/components/remixicon-font-recovery.js +110 -0
  123. package/dist/styles/admin.css +8 -2
  124. package/package.json +12 -7
  125. package/dist/features/panel-kit/fetch-query/cache.d.ts +0 -125
  126. package/dist/features/panel-kit/fetch-query/cache.js +0 -232
  127. package/dist/features/panel-kit/fetch-query/types.d.ts +0 -93
  128. package/dist/features/panel-kit/fetch-query/types.js +0 -2
@@ -6,6 +6,9 @@ export { InfoTip } from './components/InfoTip.js';
6
6
  export type { InfoTipProps } from './components/InfoTip.js';
7
7
  export { useInfoTip } from './components/InfoTip.hooks.js';
8
8
  export type { InfoTipController } from './components/InfoTip.hooks.js';
9
+ export { TIP_BOUNDS_ATTRIBUTE } from './components/InfoTip.hooks.js';
10
+ export { placeInfoTipBubble, infoTipBubbleStyle } from './components/InfoTip.rules.js';
11
+ export type { InfoTipPlacement } from './components/InfoTip.rules.js';
9
12
  export { SeeMore, resolveSeeMoreView } from './components/SeeMore/SeeMore.js';
10
13
  export type { SeeMoreProps, SeeMoreView } from './components/SeeMore/SeeMore.js';
11
14
  export { useSeeMoreClamp } from './components/SeeMore/SeeMore.hooks.js';
@@ -17,4 +20,10 @@ export { ComingSoonPanel } from './components/ComingSoonPanel.js';
17
20
  export type { ComingSoonPanelProps } from './components/ComingSoonPanel.js';
18
21
  export { ComingSoonNotice } from './components/ComingSoonNotice.js';
19
22
  export type { ComingSoonNoticeProps } from './components/ComingSoonNotice.js';
23
+ export { SuggestInput } from './components/SuggestInput/SuggestInput.js';
24
+ export type { SuggestInputProps } from './components/SuggestInput/SuggestInput.js';
25
+ export { useSuggestInput, matchSuggestions } from './components/SuggestInput/SuggestInput.hooks.js';
26
+ export type { SuggestInputController } from './components/SuggestInput/SuggestInput.hooks.js';
27
+ export { useDebouncedHtmlPreview } from './components/useDebouncedHtmlPreview.hooks.js';
28
+ export type { HtmlPreviewPort, HtmlPreviewState } from './components/useDebouncedHtmlPreview.hooks.js';
20
29
  //# sourceMappingURL=index.d.ts.map
@@ -2,10 +2,15 @@ export { Select, resolveSelectTriggerLabel } from './components/Select/Select.js
2
2
  export { useSelectDropdown, usePanelPosition } from './components/Select/Select.hooks.js';
3
3
  export { InfoTip } from './components/InfoTip.js';
4
4
  export { useInfoTip } from './components/InfoTip.hooks.js';
5
+ export { TIP_BOUNDS_ATTRIBUTE } from './components/InfoTip.hooks.js';
6
+ export { placeInfoTipBubble, infoTipBubbleStyle } from './components/InfoTip.rules.js';
5
7
  export { SeeMore, resolveSeeMoreView } from './components/SeeMore/SeeMore.js';
6
8
  export { useSeeMoreClamp } from './components/SeeMore/SeeMore.hooks.js';
7
9
  export { ImagePreviewModal } from './components/ImagePreviewModal.js';
8
10
  export { useImagePreviewModal } from './components/ImagePreviewModal.hooks.js';
9
11
  export { ComingSoonPanel } from './components/ComingSoonPanel.js';
10
12
  export { ComingSoonNotice } from './components/ComingSoonNotice.js';
13
+ export { SuggestInput } from './components/SuggestInput/SuggestInput.js';
14
+ export { useSuggestInput, matchSuggestions } from './components/SuggestInput/SuggestInput.hooks.js';
15
+ export { useDebouncedHtmlPreview } from './components/useDebouncedHtmlPreview.hooks.js';
11
16
  //# sourceMappingURL=index.js.map
@@ -1,10 +1,11 @@
1
+ import { VENDOR_API_BASE_URLS } from '@jini-ai/integrations/media-providers/catalog';
1
2
  /** Fallback endpoint per protocol, used when a host supplies no preset and
2
3
  * the operator has not typed a base URL. Origin: `DEFAULT_BASE_URL_BY_PROTOCOL`. */
3
4
  export const DEFAULT_BASE_URL_BY_PROTOCOL = {
4
- anthropic: 'https://api.anthropic.com',
5
- openai: 'https://api.openai.com/v1',
6
- azure: '',
7
- google: 'https://generativelanguage.googleapis.com',
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,
8
9
  };
9
10
  /** Display order and English labels for the protocol row. Values are i18n
10
11
  * keys (this package's convention is "the English string is the key"). */
@@ -62,7 +63,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
62
63
  id: 'anthropic',
63
64
  title: 'Anthropic',
64
65
  protocol: 'anthropic',
65
- baseUrl: 'https://api.anthropic.com',
66
+ baseUrl: VENDOR_API_BASE_URLS.anthropic,
66
67
  preferredModels: ['claude-sonnet-5', 'claude-opus-5', 'claude-haiku-4-5'],
67
68
  kind: 'protocol',
68
69
  apiKeyPrefix: 'sk-ant-',
@@ -71,7 +72,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
71
72
  id: 'openai',
72
73
  title: 'OpenAI',
73
74
  protocol: 'openai',
74
- baseUrl: 'https://api.openai.com/v1',
75
+ baseUrl: VENDOR_API_BASE_URLS.openai,
75
76
  // `gpt-4o`/`gpt-4o-mini`/`o3`/`o4-mini` are stale: OpenAI's GPT-5.6 family
76
77
  // (released 2026-07-09, per the API changelog) replaced the old
77
78
  // flagship/mini/nano naming with named tiers — sol/terra/luna. `sol` is
@@ -88,7 +89,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
88
89
  id: 'azure-openai',
89
90
  title: 'Azure OpenAI',
90
91
  protocol: 'azure',
91
- baseUrl: '',
92
+ baseUrl: VENDOR_API_BASE_URLS.azure,
92
93
  preferredModels: [],
93
94
  kind: 'protocol',
94
95
  // No `apiKeyPrefix` deliberately: an Azure OpenAI key is a bare hex string with no vendor
@@ -101,7 +102,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
101
102
  id: 'google-gemini',
102
103
  title: 'Google Gemini',
103
104
  protocol: 'google',
104
- baseUrl: 'https://generativelanguage.googleapis.com',
105
+ baseUrl: VENDOR_API_BASE_URLS.google,
105
106
  // Ordered newest-first: `rules.ts`'s `saved?.model ?? preset.preferredModels[0]` makes entry [0]
106
107
  // the default for anyone who has never picked a model, so a stale head silently pins new users
107
108
  // to an old generation. Reported live: the field defaulted to `gemini-2.5-flash` while the
@@ -119,7 +120,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
119
120
  id: 'openrouter',
120
121
  title: 'OpenRouter',
121
122
  protocol: 'openai',
122
- baseUrl: 'https://openrouter.ai/api/v1',
123
+ baseUrl: VENDOR_API_BASE_URLS.openrouter,
123
124
  preferredModels: ['anthropic/claude-sonnet-5', 'google/gemini-2.5-pro', 'openai/gpt-5.6-sol'],
124
125
  kind: 'gateway',
125
126
  // Longer than OpenAI's `sk-`, and deliberately so: `apiKeyFormatWarning` resolves a key to the
@@ -130,7 +131,7 @@ export const DEFAULT_PROVIDER_PRESETS = [
130
131
  id: 'ollama',
131
132
  title: 'Ollama',
132
133
  protocol: 'openai',
133
- baseUrl: 'http://localhost:11434/v1',
134
+ baseUrl: VENDOR_API_BASE_URLS.ollama,
134
135
  preferredModels: [],
135
136
  kind: 'gateway',
136
137
  requiresApiKey: false,
@@ -2,7 +2,9 @@ import type { ByokConfig, DetectedAgent } from './types.js';
2
2
  import type { ExecutionPort } from './ports.js';
3
3
  /** Decoded HTTP operations; hosts retain auth, URLs and transport validation. */
4
4
  export interface ExecutionProbeTransport {
5
- detectExecutionAgents(required: Record<string, never>, optional?: Record<string, never>): Promise<{
5
+ detectExecutionAgents(required: Record<string, never>, optional?: {
6
+ force?: boolean;
7
+ }): Promise<{
6
8
  data: readonly DetectedAgent[];
7
9
  }>;
8
10
  testExecutionConnection(required: Pick<ByokConfig, "protocol" | "baseUrl" | "apiKey" | "model"> & ExecutionProbeFlags, optional?: Record<string, never>): Promise<{
@@ -58,8 +58,8 @@ export function createExecutionHttpAdapter({ transport }, _options = {}) {
58
58
  * route call itself fails (session expired, network down, this admin's own
59
59
  * route 500ing) — a case with no provider-side answer to report as a value.
60
60
  */
61
- async function requestAgentDetection() {
62
- return requireExecutionResponseData({ response: await transport.detectExecutionAgents({}, {}), callSite: "detectExecutionAgents" }, {});
61
+ async function requestAgentDetection({ force = false } = {}) {
62
+ return requireExecutionResponseData({ response: await transport.detectExecutionAgents({}, force ? { force: true } : {}), callSite: "detectExecutionAgents" }, {});
63
63
  }
64
64
  /**
65
65
  * Last detection, shared across every `ExecutionTab` mount in this tab's
@@ -96,7 +96,12 @@ export function createExecutionHttpAdapter({ transport }, _options = {}) {
96
96
  // error in place for the rest of the session, and the port's contract is
97
97
  // that a failure REJECTS (see this function's doc) — so a retry has to be
98
98
  // able to actually retry.
99
- inFlight.catch(() => {
99
+ inFlight.then((agents) => {
100
+ // A background snapshot is usable but provisional. Subsequent reads must reach the host
101
+ // cache so completed discovery replaces it; they never request another CLI probe.
102
+ if (agents.some(agent => agent.discoveryPending) && cachedDetection === inFlight)
103
+ cachedDetection = null;
104
+ }, () => {
100
105
  if (cachedDetection === inFlight)
101
106
  cachedDetection = null;
102
107
  });
@@ -116,7 +121,7 @@ export function createExecutionHttpAdapter({ transport }, _options = {}) {
116
121
  async rescanLocalAgents() {
117
122
  // Explicit operator action: always re-probe, and make the fresh result
118
123
  // the new baseline for subsequent mounts.
119
- return cacheDetection(requestAgentDetection());
124
+ return cacheDetection(requestAgentDetection({ force: true }));
120
125
  },
121
126
  async testConnection(config) {
122
127
  return transport.testExecutionConnection({
@@ -1,7 +1,7 @@
1
1
  export { API_KEY_CROSS_VENDOR_WARNING, API_KEY_TOO_SHORT_WARNING, CUSTOM_MODEL_SENTINEL, CUSTOM_PRESET_ID, DEFAULT_AGENT_CLI_ENV_FIELDS, DEFAULT_AGENT_DESCRIPTIONS, DEFAULT_BASE_URL_BY_PROTOCOL, DEFAULT_PROVIDER_PRESETS, MIN_PLAUSIBLE_API_KEY_LENGTH, PROTOCOL_OPTIONS, } from './constants.js';
2
2
  export { createFakeExecutionPort, type FakeExecutionPortOptions } from './dependencies.js';
3
3
  export type { ExecutionPort } from './ports.js';
4
- export { agentCliEnvValue, agentDiagnosticTooltip, agentExecutableRepairState, agentMetaLabel, type AgentMetaLabels, agentModelSummary, apiKeyFormatWarning, binPathEnvField, cleanAgentVersionLabel, cliEnvFieldsForAgent, credentialsForPreset, customPreset, filterAgentModelOptions, groupPresets, isBaseUrlInvalid, isProviderConfigured, isValidApiBaseUrl, missingRequiredFields, modelIdForReasoningLevel, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, nextConfigForProtocolSelect, parseMaxTokens, presetRequiresApiKey, presetsForProtocol, reasoningModelGroupFor, reasoningModelGroups, resolveSelectedPreset, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, showsBaseUrlField, sortDetectedAgents, splitReasoningModelId, } from './rules.js';
4
+ export { agentCliEnvValue, agentDiagnosticTooltip, agentExecutableRepairState, agentMetaLabel, type AgentMetaLabels, agentModelSummary, apiKeyFormatWarning, binPathEnvField, cleanAgentVersionLabel, cliEnvFieldsForAgent, credentialsForPreset, customPreset, filterAgentModelOptions, groupPresets, isBaseUrlInvalid, isProviderConfigured, isValidApiBaseUrl, missingRequiredFields, modelIdForReasoningLevel, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, nextConfigForProtocolSelect, parseMaxTokens, presetRequiresApiKey, presetsForProtocol, reasoningModelGroupFor, reasoningModelGroups, resolveSelectedPreset, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, showsBaseUrlField, sortDetectedAgents, partitionAgentsByInstalled, splitReasoningModelId, } from './rules.js';
5
5
  export type { AgentAuthStatus, AgentCliEnvFieldSpec, AgentDiagnostic, AgentDiagnosticReason, AgentDiagnosticSeverity, AgentExecutableRepair, AgentExecutableSource, AgentFixIntent, AgentModelOption, AgentModelSource, AgentScanState, ApiKeyWarning, AgentSupportsCustomModel, AgentTestState, ApiProtocol, ByokConfig, ByokProviderCredentials, ByokRequiredField, ConnectionTestState, DetectedAgent, ExecutionConfig, ExecutionMode, LocalCliConfig, ModelDiscoveryState, ProviderPreset, ProviderPresetKind, ReasoningModelGroup, } from './types.js';
6
6
  export { ExecutionTab } from './react/components/ExecutionTab.js';
7
7
  export type { ExecutionTabProps } from './react/components/ExecutionTab.js';
@@ -1,6 +1,6 @@
1
1
  export { API_KEY_CROSS_VENDOR_WARNING, API_KEY_TOO_SHORT_WARNING, CUSTOM_MODEL_SENTINEL, CUSTOM_PRESET_ID, DEFAULT_AGENT_CLI_ENV_FIELDS, DEFAULT_AGENT_DESCRIPTIONS, DEFAULT_BASE_URL_BY_PROTOCOL, DEFAULT_PROVIDER_PRESETS, MIN_PLAUSIBLE_API_KEY_LENGTH, PROTOCOL_OPTIONS, } from './constants.js';
2
2
  export { createFakeExecutionPort } from './dependencies.js';
3
- export { agentCliEnvValue, agentDiagnosticTooltip, agentExecutableRepairState, agentMetaLabel, agentModelSummary, apiKeyFormatWarning, binPathEnvField, cleanAgentVersionLabel, cliEnvFieldsForAgent, credentialsForPreset, customPreset, filterAgentModelOptions, groupPresets, isBaseUrlInvalid, isProviderConfigured, isValidApiBaseUrl, missingRequiredFields, modelIdForReasoningLevel, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, nextConfigForProtocolSelect, parseMaxTokens, presetRequiresApiKey, presetsForProtocol, reasoningModelGroupFor, reasoningModelGroups, resolveSelectedPreset, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, showsBaseUrlField, sortDetectedAgents, splitReasoningModelId, } from './rules.js';
3
+ export { agentCliEnvValue, agentDiagnosticTooltip, agentExecutableRepairState, agentMetaLabel, agentModelSummary, apiKeyFormatWarning, binPathEnvField, cleanAgentVersionLabel, cliEnvFieldsForAgent, credentialsForPreset, customPreset, filterAgentModelOptions, groupPresets, isBaseUrlInvalid, isProviderConfigured, isValidApiBaseUrl, missingRequiredFields, modelIdForReasoningLevel, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, nextConfigForProtocolSelect, parseMaxTokens, presetRequiresApiKey, presetsForProtocol, reasoningModelGroupFor, reasoningModelGroups, resolveSelectedPreset, selectedAgentModel, selectedAgentReasoning, shouldShowCustomModelInput, showsBaseUrlField, sortDetectedAgents, partitionAgentsByInstalled, splitReasoningModelId, } from './rules.js';
4
4
  export { ExecutionTab } from './react/components/ExecutionTab.js';
5
5
  export { ByokProviderForm } from './react/components/ByokProviderForm.js';
6
6
  export { LocalCliAgentList } from './react/components/LocalCliAgentList.js';
@@ -58,6 +58,8 @@ export interface ByokProviderFormProps {
58
58
  * Omitted by every existing caller, so the rendered output is byte-identical without it.
59
59
  */
60
60
  formFooter?: ReactNode;
61
+ /** Host actions directly below Model and its results, before any Advanced disclosure. */
62
+ modelFooter?: ReactNode;
61
63
  /**
62
64
  * `true` when a key is already held somewhere the browser cannot read — so the API-key input is
63
65
  * legitimately empty and must NOT be treated as a missing required field.
@@ -91,6 +93,21 @@ export interface ByokProviderFormProps {
91
93
  apiKeyPlaceholder?: string;
92
94
  /** This card's own agent handle — see `ExecutionTab`'s `agentHandle` doc for the split. */
93
95
  agentHandle?: string;
96
+ /**
97
+ * A host's button row for the key (Test key / Save key), directly UNDER the input; Show stays on
98
+ * the input's own line (owner, 2026-10-09: the input with Show beside it, actions beneath). Given,
99
+ * the key field renders as a `<div>` whose `<label>` wraps only the field name, so these buttons are
100
+ * never nested inside a label (which would fold their text into the input's accessible name).
101
+ */
102
+ apiKeyActions?: ReactNode;
103
+ /**
104
+ * Moves Base URL and Max tokens into a collapsed "Advanced" disclosure after the Model row. For
105
+ * hosts where those two are rarely touched; it opens on its own while Base URL is invalid or
106
+ * missing, so a required-field problem is never hidden. Default `false` keeps the original order.
107
+ */
108
+ collapseAdvanced?: boolean;
109
+ /** Paint required-field errors after that field loses focus; validation gates stay immediate. */
110
+ deferMissingFeedback?: boolean;
94
111
  }
95
112
  /**
96
113
  * The BYOK credential card: API key, base URL, optional token cap, and model.
@@ -98,5 +115,5 @@ export interface ByokProviderFormProps {
98
115
  * `ByokModelField`, `ByokConnectionTestControl`) — already factored out
99
116
  * upstream, so this is assembly plus the origin's required-field marking.
100
117
  */
101
- export declare function ByokProviderForm({ config, onConfigChange, preset, presets, modelDiscovery, connectionTest, onTestConnection, canTestConnection, apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder, agentHandle, }: ByokProviderFormProps): import("react").JSX.Element;
118
+ export declare function ByokProviderForm({ config, onConfigChange, preset, presets, modelDiscovery, connectionTest, onTestConnection, canTestConnection, apiKeyFooter, formFooter, modelFooter, apiKeyStoredExternally, apiKeyPlaceholder, agentHandle, apiKeyActions, collapseAdvanced, deferMissingFeedback, }: ByokProviderFormProps): import("react").JSX.Element;
102
119
  //# sourceMappingURL=ByokProviderForm.d.ts.map
@@ -1,18 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
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
5
  import { CUSTOM_MODEL_SENTINEL, DEFAULT_PROVIDER_PRESETS } from '../../constants.js';
6
- import { apiKeyFormatWarning, isBaseUrlInvalid, missingRequiredFields, parseMaxTokens, presetRequiresApiKey, shouldShowCustomModelInput, showsBaseUrlField, } from '../../rules.js';
6
+ import { apiKeyFormatWarning, parseMaxTokens, presetRequiresApiKey, shouldShowCustomModelInput, showsBaseUrlField, } from '../../rules.js';
7
7
  import { SearchableModelSelect } from './SearchableModelSelect.js';
8
+ import { useByokFieldValidation } from '../hooks/use-byok-field-validation.hooks.js';
8
9
  /**
9
10
  * The BYOK credential card: API key, base URL, optional token cap, and model.
10
11
  * Origin: `components/byok/*` (`ByokKeyField`, `ByokProviderBaseUrl`,
11
12
  * `ByokModelField`, `ByokConnectionTestControl`) — already factored out
12
13
  * upstream, so this is assembly plus the origin's required-field marking.
13
14
  */
14
- export function ByokProviderForm({ config, onConfigChange, preset, presets = DEFAULT_PROVIDER_PRESETS, modelDiscovery, connectionTest, onTestConnection, canTestConnection = true, apiKeyFooter, formFooter, apiKeyStoredExternally = false, apiKeyPlaceholder, agentHandle, }) {
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, }) {
15
16
  const t = useT();
17
+ const keyInputId = useId();
18
+ const KeyField = apiKeyActions ? 'div' : 'label';
16
19
  const [revealKey, setRevealKey] = useState(false);
17
20
  /** Whether the operator explicitly picked "Custom…" in the model picker — distinct from "the
18
21
  * saved model simply isn't in the live list". Same UI-local toggle, for the same reason, as
@@ -33,13 +36,7 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
33
36
  useEffect(() => {
34
37
  setRevealKey(false);
35
38
  }, [config.providerId]);
36
- const missing = new Set(missingRequiredFields(config, preset));
37
- // A stored-but-unreadable key satisfies the requirement. Subtracted here rather than threaded into
38
- // `missingRequiredFields` because that function is a pure rule over `ByokConfig` and this is a fact
39
- // about the HOST's storage, which does not belong in the config shape.
40
- if (apiKeyStoredExternally)
41
- missing.delete('apiKey');
42
- const baseUrlInvalid = isBaseUrlInvalid(config);
39
+ const { missing, baseUrlInvalid, isMissing, markTouched } = useByokFieldValidation({ config, preset, apiKeyStoredExternally }, { deferMissingFeedback });
43
40
  /** Deliberately NOT folded into `missing` above: that set drives `is-missing` styling and the
44
41
  * Test-connection disable, and a wrong-SHAPED key must never reach either. */
45
42
  const keyFormatWarning = apiKeyFormatWarning(config, preset, presets);
@@ -70,7 +67,10 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
70
67
  // a hydrated or hand-typed id is never silently replaced by whatever sorts first.
71
68
  const customModelActive = showModelPicker && shouldShowCustomModelInput(config.model, liveModels, explicitCustomModel);
72
69
  const patch = (next) => onConfigChange({ ...config, ...next });
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("label", { className: "jini-field", children: [_jsxs("span", { className: "jini-field-label", 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", { className: 'jini-input' + (missing.has('apiKey') ? ' is-missing' : ''), type: revealKey ? 'text' : 'password',
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
74
  /* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on credential-shaped
75
75
  fields (a long-standing intentional decision, not a bug). `off` here is what let the
76
76
  reported autofill through: Chrome filled this field with the admin's own saved
@@ -81,9 +81,7 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
81
81
 
82
82
  Known trade-off, not fixed here: Chrome may now offer to GENERATE a password on
83
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') })] }), _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, _jsxs("div", { className: "jini-byok-field-row", children: [showsBaseUrlField(preset) ? (_jsxs("label", { className: "jini-field", children: [_jsxs("span", { className: "jini-field-label", children: [t('Base URL'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), _jsx("input", { className: 'jini-input' + (baseUrlInvalid || missing.has('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
85
- ? t('Enter an absolute http(s) URL.')
86
- : 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.') })] })] }), _jsxs("div", { className: "jini-byok-model-row", children: [_jsxs("label", { className: "jini-field", 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' + (missing.has('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) => {
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) => {
87
85
  if (next === CUSTOM_MODEL_SENTINEL) {
88
86
  // Open the free-text box WITHOUT clearing `config.model`. Blanking it here would
89
87
  // discard a working model the moment someone opened the picker to look at it.
@@ -92,7 +90,7 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
92
90
  }
93
91
  setExplicitCustomModel(false);
94
92
  patch({ model: next });
95
- } })) : null, !showModelPicker || customModelActive ? (_jsx("input", { className: 'jini-input' + (missing.has('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' ? (
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' ? (
96
94
  // Non-blocking (the field above stays editable, with the preset's static
97
95
  // suggestions) but never silent — a discovery failure is an operator-actionable
98
96
  // fact (bad key, wrong base URL, unreachable endpoint), not "no models exist".
@@ -103,6 +101,6 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
103
101
  // identity host suites locate it by — only its parent changed.
104
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'
105
103
  ? (connectionTest.message !== undefined ? t(connectionTest.message) : t('Connection succeeded'))
106
- : t(connectionTest.message) }) })) : null, formFooter ? _jsx("div", { className: "jini-byok-form-footer", children: formFooter }) : null] }));
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] }));
107
105
  }
108
106
  //# sourceMappingURL=ByokProviderForm.js.map
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from 'react';
1
+ import type { ReactNode } from 'react';
2
2
  import type { AgentCliEnvFieldSpec, AgentTestState, DetectedAgent, LocalCliConfig } from '../../types.js';
3
3
  export interface LocalCliAgentCardProps {
4
4
  agent: DetectedAgent;
@@ -38,6 +38,10 @@ export interface LocalCliAgentCardProps {
38
38
  * the origin's managed-runtime card — its wallet balance, plan badges,
39
39
  * sign-in pill, and upgrade coachmarks are bound to that product's hosted
40
40
  * service, not to the idea of "a CLI on a machine".
41
+ *
42
+ * Owner, 2026-10-09: selection grows this card inside its own grid column.
43
+ * `LocalCliAgentSettings` stays inside it, so the neighbour keeps its normal
44
+ * height rather than sharing a full-row settings strip.
41
45
  */
42
46
  export declare function LocalCliAgentCard({ agent, config, selected, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, renderIcon, onTest, agentTest, onRescan, agentHandle, }: LocalCliAgentCardProps): import("react").JSX.Element;
43
47
  //# sourceMappingURL=LocalCliAgentCard.d.ts.map
@@ -1,13 +1,11 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { agentHandleProps } from '@jini-ai/agentic';
4
3
  import { useT } from '../../../i18n/index.js';
5
- import { CUSTOM_MODEL_SENTINEL, DEFAULT_AGENT_DESCRIPTIONS } from '../../constants.js';
6
- import { agentExecutableRepairState, agentMetaLabel, agentModelSummary, binPathEnvField, } from '../../rules.js';
7
- import { useReasoningControl } from '../hooks/useReasoningControl.js';
8
- import { AgentCliEnvFields } from './AgentCliEnvFields.js';
4
+ import { DEFAULT_AGENT_DESCRIPTIONS } from '../../constants.js';
5
+ import { agentMetaLabel, agentModelSummary } from '../../rules.js';
6
+ import { useLocalCliAgentCard } from '../hooks/useLocalCliAgentCard.js';
9
7
  import { AgentDiagnosticRow } from './AgentDiagnosticRow.js';
10
- import { SearchableModelSelect } from './SearchableModelSelect.js';
8
+ import { LocalCliAgentSettings } from './LocalCliAgentSettings.js';
11
9
  /**
12
10
  * One detected code-agent CLI, as a selectable card.
13
11
  *
@@ -21,18 +19,14 @@ import { SearchableModelSelect } from './SearchableModelSelect.js';
21
19
  * the origin's managed-runtime card — its wallet balance, plan badges,
22
20
  * sign-in pill, and upgrade coachmarks are bound to that product's hosted
23
21
  * service, not to the idea of "a CLI on a machine".
22
+ *
23
+ * Owner, 2026-10-09: selection grows this card inside its own grid column.
24
+ * `LocalCliAgentSettings` stays inside it, so the neighbour keeps its normal
25
+ * height rather than sharing a full-row settings strip.
24
26
  */
25
27
  export function LocalCliAgentCard({ agent, config, selected, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, renderIcon, onTest, agentTest, onRescan, agentHandle, }) {
26
28
  const t = useT();
27
- // Whether the operator explicitly opened the free-text "Custom" entry via
28
- // the model picker's own "Custom…" option — distinct from "the saved model
29
- // simply isn't in the known list" (`shouldShowCustomModelInput` covers
30
- // both, but only THIS one needs to be remembered as a UI-local toggle,
31
- // since it is not itself persisted config: see `LocalCliConfig`, which has
32
- // nowhere for "the operator is mid-typing a custom id" to live). Scoped to
33
- // this card instance, so switching to a different agent and back does not
34
- // leak one agent's custom-mode toggle onto another.
35
- const [explicitCustomMode, setExplicitCustomMode] = useState(false);
29
+ const card = useLocalCliAgentCard({ agent, config, agentTest, cliEnvFields, onModelChange, onReasoningChange, onRescan, onTest, t });
36
30
  const description = agent.description ?? DEFAULT_AGENT_DESCRIPTIONS[agent.id];
37
31
  const meta = agentMetaLabel(agent, {
38
32
  authRequired: t('Authentication required'),
@@ -41,77 +35,8 @@ export function LocalCliAgentCard({ agent, config, selected, onSelect, onModelCh
41
35
  notInstalled: t('Not installed'),
42
36
  });
43
37
  const summary = agentModelSummary(config, agent);
44
- const { hasModels, reasoningOptions, hasReasoning, reasoningValue, reasoningDisabled, allowCustomModel, customActive, modelChoices, selectValue, customModelInputValue, resolveNextModelId, resolveReasoningModelId, } = useReasoningControl({
45
- agent,
46
- config,
47
- explicitCustomMode,
48
- onReasoningChange: (next) => onReasoningChange(agent.id, next),
49
- });
50
- // `modelsSource` is optional in the contract, so an absent value means the
51
- // host did not say where the list came from — NOT that it came from a
52
- // built-in fallback. Defaulting to 'fallback' rendered "Built-in list" over
53
- // a list that may well be live, and told the operator to Rescan to fix a
54
- // problem they do not have. Unknown provenance shows no badge and no claim.
55
- const source = agent.modelsSource;
56
- const sourceLabel = source === 'live' ? t('Live from CLI') : source === 'fallback' ? t('Built-in list') : '';
57
- const sourceHint = source === 'live'
58
- ? t("Model list comes from this CLI. Default uses the CLI's own config.")
59
- : source === 'fallback'
60
- ? t('Showing built-in defaults. Click Rescan to pull live models from the CLI.')
61
- : '';
62
- // The test result belongs to whichever agent was probed, so a stale verdict
63
- // from a previously-tested CLI never renders under this one.
64
- const testing = agentTest.status === 'testing' && agentTest.agentId === agent.id;
65
- const result = (agentTest.status === 'ok' || agentTest.status === 'error') && agentTest.agentId === agent.id
66
- ? agentTest
67
- : null;
68
- // The "use the binary that actually ran" / "clear the override" repair
69
- // affordance — only meaningful when a successful test silently fell back
70
- // off a configured override, AND this agent's catalog names which field
71
- // that override lives in. See `agentExecutableRepairState`'s doc.
72
- //
73
- // `AgentTestState`'s 'ok' variant spells success as `status: 'ok'`, not an
74
- // `ok: true` field — `agentExecutableRepairState` takes the latter (it
75
- // mirrors `ExecutionPort.testAgent`'s raw resolved shape, one layer below
76
- // this state union), so the two must be translated explicitly here rather
77
- // than passing `result` straight through.
78
- const repair = result?.status === 'ok'
79
- ? agentExecutableRepairState({
80
- ok: true,
81
- usedExecutableSource: result.usedExecutableSource,
82
- detectedExecutablePath: result.detectedExecutablePath,
83
- })
84
- : null;
85
- const repairField = repair ? binPathEnvField(cliEnvFields, agent.id) : null;
86
- const openUrl = (url) => {
87
- if (!url)
88
- return;
89
- window.open(url, '_blank', 'noopener,noreferrer');
90
- };
91
- const diagnosticHandlers = {
92
- onRescan,
93
- onOpenInstall: agent.installUrl ? () => openUrl(agent.installUrl) : undefined,
94
- onOpenDocs: agent.docsUrl ? () => openUrl(agent.docsUrl) : undefined,
95
- };
96
38
  return (_jsxs("div", { className: 'jini-agent-card' +
97
39
  (selected ? ' is-selected' : '') +
98
- (agent.installed ? ' is-installed' : ' is-missing'), "data-testid": `jini-agent-card-${agent.id}`, children: [_jsxs("div", { className: "jini-agent-card-main", children: [_jsxs("button", { type: "button", className: "jini-agent-card-select", "data-testid": `jini-agent-select-${agent.id}`, "aria-pressed": selected, disabled: !agent.installed, onClick: () => onSelect(agent.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'select', role: 'button', label: agent.label }) }), children: [_jsx("span", { className: "jini-agent-card-icon", "aria-hidden": "true", children: renderIcon ? renderIcon(agent) : agent.label.slice(0, 1).toUpperCase() }), _jsxs("span", { className: "jini-agent-card-body", children: [_jsxs("span", { className: "jini-agent-card-name", children: [_jsx("span", { className: "jini-agent-card-title", children: agent.label }), description ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "jini-agent-card-name-divider", "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "jini-agent-card-tagline", children: description })] })) : null] }), agent.badges?.length ? (_jsx("span", { className: "jini-agent-card-badges", children: agent.badges.map((badge) => (_jsx("span", { className: "jini-agent-card-badge", children: badge }, badge))) })) : null, meta.text ? (_jsx("span", { className: "jini-agent-card-meta", children: _jsx("span", { title: meta.title || undefined, children: meta.text }) })) : null, !selected && summary ? (_jsxs("span", { className: "jini-agent-card-model-summary", children: [_jsx("span", { children: t('Model') }), _jsx("strong", { children: summary })] })) : null] })] }), selected && onTest ? (_jsx("button", { type: "button", className: 'jini-btn jini-agent-card-test' + (testing ? ' is-loading' : ''), "data-testid": `jini-agent-test-${agent.id}`, disabled: testing, onClick: () => onTest(agent), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'test', role: 'button', label: t('Test') }) }), children: testing ? t('Testing…') : t('Test') })) : null] }), (agent.diagnostics ?? []).map((diagnostic, index) => (_jsx(AgentDiagnosticRow, { diagnostic: diagnostic, handlers: diagnosticHandlers }, `${diagnostic.reason}-${index}`))), selected && (hasModels || hasReasoning) ? (_jsxs("div", { className: "jini-agent-card-config", children: [hasModels ? (_jsxs(_Fragment, { children: [_jsxs("label", { className: "jini-field", children: [_jsxs("span", { className: "jini-field-label", children: [t('Model'), sourceLabel ? (_jsx("span", { className: `jini-agent-model-source ${source}`, children: sourceLabel })) : null] }), _jsx(SearchableModelSelect, { className: "jini-input jini-agent-model-select", ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: `jini-agent-model-${agent.id}`, searchInputTestId: `jini-agent-model-search-${agent.id}`, ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {}), value: selectValue, models: modelChoices, additionalOptions: allowCustomModel ? [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }] : undefined, onChange: (nextValue) => {
99
- if (nextValue === CUSTOM_MODEL_SENTINEL) {
100
- setExplicitCustomMode(true);
101
- onModelChange(agent.id, '');
102
- return;
103
- }
104
- setExplicitCustomMode(false);
105
- onModelChange(agent.id, resolveNextModelId(nextValue));
106
- } })] }), sourceHint ? _jsx("p", { className: "jini-field-hint", children: sourceHint }) : null] })) : null, customActive ? (_jsxs("label", { className: "jini-field", children: [_jsx("span", { className: "jini-field-label", children: t('Custom model id') }), _jsx("input", { className: "jini-input", type: "text", "data-testid": `jini-agent-model-custom-${agent.id}`, value: customModelInputValue, placeholder: t('e.g. my-fine-tuned-model'), spellCheck: false, onChange: (event) => onModelChange(agent.id, event.target.value.trim()), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'custom-model', role: 'field', label: t('Custom model id') }) }) })] })) : null, hasReasoning ? (_jsxs("label", { className: "jini-field", children: [_jsx("span", { className: "jini-field-label", children: t('Reasoning effort') }), _jsx("select", { className: "jini-input", "data-testid": `jini-agent-reasoning-${agent.id}`, value: reasoningValue, disabled: reasoningDisabled, onChange: (event) => {
107
- // A model-suffix agent has no reasoning value of its own to record: the choice
108
- // is which model id runs, so it goes back out through `onModelChange`.
109
- const suffixModelId = resolveReasoningModelId(event.target.value);
110
- if (suffixModelId !== null) {
111
- onModelChange(agent.id, suffixModelId);
112
- return;
113
- }
114
- onReasoningChange(agent.id, event.target.value);
115
- }, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'reasoning', role: 'field', label: t('Reasoning effort') }) }), children: reasoningOptions.map((option) => (_jsx("option", { value: option.id, children: option.label }, option.id))) })] })) : null] })) : null, selected ? (_jsx(AgentCliEnvFields, { agentId: agent.id, fields: cliEnvFields, config: config, onChange: onEnvChange })) : null, result ? (_jsx("p", { className: 'jini-agent-card-test-result is-' + result.status, role: result.status === 'error' ? 'alert' : 'status', children: result.message ?? (result.status === 'ok' ? t('Agent is ready.') : t('Agent check failed')) })) : null, repair && repairField ? (_jsxs("div", { className: "jini-agent-card-path-repair", children: [_jsx("span", { className: "jini-field-hint", children: t('A custom path override did not work, so this used the automatically detected binary instead.') }), _jsxs("div", { className: "jini-agent-card-path-repair-actions", children: [repair.canUseDetected ? (_jsx("button", { type: "button", className: "jini-btn", "data-testid": `jini-agent-path-repair-use-${agent.id}`, onClick: () => onEnvChange(agent.id, repairField.envKey, repair.detectedPath), children: t('Use detected path') })) : null, _jsx("button", { type: "button", className: "jini-btn jini-btn-ghost", "data-testid": `jini-agent-path-repair-clear-${agent.id}`, onClick: () => onEnvChange(agent.id, repairField.envKey, ''), children: t('Clear custom path') })] })] })) : null] }));
40
+ (agent.installed ? ' is-installed' : ' is-missing'), "data-testid": `jini-agent-card-${agent.id}`, children: [_jsxs("div", { className: "jini-agent-card-main", children: [_jsxs("button", { type: "button", className: "jini-agent-card-select", "data-testid": `jini-agent-select-${agent.id}`, "aria-pressed": selected, disabled: !agent.installed, onClick: () => onSelect(agent.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'select', role: 'button', label: agent.label }) }), children: [_jsx("span", { className: "jini-agent-card-icon", "aria-hidden": "true", children: renderIcon ? renderIcon(agent) : agent.label.slice(0, 1).toUpperCase() }), _jsxs("span", { className: "jini-agent-card-body", children: [_jsxs("span", { className: "jini-agent-card-name", children: [_jsx("span", { className: "jini-agent-card-title", children: agent.label }), description ? (_jsx("span", { className: "jini-agent-card-tagline", children: description })) : null] }), agent.badges?.length ? (_jsx("span", { className: "jini-agent-card-badges", children: agent.badges.map((badge) => (_jsx("span", { className: "jini-agent-card-badge", children: badge }, badge))) })) : null, meta.text ? (_jsx("span", { className: "jini-agent-card-meta", children: _jsx("span", { title: meta.title || undefined, children: meta.text }) })) : null, !selected && summary ? (_jsxs("span", { className: "jini-agent-card-model-summary", children: [_jsx("span", { children: t('Model') }), _jsx("strong", { children: summary })] })) : null] }), selected ? (_jsx("span", { className: "jini-agent-card-check", "aria-hidden": "true", children: "\u2713" })) : null] }), selected && onTest ? (_jsx("button", { type: "button", className: 'jini-btn jini-agent-card-test' + (card.testing ? ' is-loading' : ''), "data-testid": `jini-agent-test-${agent.id}`, disabled: card.testing, onClick: () => onTest(agent), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'test', role: 'button', label: t('Test') }) }), children: card.testing ? t('Testing…') : t('Test') })) : null] }), (agent.diagnostics ?? []).map((diagnostic, index) => (_jsx(AgentDiagnosticRow, { diagnostic: diagnostic, handlers: card.diagnosticHandlers }, `${diagnostic.reason}-${index}`))), selected && card.hasSettings ? (_jsx(LocalCliAgentSettings, { agent: agent, config: config, card: card, cliEnvFields: cliEnvFields, onModelChange: onModelChange, onEnvChange: onEnvChange, agentHandle: agentHandle })) : null] }));
116
41
  }
117
42
  //# sourceMappingURL=LocalCliAgentCard.js.map
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { AgentCliEnvFieldSpec, AgentScanState, AgentTestState, DetectedAgent, LocalCliConfig } from '../../types.js';
3
+ import { useLocalCliAgentList } from '../hooks/useLocalCliAgentList.js';
3
4
  export interface LocalCliAgentListProps {
4
5
  agents: readonly DetectedAgent[];
5
6
  config: LocalCliConfig;
@@ -27,6 +28,8 @@ export interface LocalCliAgentListProps {
27
28
  /** This list's own agent handle — see `ExecutionTab`'s `agentHandle` doc. Each card gets its own
28
29
  * distinct sub-handle, derived from the agent's own stable id via `buildAgentListHandles`. */
29
30
  agentHandle?: string;
31
+ /** Injection seam for the not-installed disclosure state. */
32
+ useAgentList?: typeof useLocalCliAgentList | undefined;
30
33
  }
31
34
  /**
32
35
  * The detected code-agent CLIs, as a selectable card grid.
@@ -36,5 +39,5 @@ export interface LocalCliAgentListProps {
36
39
  * managed-runtime entry (wallet, sign-in, plan upgrades) is left behind; see
37
40
  * `LocalCliAgentCard` for what that split does and does not cover.
38
41
  */
39
- export declare function LocalCliAgentList({ agents, config, scan, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, onRescan, onTest, agentTest, renderAgentIcon, scopeLabel, agentHandle, }: LocalCliAgentListProps): import("react").JSX.Element;
42
+ export declare function LocalCliAgentList({ agents, config, scan, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, onRescan, onTest, agentTest, renderAgentIcon, scopeLabel, agentHandle, useAgentList, }: LocalCliAgentListProps): import("react").JSX.Element;
40
43
  //# sourceMappingURL=LocalCliAgentList.d.ts.map
@@ -1,7 +1,8 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { agentHandleProps, buildAgentListHandles } from '@jini-ai/agentic';
3
3
  import { useT } from '../../../i18n/index.js';
4
4
  import { LocalCliAgentCard } from './LocalCliAgentCard.js';
5
+ import { useLocalCliAgentList } from '../hooks/useLocalCliAgentList.js';
5
6
  /**
6
7
  * The detected code-agent CLIs, as a selectable card grid.
7
8
  *
@@ -10,11 +11,19 @@ import { LocalCliAgentCard } from './LocalCliAgentCard.js';
10
11
  * managed-runtime entry (wallet, sign-in, plan upgrades) is left behind; see
11
12
  * `LocalCliAgentCard` for what that split does and does not cover.
12
13
  */
13
- export function LocalCliAgentList({ agents, config, scan, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, onRescan, onTest, agentTest, renderAgentIcon, scopeLabel, agentHandle, }) {
14
+ export function LocalCliAgentList({ agents, config, scan, onSelect, onModelChange, onReasoningChange, onEnvChange, cliEnvFields, onRescan, onTest, agentTest, renderAgentIcon, scopeLabel, agentHandle, useAgentList = useLocalCliAgentList, }) {
14
15
  const t = useT();
15
16
  const installed = agents.filter((agent) => agent.installed);
16
17
  const scope = scopeLabel ?? t('Runs a code-agent CLI detected by the host.');
17
- const cardHandles = agentHandle ? buildAgentListHandles({ prefix: agentHandle, ids: agents.map((agent) => agent.id) }) : undefined;
18
- return (_jsxs("section", { className: "jini-settings-section jini-local-cli", children: [_jsxs("div", { className: "jini-section-head", children: [_jsxs("div", { className: "jini-section-head-text", children: [_jsxs("h4", { className: "jini-agent-group-head", children: [t('Your CLIs'), " (", installed.length, ")"] }), _jsx("p", { className: "jini-field-hint", children: t('Pick the CLI that runs your prompts.') })] }), onRescan ? (_jsx("button", { type: "button", className: "jini-btn jini-local-cli-rescan", disabled: scan.status === 'scanning', onClick: onRescan, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'rescan', role: 'button', label: t('Rescan') }) }), children: scan.status === 'scanning' ? t('Scanning…') : t('↻ Rescan') })) : null] }), _jsx("p", { className: "jini-field-hint jini-local-cli-scope", children: scope }), scan.status === 'error' ? (_jsx("p", { className: "jini-field-hint is-error", role: "alert", children: scan.message })) : null, scan.status === 'scanning' ? (_jsxs("div", { className: "jini-agent-scan-card", role: "status", "aria-live": "polite", children: [_jsx("span", { className: "jini-agent-scan-ring", "aria-hidden": "true" }), _jsx("strong", { children: t('Scanning for installed agents…') })] })) : agents.length === 0 ? (scan.status === 'error' ? null : (_jsx("div", { className: "jini-empty-card", children: t('No agent CLIs detected.') }))) : (_jsx("div", { className: "jini-agent-grid", children: agents.map((agent, index) => (_jsx(LocalCliAgentCard, { agent: agent, config: config, selected: config.agentId === agent.id, onSelect: onSelect, onModelChange: onModelChange, onReasoningChange: onReasoningChange, onEnvChange: onEnvChange, cliEnvFields: cliEnvFields, renderIcon: renderAgentIcon, onTest: onTest, agentTest: agentTest, onRescan: onRescan, ...(cardHandles?.[index] ? { agentHandle: cardHandles[index] } : {}) }, agent.id))) }))] }));
18
+ const handleList = agentHandle ? buildAgentListHandles({ prefix: agentHandle, ids: agents.map((agent) => agent.id) }) : undefined;
19
+ const cardHandles = new Map(agents.map((agent, index) => [agent.id, handleList?.[index]]));
20
+ const list = useAgentList({ agents, selectedAgentId: config.agentId });
21
+ const renderCard = (agent) => {
22
+ const handle = cardHandles.get(agent.id);
23
+ return (_jsx(LocalCliAgentCard, { agent: agent, config: config, selected: config.agentId === agent.id, onSelect: onSelect, onModelChange: onModelChange, onReasoningChange: onReasoningChange, onEnvChange: onEnvChange, cliEnvFields: cliEnvFields, renderIcon: renderAgentIcon, onTest: onTest, agentTest: agentTest, onRescan: onRescan, ...(handle ? { agentHandle: handle } : {}) }, agent.id));
24
+ };
25
+ return (_jsxs("section", { className: "jini-settings-section jini-local-cli", children: [_jsxs("div", { className: "jini-section-head", children: [_jsxs("div", { className: "jini-section-head-text", children: [_jsxs("h4", { className: "jini-agent-group-head", children: [t('Your CLIs'), " (", installed.length, ")"] }), _jsx("p", { className: "jini-field-hint", children: t('Pick the CLI that runs your prompts.') })] }), onRescan ? (_jsx("button", { type: "button", className: "jini-btn jini-local-cli-rescan", disabled: scan.status === 'scanning', onClick: onRescan, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'rescan', role: 'button', label: t('Rescan') }) }), children: scan.status === 'scanning' ? t('Scanning…') : t('↻ Rescan') })) : null] }), _jsx("p", { className: "jini-field-hint jini-local-cli-scope", children: scope }), scan.status === 'error' ? (_jsx("p", { className: "jini-field-hint is-error", role: "alert", children: scan.message })) : null, scan.status === 'scanning' ? (_jsxs("div", { className: "jini-agent-scan-card", role: "status", "aria-live": "polite", children: [_jsx("span", { className: "jini-agent-scan-ring", "aria-hidden": "true" }), _jsx("strong", { children: t('Scanning for installed agents…') })] })) : agents.length === 0 ? (scan.status === 'error' ? null : (_jsx("div", { className: "jini-empty-card", children: t('No agent CLIs detected.') }))) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "jini-agent-grid", children: list.visible.map(renderCard) }), list.collapsed.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "jini-btn jini-local-cli-more", "aria-expanded": list.showCollapsed, onClick: list.toggleCollapsed, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'toggle-not-installed', role: 'button', label: t('Show not-installed CLIs') }) }), children: list.showCollapsed
26
+ ? t('Hide not installed')
27
+ : t('Show {count} not installed').replace('{count}', String(list.collapsed.length)) }), list.showCollapsed ? _jsx("div", { className: "jini-agent-grid", children: list.collapsed.map(renderCard) }) : null] })) : null] }))] }));
19
28
  }
20
29
  //# sourceMappingURL=LocalCliAgentList.js.map
@@ -0,0 +1,20 @@
1
+ import type { AgentCliEnvFieldSpec, DetectedAgent, LocalCliConfig } from '../../types.js';
2
+ import type { UseLocalCliAgentCardResult } from '../hooks/useLocalCliAgentCard.js';
3
+ export interface LocalCliAgentSettingsProps {
4
+ agent: DetectedAgent;
5
+ config: LocalCliConfig;
6
+ card: UseLocalCliAgentCardResult;
7
+ cliEnvFields: readonly AgentCliEnvFieldSpec[];
8
+ onModelChange: (agentId: string, model: string) => void;
9
+ onEnvChange: (agentId: string, envKey: string, value: string) => void;
10
+ agentHandle?: string | undefined;
11
+ }
12
+ /**
13
+ * The selected CLI's settings, stacked inside its own card.
14
+ *
15
+ * Owner, 2026-10-09: both the full-row card and the separate settings strip
16
+ * spread selection beyond its own column. Keep full-width controls and a
17
+ * visible provenance sentence inside the card so only that card grows taller.
18
+ */
19
+ export declare function LocalCliAgentSettings({ agent, config, card, cliEnvFields, onModelChange, onEnvChange, agentHandle, }: LocalCliAgentSettingsProps): import("react").JSX.Element;
20
+ //# sourceMappingURL=LocalCliAgentSettings.d.ts.map