@agent-native/core 0.147.0 → 0.148.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
  3. package/corpus/templates/analytics/DEVELOPING.md +50 -3
  4. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
  5. package/corpus/templates/analytics/app/i18n/index.ts +14 -29
  6. package/corpus/templates/analytics/app/i18n-data.ts +10 -10
  7. package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
  8. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
  9. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +197 -18
  10. package/corpus/templates/analytics/server/db/schema.ts +23 -0
  11. package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
  12. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
  13. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
  14. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
  15. package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
  16. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
  17. package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
  18. package/corpus/templates/analytics/server/plugins/db.ts +42 -0
  19. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
  20. package/corpus/templates/assets/app/i18n/index.ts +14 -29
  21. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
  22. package/corpus/templates/brain/app/i18n/index.ts +14 -29
  23. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
  24. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  25. package/corpus/templates/chat/app/i18n/index.ts +14 -29
  26. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
  27. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
  28. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
  29. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
  30. package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
  31. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
  32. package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
  33. package/corpus/templates/clips/desktop/src/app.tsx +1 -1
  34. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
  35. package/corpus/templates/content/app/i18n/index.ts +14 -29
  36. package/corpus/templates/crm/app/i18n/index.ts +14 -29
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
  38. package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
  39. package/corpus/templates/design/actions/import-code.ts +2 -43
  40. package/corpus/templates/design/app/i18n/index.ts +14 -29
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
  42. package/corpus/templates/dispatch/app/i18n-data.ts +1 -1
  43. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
  44. package/corpus/templates/factory/AGENTS.md +40 -49
  45. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
  46. package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
  47. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
  48. package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
  49. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
  50. package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
  51. package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
  52. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  53. package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
  54. package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
  55. package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
  56. package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
  57. package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
  58. package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
  59. package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
  60. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
  61. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
  62. package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
  63. package/corpus/templates/factory/app/routes/factory.tsx +95 -42
  64. package/corpus/templates/factory/server/db/schema.ts +37 -0
  65. package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
  66. package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
  67. package/corpus/templates/factory/server/triage/audit.ts +79 -0
  68. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
  69. package/corpus/templates/forms/app/i18n/index.ts +14 -29
  70. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
  71. package/corpus/templates/macros/app/i18n/index.ts +14 -29
  72. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
  73. package/corpus/templates/mail/app/i18n/index.ts +14 -29
  74. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
  75. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
  76. package/corpus/templates/plan/app/i18n/index.ts +14 -29
  77. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
  78. package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
  79. package/corpus/templates/slides/actions/import-code.ts +2 -43
  80. package/corpus/templates/slides/app/i18n/index.ts +14 -29
  81. package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
  82. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
  83. package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
  84. package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
  85. package/corpus/templates/tasks/app/i18n/index.ts +14 -29
  86. package/dist/agent/engine/ai-sdk-engine.js +2 -1
  87. package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
  88. package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
  89. package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
  90. package/dist/agent/engine/provider-endpoint-validation.js +26 -0
  91. package/dist/agent/engine/provider-env-vars.d.ts +1 -1
  92. package/dist/agent/engine/provider-env-vars.js +1 -1
  93. package/dist/agent/engine/registry.d.ts +8 -6
  94. package/dist/agent/engine/registry.js +37 -24
  95. package/dist/agent/tool-search.d.ts +1 -0
  96. package/dist/agent/tool-search.js +17 -1
  97. package/dist/automations/service.js +4 -10
  98. package/dist/brand-kit/brand-signals.d.ts +13 -0
  99. package/dist/brand-kit/brand-signals.js +15 -0
  100. package/dist/brand-kit/index.d.ts +2 -2
  101. package/dist/brand-kit/index.js +1 -1
  102. package/dist/cli/plan-blocks.d.ts +3 -18
  103. package/dist/cli/plan-blocks.js +2 -95
  104. package/dist/cli/plan-publish-store.d.ts +2 -61
  105. package/dist/cli/plan-publish-store.js +2 -127
  106. package/dist/cli/skills-content/connection.d.ts +1 -1
  107. package/dist/cli/skills-content/connection.js +1 -52
  108. package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
  109. package/dist/cli/skills-content/visual-recap-skill.js +1 -553
  110. package/dist/client/agent-engine-key.d.ts +14 -4
  111. package/dist/client/agent-engine-key.js +51 -5
  112. package/dist/client/agent-provider-catalog.d.ts +20 -0
  113. package/dist/client/agent-provider-catalog.js +100 -0
  114. package/dist/client/blocks/index.d.ts +1 -0
  115. package/dist/client/blocks/index.js +1 -0
  116. package/dist/client/chat/run-recovery.js +4 -48
  117. package/dist/client/chat-first.js +4 -3
  118. package/dist/client/i18n.d.ts +14 -0
  119. package/dist/client/i18n.js +18 -0
  120. package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
  121. package/dist/client/settings/ProviderSetupForm.js +145 -0
  122. package/dist/client/settings/SettingsPanel.js +52 -49
  123. package/dist/client/settings/index.d.ts +2 -0
  124. package/dist/client/settings/index.js +2 -0
  125. package/dist/coding-tools/run-code.d.ts +5 -0
  126. package/dist/coding-tools/run-code.js +196 -36
  127. package/dist/coding-tools/tool-orchestration.d.ts +10 -0
  128. package/dist/coding-tools/tool-orchestration.js +107 -0
  129. package/dist/collab/awareness.d.ts +2 -2
  130. package/dist/jobs/frontmatter.d.ts +9 -0
  131. package/dist/jobs/frontmatter.js +16 -0
  132. package/dist/jobs/scheduler.js +8 -8
  133. package/dist/jobs/tools.d.ts +1 -1
  134. package/dist/jobs/tools.js +2 -7
  135. package/dist/localization/default-messages.d.ts +20 -0
  136. package/dist/localization/default-messages.js +25 -5
  137. package/dist/observability/routes.d.ts +3 -3
  138. package/dist/onboarding/app-profile.js +1 -1
  139. package/dist/progress/routes.d.ts +1 -1
  140. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  141. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  142. package/dist/resources/handlers.d.ts +1 -1
  143. package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
  144. package/dist/secrets/routes.d.ts +9 -9
  145. package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
  146. package/dist/server/agent-chat/run-code-tools.js +3 -1
  147. package/dist/server/agent-chat-plugin.js +6 -6
  148. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  149. package/dist/server/agent-engine-api-key-route.js +43 -12
  150. package/dist/server/agents-bundle.js +2 -1
  151. package/dist/server/auth.js +21 -5
  152. package/dist/server/credential-provider.js +1 -0
  153. package/dist/server/design-token-utils.d.ts +18 -0
  154. package/dist/server/design-token-utils.js +31 -0
  155. package/dist/server/onboarding-html.js +22 -2
  156. package/dist/server/realtime-token.d.ts +1 -1
  157. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  158. package/dist/templates/chat/app/i18n/index.ts +14 -29
  159. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  160. package/dist/templates/default/app/i18n/index.ts +14 -29
  161. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  162. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  163. package/dist/triggers/dispatcher.js +1 -8
  164. package/docs/content/environment-variables.mdx +39 -38
  165. package/package.json +4 -4
  166. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
  167. package/src/templates/chat/app/i18n/index.ts +14 -29
  168. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
  169. package/src/templates/default/app/i18n/index.ts +14 -29
  170. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
  171. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
  172. package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
@@ -0,0 +1,20 @@
1
+ export type AgentProviderId = "anthropic" | "openai" | "openrouter" | "google" | "groq" | "mistral" | "cohere" | "ollama";
2
+ export interface AgentProviderOption {
3
+ id: AgentProviderId;
4
+ engine: string;
5
+ label: string;
6
+ description: string;
7
+ key?: string;
8
+ endpointKey?: string;
9
+ placeholder?: string;
10
+ defaultModel: string;
11
+ supportedModels: readonly string[];
12
+ docsUrl?: string;
13
+ supportsCustomModel?: boolean;
14
+ supportsEndpoint?: boolean;
15
+ endpointPlaceholder?: string;
16
+ kind: "cloud" | "gateway" | "local";
17
+ }
18
+ export declare const AGENT_PROVIDER_CATALOG: readonly AgentProviderOption[];
19
+ export declare function getAgentProviderOption(provider: AgentProviderId): AgentProviderOption;
20
+ export declare function providerIdForEngine(engine: string): AgentProviderId | null;
@@ -0,0 +1,100 @@
1
+ import { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, PROVIDER_ENV_META, } from "../agent/engine/provider-env-vars.js";
2
+ import { AI_SDK_MODEL_CONFIG, ANTHROPIC_MODEL_CONFIG, } from "../agent/model-config.js";
3
+ const PROVIDER_LABELS = {
4
+ anthropic: "Anthropic",
5
+ openai: "OpenAI",
6
+ openrouter: "OpenRouter",
7
+ google: "Google Gemini",
8
+ groq: "Groq",
9
+ mistral: "Mistral",
10
+ cohere: "Cohere",
11
+ ollama: "Ollama",
12
+ };
13
+ const PROVIDER_DESCRIPTIONS = {
14
+ anthropic: "Claude models with your own API key.",
15
+ openai: "GPT models and OpenAI-compatible gateways.",
16
+ openrouter: "One key for hundreds of hosted models.",
17
+ google: "Gemini models with a Google AI key.",
18
+ groq: "Fast inference for open models.",
19
+ mistral: "Mistral models through the Mistral API.",
20
+ cohere: "Command models through the Cohere API.",
21
+ ollama: "Run local models from Ollama on your machine.",
22
+ };
23
+ const PROVIDER_DOCS = {
24
+ anthropic: "https://console.anthropic.com/settings/keys",
25
+ openai: "https://platform.openai.com/api-keys",
26
+ openrouter: "https://openrouter.ai/keys",
27
+ google: "https://aistudio.google.com/apikey",
28
+ groq: "https://console.groq.com/keys",
29
+ mistral: "https://console.mistral.ai/api-keys/",
30
+ cohere: "https://dashboard.cohere.com/api-keys",
31
+ };
32
+ const PROVIDER_ORDER = [
33
+ "openrouter",
34
+ "ollama",
35
+ "anthropic",
36
+ "openai",
37
+ "google",
38
+ "groq",
39
+ "mistral",
40
+ "cohere",
41
+ ];
42
+ function modelConfig(provider) {
43
+ if (provider === "anthropic")
44
+ return ANTHROPIC_MODEL_CONFIG;
45
+ return AI_SDK_MODEL_CONFIG[provider];
46
+ }
47
+ export const AGENT_PROVIDER_CATALOG = PROVIDER_ORDER.map((id) => {
48
+ const config = modelConfig(id);
49
+ const envMeta = PROVIDER_ENV_META[id];
50
+ const isOllama = id === "ollama";
51
+ return {
52
+ id,
53
+ engine: id === "anthropic" ? "anthropic" : `ai-sdk:${id}`,
54
+ label: PROVIDER_LABELS[id],
55
+ description: PROVIDER_DESCRIPTIONS[id],
56
+ ...(envMeta
57
+ ? { key: envMeta.envVar, placeholder: envMeta.placeholder }
58
+ : {}),
59
+ ...(id === "openai"
60
+ ? { endpointKey: OPENAI_BASE_URL_ENV_VAR }
61
+ : isOllama
62
+ ? { endpointKey: OLLAMA_BASE_URL_ENV_VAR }
63
+ : {}),
64
+ defaultModel: config.defaultModel,
65
+ supportedModels: config.supportedModels,
66
+ ...(PROVIDER_DOCS[id] ? { docsUrl: PROVIDER_DOCS[id] } : {}),
67
+ ...(id === "openrouter" || isOllama ? { supportsCustomModel: true } : {}),
68
+ ...(id === "openai" || isOllama
69
+ ? {
70
+ supportsEndpoint: true,
71
+ endpointPlaceholder: isOllama
72
+ ? "http://localhost:11434"
73
+ : "https://gateway.example/v1",
74
+ }
75
+ : {}),
76
+ kind: isOllama
77
+ ? "local"
78
+ : id === "openrouter"
79
+ ? "gateway"
80
+ : "cloud",
81
+ };
82
+ });
83
+ export function getAgentProviderOption(provider) {
84
+ const option = AGENT_PROVIDER_CATALOG.find((item) => item.id === provider);
85
+ if (!option)
86
+ throw new Error(`Unknown AI provider: ${provider}`);
87
+ return option;
88
+ }
89
+ export function providerIdForEngine(engine) {
90
+ if (engine === "anthropic" || engine === "ai-sdk:anthropic") {
91
+ return "anthropic";
92
+ }
93
+ if (engine.startsWith("ai-sdk:")) {
94
+ const provider = engine.slice("ai-sdk:".length);
95
+ if (AGENT_PROVIDER_CATALOG.some((option) => option.id === provider)) {
96
+ return provider;
97
+ }
98
+ }
99
+ return null;
100
+ }
@@ -45,6 +45,7 @@ export { questionFormSchema, questionFormMdx, visualQuestionsSchema, visualQuest
45
45
  export { diagramBlock, DiagramRead, DiagramEdit } from "./library/diagram.js";
46
46
  export { diagramSchema, diagramMdx, type DiagramData, type DiagramNode, type DiagramEdge, type DiagramNote, } from "./library/diagram.config.js";
47
47
  export { wireframeBlock, WireframeBlock, WireframeEditor, } from "./library/wireframe.js";
48
+ export { sanitizeDiagramHtml, sanitizeWireframeCss, sanitizeWireframeHtml, scopeDesignCss, } from "./library/sanitize-html.js";
48
49
  export { wireframeSchema, wireframeMdx, createStableWireframeNodeId, WIREFRAME_SURFACES, WIREFRAME_EL_NAMES, type WireframeData, type WireframeNode, type WireframeElName, type WireframeTone, type WireframeSurface, type WireframeRenderMode, } from "./library/wireframe.config.js";
49
50
  export { Screen, renderNode, renderNodes, hasRenderer, NODE_REGISTRY, KitConfigContext, RoughOverlay, HTML_ROUGH_SELECTOR, useWireframeStyle, setWireframeStyle, toggleWireframeStyle, useIsDark, type WireframeStyle, } from "./library/wireframe-kit.js";
50
51
  export { renderWireframeIconHtml } from "./library/wireframe-icons.js";
@@ -56,6 +56,7 @@ export { questionFormSchema, questionFormMdx, visualQuestionsSchema, visualQuest
56
56
  export { diagramBlock, DiagramRead, DiagramEdit } from "./library/diagram.js";
57
57
  export { diagramSchema, diagramMdx, } from "./library/diagram.config.js";
58
58
  export { wireframeBlock, WireframeBlock, WireframeEditor, } from "./library/wireframe.js";
59
+ export { sanitizeDiagramHtml, sanitizeWireframeCss, sanitizeWireframeHtml, scopeDesignCss, } from "./library/sanitize-html.js";
59
60
  export { wireframeSchema, wireframeMdx, createStableWireframeNodeId, WIREFRAME_SURFACES, WIREFRAME_EL_NAMES, } from "./library/wireframe.config.js";
60
61
  export { Screen, renderNode, renderNodes, hasRenderer, NODE_REGISTRY, KitConfigContext, RoughOverlay, HTML_ROUGH_SELECTOR, useWireframeStyle, setWireframeStyle, toggleWireframeStyle, useIsDark, } from "./library/wireframe-kit.js";
61
62
  export { renderWireframeIconHtml } from "./library/wireframe-icons.js";
@@ -5,11 +5,11 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
5
5
  // by AssistantChatInner.
6
6
  import { IconLoader2, IconCheck, IconCopy, IconX, IconChevronDown, IconGitFork, IconGauge, IconSettings, IconArrowRight, IconAlertTriangle, IconPlayerPlay, IconRefresh, IconPlus, IconClipboardList, } from "@tabler/icons-react";
7
7
  import { useState, useEffect, useCallback, useRef } from "react";
8
- import { saveAgentEngineApiKey, } from "../agent-engine-key.js";
9
8
  import { agentNativePath } from "../api-path.js";
10
9
  import { writeClipboardText } from "../clipboard.js";
11
10
  import { isProviderAuthenticationError } from "../error-format.js";
12
11
  import { useT } from "../i18n.js";
12
+ import { AgentProviderSetupForm } from "../settings/ProviderSetupForm.js";
13
13
  import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
14
14
  import { cn } from "../utils.js";
15
15
  // ─── Metadata extractors ──────────────────────────────────────────────────────
@@ -132,65 +132,21 @@ export function BuilderConnectCta({ variant = "primary", onConnected, }) {
132
132
  return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Connect Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 max-w-[220px]", children: "Free credits for LLM, hosting, and more \u2014 no API key needed" }), error && _jsx("p", { className: "mt-1 text-[10px] text-destructive", children: error })] }), _jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-foreground px-3 py-1.5 text-[11px] font-medium no-underline text-background hover:opacity-90 disabled:opacity-60 disabled:cursor-wait", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Waiting\u2026"] })) : ("Connect") })] }));
133
133
  }
134
134
  // ─── ApiKeyConnect ────────────────────────────────────────────────────────────
135
- const API_KEY_PROVIDERS = [
136
- { value: "anthropic", label: "Anthropic", placeholder: "sk-ant-…" },
137
- { value: "openai", label: "OpenAI", placeholder: "sk-…" },
138
- ];
139
135
  export function ApiKeyConnect({ onConnected }) {
140
- const t = useT();
141
- const [provider, setProvider] = useState("anthropic");
142
- const [apiKey, setApiKey] = useState("");
143
- const [saving, setSaving] = useState(false);
144
- const [error, setError] = useState(null);
145
- const active = API_KEY_PROVIDERS.find((p) => p.value === provider);
146
- const handleSave = useCallback(async () => {
147
- if (!apiKey.trim() || saving)
148
- return;
149
- setSaving(true);
150
- setError(null);
151
- try {
152
- await saveAgentEngineApiKey({ provider, apiKey });
153
- setApiKey("");
154
- onConnected?.();
155
- }
156
- catch (err) {
157
- setError(err instanceof Error ? err.message : "Could not save the key.");
158
- }
159
- finally {
160
- setSaving(false);
161
- }
162
- }, [apiKey, onConnected, provider, saving]);
163
- return (_jsxs("div", { className: "rounded-md border border-border bg-background/60 p-3", children: [_jsx("div", { className: "mb-2 text-[11px] font-medium text-foreground", children: t("agentPanel.addOwnKeys", { defaultValue: "Add your own keys" }) }), _jsx("p", { className: "mb-2.5 text-[11px] leading-relaxed text-muted-foreground", children: "Stored securely for this app only." }), _jsx("div", { role: "tablist", "aria-label": "API key provider", className: "mb-2 inline-flex rounded-md border border-border bg-muted/40 p-0.5", children: API_KEY_PROVIDERS.map((option) => {
164
- const selected = option.value === provider;
165
- return (_jsx("button", { type: "button", role: "tab", "aria-selected": selected, onClick: () => {
166
- setProvider(option.value);
167
- setError(null);
168
- }, className: cn("rounded px-2.5 py-1 text-[11px] font-medium transition-colors", selected
169
- ? "bg-background text-foreground shadow-sm"
170
- : "text-muted-foreground hover:text-foreground"), children: option.label }, option.value));
171
- }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "password", value: apiKey, autoComplete: "off", spellCheck: false, placeholder: active.placeholder, onChange: (e) => {
172
- setApiKey(e.target.value);
173
- if (error)
174
- setError(null);
175
- }, onKeyDown: (e) => {
176
- if (e.key === "Enter") {
177
- e.preventDefault();
178
- void handleSave();
179
- }
180
- }, className: "h-8 min-w-0 flex-1 rounded-md border border-input bg-background px-2.5 text-[12px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background" }), _jsx("button", { type: "button", onClick: handleSave, disabled: !apiKey.trim() || saving, className: "inline-flex h-8 shrink-0 items-center gap-1 rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60", children: saving ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 11, className: "animate-spin" }), "Saving\u2026"] })) : ("Save") })] }), error ? (_jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })) : null] }));
136
+ return (_jsx(AgentProviderSetupForm, { onConnected: () => onConnected?.(), layout: "compact", showTitle: false }));
181
137
  }
182
138
  export function BuilderSetupContent({ onConnected, layout = "default", }) {
183
139
  const t = useT();
184
140
  const [keyOpen, setKeyOpen] = useState(false);
185
141
  const sidebarLayout = layout === "sidebar";
186
142
  return (_jsxs("div", { className: cn("agent-builder-setup-content", sidebarLayout && "agent-builder-setup-card--sidebar"), children: [_jsxs("div", { className: cn("agent-builder-setup-card__content flex flex-col sm:flex-row sm:items-center sm:justify-between", sidebarLayout ? "gap-2" : "gap-3"), children: [_jsxs("div", { className: "agent-builder-setup-card__copy min-w-0", children: [_jsx("h3", { className: "text-[13px] font-medium text-foreground", children: t("agentPanel.connectAi", { defaultValue: "Connect AI" }) }), _jsx("p", { className: "mt-0.5 text-[11px] leading-relaxed text-muted-foreground", children: t("agentPanel.builderOrOwnKeys", {
187
- defaultValue: "Use Builder.io (free credits), or add your own provider keys.",
143
+ defaultValue: "Choose Builder.io or custom keys.",
188
144
  }) })] }), _jsxs("div", { className: cn("agent-builder-setup-card__actions flex shrink-0", sidebarLayout
189
145
  ? "flex-col items-start gap-1 sm:items-center"
190
146
  : "flex-nowrap items-center gap-2"), children: [_jsx(BuilderConnectCta, { variant: "compact", onConnected: onConnected }), _jsx("button", { type: "button", onClick: () => setKeyOpen((open) => !open), className: cn("agent-builder-setup-card__key-button inline-flex shrink-0 items-center whitespace-nowrap rounded-md text-[11px] font-medium", sidebarLayout
191
147
  ? "h-7 border-0 bg-transparent px-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
192
148
  : "h-8 border border-border bg-background px-3 text-foreground hover:bg-accent"), "aria-expanded": keyOpen, children: t("agentPanel.addOwnKeys", {
193
- defaultValue: "Add your own keys",
149
+ defaultValue: "Custom keys",
194
150
  }) })] })] }), keyOpen ? (_jsx("div", { className: "mt-3", children: _jsx(ApiKeyConnect, { onConnected: onConnected }) })) : null] }));
195
151
  }
196
152
  export function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout = "default", }) {
@@ -235,16 +235,17 @@ function browserStorage() {
235
235
  }
236
236
  export function readChatFirstModeState(storage = browserStorage()) {
237
237
  if (!storage) {
238
- return { enabled: false, availability: "unavailable" };
238
+ return { enabled: true, availability: "unavailable" };
239
239
  }
240
240
  try {
241
+ const stored = storage.getItem(CHAT_FIRST_MODE_STORAGE_KEY);
241
242
  return {
242
- enabled: storage.getItem(CHAT_FIRST_MODE_STORAGE_KEY) === "true",
243
+ enabled: stored == null ? true : stored === "true",
243
244
  availability: "available",
244
245
  };
245
246
  }
246
247
  catch {
247
- return { enabled: false, availability: "unavailable" };
248
+ return { enabled: true, availability: "unavailable" };
248
249
  }
249
250
  }
250
251
  export function readChatFirstMode(storage = browserStorage()) {
@@ -3,6 +3,9 @@ import { type LocaleCode, type LocaleMetadata, type LocalePreference, type Local
3
3
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, localeDirection, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, type LocaleCode, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "../localization/shared.js";
4
4
  export { getLocaleInitScript } from "../localization/server.js";
5
5
  export type LocaleMessages = Record<string, unknown>;
6
+ export type AgentNativeI18nLocaleLoader = () => Promise<LocaleMessages | {
7
+ default: LocaleMessages;
8
+ }>;
6
9
  export interface LocaleHydrationPayload {
7
10
  locale?: LocaleCode;
8
11
  preference?: LocalizationPreference;
@@ -21,6 +24,17 @@ export interface AgentNativeI18nCatalog {
21
24
  */
22
25
  supportedLocales?: readonly LocaleCode[];
23
26
  }
27
+ export declare function createAgentNativeI18nCatalog(args: {
28
+ messages: LocaleMessages;
29
+ localeLoaders: Partial<Record<LocaleCode, AgentNativeI18nLocaleLoader>>;
30
+ namespace?: string;
31
+ sourceLocale?: LocaleCode;
32
+ supportedLocales?: readonly LocaleCode[];
33
+ }): AgentNativeI18nCatalog & {
34
+ messages: LocaleMessages;
35
+ sourceLocale: LocaleCode;
36
+ loadMessages: (locale: LocaleCode) => Promise<LocaleMessages | null>;
37
+ };
24
38
  export interface AgentNativeI18nProviderProps {
25
39
  children: React.ReactNode;
26
40
  catalog?: AgentNativeI18nCatalog;
@@ -12,6 +12,24 @@ import { callAction } from "./use-action.js";
12
12
  import { cn } from "./utils.js";
13
13
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, localeDirection, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
14
14
  export { getLocaleInitScript } from "../localization/server.js";
15
+ function isAgentNativeI18nModuleNamespace(value) {
16
+ return Object.prototype.toString.call(value) === "[object Module]";
17
+ }
18
+ export function createAgentNativeI18nCatalog(args) {
19
+ return {
20
+ namespace: args.namespace,
21
+ sourceLocale: args.sourceLocale ?? DEFAULT_LOCALE,
22
+ messages: args.messages,
23
+ supportedLocales: args.supportedLocales,
24
+ loadMessages: async (locale) => {
25
+ const loader = args.localeLoaders[locale];
26
+ if (!loader)
27
+ return null;
28
+ const loaded = await loader();
29
+ return isAgentNativeI18nModuleNamespace(loaded) ? loaded.default : loaded;
30
+ },
31
+ };
32
+ }
15
33
  const LocaleContext = globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ ??
16
34
  (globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ =
17
35
  createContext(null));
@@ -0,0 +1,21 @@
1
+ import { type AgentProviderId, type AgentProviderOption } from "../agent-provider-catalog.js";
2
+ export interface AgentProviderPickerProps {
3
+ value: AgentProviderId;
4
+ onChange: (provider: AgentProviderId) => void;
5
+ options?: readonly AgentProviderOption[];
6
+ configuredProviders?: ReadonlySet<AgentProviderId>;
7
+ disabled?: boolean;
8
+ className?: string;
9
+ layout?: "compact" | "page";
10
+ }
11
+ export declare function AgentProviderPicker({ value, onChange, options, configuredProviders, disabled, className, layout, }: AgentProviderPickerProps): import("react").JSX.Element;
12
+ export interface AgentProviderSetupFormProps {
13
+ initialProvider?: AgentProviderId;
14
+ configuredProviders?: ReadonlySet<AgentProviderId>;
15
+ onConnected?: (provider: AgentProviderId) => void;
16
+ scope?: "user" | "org";
17
+ layout?: "compact" | "page";
18
+ showTitle?: boolean;
19
+ className?: string;
20
+ }
21
+ export declare function AgentProviderSetupForm({ initialProvider, configuredProviders, onConnected, scope, layout, showTitle, className, }: AgentProviderSetupFormProps): import("react").JSX.Element;
@@ -0,0 +1,145 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@agent-native/toolkit/ui/command";
3
+ import { IconBolt, IconCheck, IconChevronDown, IconCloud, IconDeviceDesktop, IconExternalLink, IconKey, IconLoader2, IconRoute, IconServer2, } from "@tabler/icons-react";
4
+ import { useEffect, useState } from "react";
5
+ import { saveAgentEngineProviderSettings, setAgentEngineProvider, } from "../agent-engine-key.js";
6
+ import { AGENT_PROVIDER_CATALOG, getAgentProviderOption, } from "../agent-provider-catalog.js";
7
+ import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
8
+ import { useT } from "../i18n.js";
9
+ import { cn } from "../utils.js";
10
+ function ProviderIcon({ option, size = 15, }) {
11
+ if (option.kind === "local")
12
+ return _jsx(IconDeviceDesktop, { size: size });
13
+ if (option.kind === "gateway")
14
+ return _jsx(IconRoute, { size: size });
15
+ return _jsx(IconCloud, { size: size });
16
+ }
17
+ export function AgentProviderPicker({ value, onChange, options = AGENT_PROVIDER_CATALOG, configuredProviders, disabled = false, className, layout = "compact", }) {
18
+ const t = useT();
19
+ const [open, setOpen] = useState(false);
20
+ const active = getAgentProviderOption(value);
21
+ const isPage = layout === "page";
22
+ return (_jsxs("div", { className: cn("space-y-1.5", className), children: [_jsx("p", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[11px]"), children: t("agentPanel.chooseProvider", {
23
+ defaultValue: "Choose a provider",
24
+ }) }), _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: disabled, "aria-label": t("agentPanel.chooseProvider", {
25
+ defaultValue: "Choose a provider",
26
+ }), className: cn("flex w-full items-center gap-2 rounded-md border border-border bg-background text-start text-foreground transition-colors hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-60", isPage ? "min-h-10 px-3 text-sm" : "min-h-9 px-2.5 text-[12px]"), children: [_jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-accent/50 text-muted-foreground", children: _jsx(ProviderIcon, { option: active, size: isPage ? 16 : 14 }) }), _jsx("span", { className: "min-w-0 flex-1", children: _jsx("span", { className: "block truncate font-medium", children: active.label }) }), _jsx(IconChevronDown, { size: isPage ? 16 : 14, className: "shrink-0 text-muted-foreground" })] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 6, className: "w-[min(390px,calc(100vw-2rem))] p-0", children: _jsxs(Command, { filter: (candidate, search) => candidate.toLowerCase().includes(search.toLowerCase()) ? 1 : 0, children: [_jsx(CommandInput, { placeholder: t("agentPanel.searchProviders", {
27
+ defaultValue: "Search providers...",
28
+ }) }), _jsxs(CommandList, { className: "max-h-[min(440px,calc(100vh-8rem))]", children: [_jsx(CommandEmpty, { children: t("agentPanel.noProvidersFound", {
29
+ defaultValue: "No providers found.",
30
+ }) }), _jsx(CommandGroup, { heading: t("agentPanel.availableProviders", {
31
+ defaultValue: "Available providers",
32
+ }), children: options.map((option) => {
33
+ const configured = configuredProviders?.has(option.id);
34
+ return (_jsxs(CommandItem, { value: `${option.label} ${option.description} ${option.key ?? "local"}`, onSelect: () => {
35
+ onChange(option.id);
36
+ setOpen(false);
37
+ }, className: "items-center gap-2.5 py-2.5", children: [_jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-accent/50 text-muted-foreground", children: _jsx(ProviderIcon, { option: option }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate font-medium", children: option.label }), configured ? (_jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-[10px] font-medium text-primary", children: [_jsx(IconCheck, { size: 11 }), t("agentPanel.configured", {
38
+ defaultValue: "Configured",
39
+ })] })) : null] }), _jsx("span", { className: "block truncate text-[11px] text-muted-foreground", children: option.description })] }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: option.key
40
+ ? t("agentPanel.apiKey", {
41
+ defaultValue: "API key",
42
+ })
43
+ : t("agentPanel.localRuntime", {
44
+ defaultValue: "Local",
45
+ }) }), _jsx(IconCheck, { size: 15, className: cn("shrink-0", option.id === value ? "opacity-100" : "opacity-0") })] }, option.id));
46
+ }) })] })] }) })] })] }));
47
+ }
48
+ export function AgentProviderSetupForm({ initialProvider = "anthropic", configuredProviders, onConnected, scope = "user", layout = "compact", showTitle = true, className, }) {
49
+ const t = useT();
50
+ const isPage = layout === "page";
51
+ const [provider, setProvider] = useState(initialProvider);
52
+ const [apiKey, setApiKey] = useState("");
53
+ const [model, setModel] = useState("");
54
+ const [endpoint, setEndpoint] = useState("");
55
+ const [endpointOpen, setEndpointOpen] = useState(initialProvider === "ollama");
56
+ const [saving, setSaving] = useState(false);
57
+ const [saved, setSaved] = useState(false);
58
+ const [error, setError] = useState(null);
59
+ const active = getAgentProviderOption(provider);
60
+ useEffect(() => {
61
+ setModel(active.defaultModel);
62
+ setApiKey("");
63
+ setEndpoint("");
64
+ setEndpointOpen(provider === "ollama");
65
+ setError(null);
66
+ setSaved(false);
67
+ }, [active.defaultModel, provider]);
68
+ const handleProviderChange = (nextProvider) => {
69
+ setProvider(nextProvider);
70
+ };
71
+ const handleSave = async () => {
72
+ if (saving)
73
+ return;
74
+ if (active.key && !apiKey.trim()) {
75
+ setError(t("agentPanel.enterApiKey", {
76
+ defaultValue: `Enter your ${active.label} API key.`,
77
+ }));
78
+ return;
79
+ }
80
+ setSaving(true);
81
+ setError(null);
82
+ try {
83
+ if (active.key || endpoint.trim()) {
84
+ await saveAgentEngineProviderSettings({
85
+ provider,
86
+ ...(active.key ? { key: active.key } : {}),
87
+ ...(apiKey.trim() ? { apiKey } : {}),
88
+ ...(endpoint.trim() ? { baseUrl: endpoint } : {}),
89
+ scope,
90
+ });
91
+ }
92
+ await setAgentEngineProvider({
93
+ provider,
94
+ model: model.trim() || active.defaultModel,
95
+ });
96
+ setApiKey("");
97
+ setSaved(true);
98
+ onConnected?.(provider);
99
+ window.setTimeout(() => setSaved(false), 2200);
100
+ }
101
+ catch (err) {
102
+ setError(err instanceof Error
103
+ ? err.message
104
+ : t("agentPanel.providerSetupFailed", {
105
+ defaultValue: "Could not configure this provider.",
106
+ }));
107
+ }
108
+ finally {
109
+ setSaving(false);
110
+ }
111
+ };
112
+ const isConfigured = configuredProviders?.has(provider) || saved;
113
+ const modelInputVisible = active.supportsCustomModel;
114
+ const endpointVisible = active.supportsEndpoint;
115
+ return (_jsxs("form", { className: cn("space-y-3", className), onSubmit: (event) => {
116
+ event.preventDefault();
117
+ void handleSave();
118
+ }, children: [showTitle ? (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(IconKey, { size: isPage ? 16 : 13, className: "text-muted-foreground" }), _jsx("h4", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[11px]"), children: t("agentPanel.addOwnKeys", {
119
+ defaultValue: "Custom keys",
120
+ }) })] }), _jsx("p", { className: cn("mt-1 leading-relaxed text-muted-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.configureProviderKeys", {
121
+ defaultValue: "Choose a provider.",
122
+ }) })] })) : null, _jsx(AgentProviderPicker, { value: provider, onChange: handleProviderChange, configuredProviders: configuredProviders, disabled: saving, layout: layout }), _jsxs("div", { className: cn("rounded-md border border-border bg-accent/20", isPage ? "space-y-3 p-3.5" : "space-y-2.5 p-2.5"), children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-md bg-background text-muted-foreground", children: active.kind === "local" ? (_jsx(IconDeviceDesktop, { size: isPage ? 15 : 13 })) : active.kind === "gateway" ? (_jsx(IconRoute, { size: isPage ? 15 : 13 })) : (_jsx(IconBolt, { size: isPage ? 15 : 13 })) }), _jsx("div", { className: "min-w-0 flex-1", title: active.description, children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: cn("font-medium text-foreground", isPage ? "text-sm" : "text-[12px]"), children: active.label }), isConfigured ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] font-medium text-primary", children: [_jsx(IconCheck, { size: 11 }), t("agentPanel.configured", {
123
+ defaultValue: "Configured",
124
+ })] })) : null] }) })] }), active.key ? (_jsxs("label", { className: "block space-y-1.5", children: [_jsx("span", { className: cn("font-medium text-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.apiKey", { defaultValue: "API key" }) }), _jsx("input", { type: "password", value: apiKey, autoComplete: "off", spellCheck: false, placeholder: active.placeholder, disabled: saving, onChange: (event) => {
125
+ setApiKey(event.target.value);
126
+ if (error)
127
+ setError(null);
128
+ }, className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") })] })) : (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-border/70 bg-background/60 px-2.5 py-2", children: [_jsx(IconServer2, { size: isPage ? 15 : 13, className: "mt-0.5 shrink-0 text-muted-foreground" }), _jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground", children: t("agentPanel.noApiKeyNeeded", {
129
+ defaultValue: "No API key required.",
130
+ }) })] })), modelInputVisible ? (_jsxs("label", { className: "block space-y-1.5", children: [_jsx("span", { className: cn("font-medium text-foreground", isPage ? "text-xs" : "text-[11px]"), children: t("agentPanel.modelId", { defaultValue: "Model ID" }) }), _jsx("input", { type: "text", value: model, list: `agent-provider-models-${provider}`, disabled: saving, spellCheck: false, autoComplete: "off", placeholder: active.defaultModel, onChange: (event) => setModel(event.target.value), className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") }), _jsx("datalist", { id: `agent-provider-models-${provider}`, children: active.supportedModels.map((modelOption) => (_jsx("option", { value: modelOption }, modelOption))) })] })) : null, endpointVisible ? (_jsxs("div", { className: "border-t border-border/70 pt-2", children: [_jsxs("button", { type: "button", onClick: () => setEndpointOpen((open) => !open), className: "flex w-full items-center justify-between gap-2 text-start text-[11px] font-medium text-foreground", "aria-expanded": endpointOpen, title: provider === "ollama"
131
+ ? "Defaults to Ollama at http://localhost:11434."
132
+ : "Use for LiteLLM or another OpenAI-compatible gateway.", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx(IconChevronDown, { size: 13, className: cn("text-muted-foreground transition-transform", !endpointOpen && "-rotate-90") }), t("agentPanel.endpointUrl", {
133
+ defaultValue: "Endpoint URL",
134
+ })] }), _jsx("span", { className: "text-[10px] font-normal text-muted-foreground", children: t("agentPanel.optional", { defaultValue: "Optional" }) })] }), endpointOpen ? (_jsx("div", { className: "mt-2 space-y-1.5", children: _jsx("input", { type: "url", value: endpoint, disabled: saving, spellCheck: false, autoComplete: "off", placeholder: active.endpointPlaceholder, onChange: (event) => setEndpoint(event.target.value), className: cn("w-full rounded-md border border-input bg-background text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background placeholder:text-muted-foreground/50", isPage ? "h-10 px-3 text-sm" : "h-8 px-2.5 text-[12px]") }) })) : null] })) : null, _jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-0.5", children: [_jsx("button", { type: "submit", disabled: saving || Boolean(active.key && !apiKey.trim()), className: cn("inline-flex items-center justify-center gap-1.5 rounded-md bg-foreground font-medium text-background transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", isPage ? "h-9 px-3 text-xs" : "h-8 px-3 text-[11px]"), children: saving ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: isPage ? 14 : 11, className: "animate-spin" }), t("agentPanel.savingProvider", {
135
+ defaultValue: "Saving...",
136
+ })] })) : saved ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { size: isPage ? 14 : 11 }), t("agentPanel.providerSaved", {
137
+ defaultValue: "Connected",
138
+ })] })) : provider === "ollama" ? (t("agentPanel.useProvider", {
139
+ provider: active.label,
140
+ defaultValue: `Use ${active.label}`,
141
+ })) : (t("agentPanel.saveAndUseProvider", {
142
+ provider: active.label,
143
+ defaultValue: `Save and use ${active.label}`,
144
+ })) }), active.docsUrl ? (_jsxs("a", { href: active.docsUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[10px] font-medium text-muted-foreground no-underline transition-colors hover:bg-accent/40 hover:text-foreground", children: [t("agentPanel.getApiKey", { defaultValue: "Get an API key" }), _jsx(IconExternalLink, { size: 11 })] })) : null] }), error ? (_jsx("p", { className: "text-[11px] leading-relaxed text-destructive", children: error })) : null] })] }));
145
+ }