@agent-native/core 0.185.0 → 0.186.0-nightly-20260923174435

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 (63) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/actions/delete-event.ts +6 -4
  3. package/corpus/templates/calendar/actions/delete-events.ts +10 -11
  4. package/corpus/templates/calendar/actions/event-action-helpers.ts +53 -0
  5. package/corpus/templates/calendar/actions/get-event.ts +59 -14
  6. package/corpus/templates/calendar/actions/rsvp-event.ts +5 -3
  7. package/corpus/templates/calendar/actions/update-event.ts +15 -7
  8. package/corpus/templates/calendar/actions/update-events.ts +29 -11
  9. package/corpus/templates/calendar/changelog/2026-09-21-events-stay-editable-when-their-calendar-is-connected-throug.md +6 -0
  10. package/corpus/templates/calendar/server/lib/google-calendar.ts +70 -18
  11. package/corpus/templates/calendar/shared/google-calendar-sources.ts +47 -0
  12. package/corpus/templates/clips/actions/finalize-recording.ts +35 -1
  13. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +5 -1
  14. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +18 -1
  15. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +2 -2
  16. package/corpus/templates/clips/app/routes/record.tsx +1 -0
  17. package/corpus/templates/clips/changelog/2026-09-23-long-recording-menus-scroll-within-the-visible-space-so-ever.md +6 -0
  18. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +14 -4
  19. package/corpus/templates/content/app/blocks/contentBlockRegistry.tsx +9 -1
  20. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +16 -2
  21. package/corpus/templates/content/app/components/editor/registrySlashItems.ts +61 -40
  22. package/corpus/templates/content/app/i18n-data.ts +12 -0
  23. package/corpus/templates/content/app/routes/_app.settings.tsx +36 -9
  24. package/corpus/templates/content/docs/product/capabilities/content.renderer.custom-block.md +1 -1
  25. package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
  26. package/corpus/templates/content/shared/labs.ts +35 -1
  27. package/corpus/templates/slides/actions/get-design-system.ts +4 -2
  28. package/dist/agent/engine/openai-compatible-endpoint.d.ts +7 -0
  29. package/dist/agent/engine/openai-compatible-endpoint.js +9 -0
  30. package/dist/agent/engine/provider-endpoint-validation.d.ts +8 -1
  31. package/dist/agent/engine/provider-endpoint-validation.js +47 -10
  32. package/dist/agent/engine/registry.js +8 -5
  33. package/dist/automation/index.d.ts +1 -1
  34. package/dist/cli/skills.d.ts +3 -0
  35. package/dist/cli/skills.js +45 -9
  36. package/dist/client/agent-engine-key.d.ts +7 -0
  37. package/dist/client/agent-engine-key.js +37 -0
  38. package/dist/client/agent-provider-catalog.js +1 -1
  39. package/dist/client/settings/ProviderSetupForm.js +80 -7
  40. package/dist/client/settings/SettingsPanel.js +149 -11
  41. package/dist/client/use-chat-models.js +31 -0
  42. package/dist/collab/awareness.d.ts +2 -2
  43. package/dist/collab/routes.d.ts +1 -1
  44. package/dist/labs/actions/set-lab.js +1 -0
  45. package/dist/labs/registry.js +6 -1
  46. package/dist/localization/default-messages.d.ts +7 -1
  47. package/dist/localization/default-messages.js +7 -1
  48. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  49. package/dist/notifications/routes.d.ts +3 -3
  50. package/dist/observability/routes.d.ts +3 -3
  51. package/dist/org/index.d.ts +1 -1
  52. package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
  53. package/dist/provider-api/actions/provider-api.d.ts +3 -3
  54. package/dist/scripts/agent-engines/test-agent-engine.js +9 -7
  55. package/dist/secrets/routes.d.ts +3 -3
  56. package/dist/server/agent-engine-api-key-route.d.ts +3 -3
  57. package/dist/server/agent-engine-api-key-route.js +8 -5
  58. package/dist/server/agent-engine-ollama-models-route.d.ts +15 -0
  59. package/dist/server/agent-engine-ollama-models-route.js +105 -0
  60. package/dist/server/core-routes-plugin.js +5 -0
  61. package/dist/server/credential-provider.d.ts +13 -0
  62. package/dist/server/credential-provider.js +19 -0
  63. package/package.json +3 -3
@@ -2,13 +2,13 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { Checkbox, Picker, Skeleton, TextField, } from "@agent-native/toolkit/design-system";
3
3
  import { Button as ToolkitButton } from "@agent-native/toolkit/ui/button";
4
4
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@agent-native/toolkit/ui/command";
5
- import { IconChevronDown, IconChevronRight, IconCheck, IconExternalLink, IconBrain, IconHierarchy2, IconBrowser, IconGitBranch, IconCloud, IconDatabase, IconFolder, IconShield, IconPlugConnected, IconTopologyRing2, IconLoader2, IconUpload, IconCoin, IconMail, IconKey, IconMicrophone, IconEyeOff, IconBolt, IconGauge, IconApps, IconUsersGroup, IconTool, IconAlertCircle, } from "@tabler/icons-react";
5
+ import { IconChevronDown, IconChevronRight, IconCheck, IconExternalLink, IconBrain, IconHierarchy2, IconBrowser, IconGitBranch, IconCloud, IconDatabase, IconFolder, IconShield, IconPlugConnected, IconTopologyRing2, IconLoader2, IconUpload, IconCoin, IconMail, IconKey, IconMicrophone, IconEyeOff, IconBolt, IconGauge, IconApps, IconUsersGroup, IconTool, IconAlertCircle, IconSearch, } from "@tabler/icons-react";
6
6
  import React, { Suspense, lazy, useState, useEffect, useCallback, useMemo, useRef, } from "react";
7
7
  import { CHATGPT_SUBSCRIPTION_DEFAULT_MODEL, CHATGPT_SUBSCRIPTION_ENGINE_NAME, CHATGPT_SUBSCRIPTION_LAB_KEY, } from "../../agent/chatgpt-subscription-contract.js";
8
8
  import { PROVIDER_ENV_PLACEHOLDERS } from "../../agent/engine/provider-env-vars.js";
9
9
  import { buildSettingsRoute, STANDARD_APP_ROUTES, } from "../../navigation/index.js";
10
10
  import { docsUrl } from "../../shared/docs-url.js";
11
- import { saveAgentEngineProviderSettings, setAgentEngineProvider, } from "../agent-engine-key.js";
11
+ import { fetchOllamaModels, saveAgentEngineProviderSettings, setAgentEngineProvider, } from "../agent-engine-key.js";
12
12
  import { AgentWorkspaceContent } from "../agent-page/AgentWorkspaceContent.js";
13
13
  import { AGENT_PROVIDER_CATALOG, getAgentProviderOption, providerIdForEngine, } from "../agent-provider-catalog.js";
14
14
  import { agentNativePath, appMountedPath } from "../api-path.js";
@@ -393,6 +393,9 @@ function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable,
393
393
  const [engineCatalogAvailable, setEngineCatalogAvailable] = useState(false);
394
394
  const [statusProbeAvailable, setStatusProbeAvailable] = useState(false);
395
395
  const probeGenerationRef = useRef({ env: 0, status: 0 });
396
+ const [ollamaModels, setOllamaModels] = useState(null);
397
+ const [ollamaModelsError, setOllamaModelsError] = useState(null);
398
+ const [ollamaModelsLoading, setOllamaModelsLoading] = useState(false);
396
399
  const initialLoading = !enginesLoaded || !!builderLoading;
397
400
  const refreshEnvKeys = useCallback(() => {
398
401
  const generation = ++probeGenerationRef.current.env;
@@ -576,8 +579,47 @@ function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable,
576
579
  const manualSetupHint = "Select a provider.";
577
580
  const engineChanged = selectedEngine !== currentEngine || selectedModel !== currentModel;
578
581
  const isEndpointProvider = selectedProviderOption.supportsEndpoint === true;
582
+ const isOllama = selectedProvider === "ollama";
579
583
  const endpointChanged = isEndpointProvider && (!!baseUrl.trim() || clearBaseUrl);
580
584
  const providerSettingsChanged = !!apiKey.trim() || endpointChanged;
585
+ // Ask the Ollama server itself which models it has pulled, instead of only
586
+ // offering the static suggestion list. Triggered explicitly by the "Find
587
+ // models" button, mirroring the same flow in `AgentProviderSetupForm`.
588
+ const handleFindOllamaModels = () => {
589
+ setOllamaModelsLoading(true);
590
+ setOllamaModelsError(null);
591
+ const typedEndpoint = baseUrl.trim();
592
+ void fetchOllamaModels(typedEndpoint || undefined)
593
+ .then(async (models) => {
594
+ setOllamaModels(models);
595
+ setOllamaModelsError(null);
596
+ // A successful check is the only signal this address actually works;
597
+ // persist it immediately so other surfaces reading the saved Ollama
598
+ // endpoint (the chat composer's model picker) don't keep falling back
599
+ // to the localhost default until "Save" is also clicked.
600
+ if (typedEndpoint) {
601
+ try {
602
+ await saveAgentEngineProviderSettings({
603
+ provider: selectedProvider,
604
+ ...(envVar ? { key: envVar } : {}),
605
+ baseUrl: typedEndpoint,
606
+ });
607
+ setBaseUrlConfigured(true);
608
+ }
609
+ catch {
610
+ // coercion-ok: the connectivity check itself still succeeded and
611
+ // the found models are shown; the address just wasn't persisted
612
+ // (e.g. a dropped session). The "Save endpoint" button below
613
+ // retries it, so this is never reported as a clean success.
614
+ }
615
+ }
616
+ })
617
+ .catch((err) => {
618
+ setOllamaModels(null);
619
+ setOllamaModelsError(err instanceof Error ? err.message : String(err));
620
+ })
621
+ .finally(() => setOllamaModelsLoading(false));
622
+ };
581
623
  const modelOptions = (selectedEngineInfo?.supportedModels ?? []).map((m) => ({ value: m, label: friendlyModelName(m) }));
582
624
  const handleSave = async () => {
583
625
  if (!providerSettingsChanged || (!envVar && !isEndpointProvider))
@@ -735,7 +777,33 @@ function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable,
735
777
  setApplyError(null);
736
778
  setApplyNote(false);
737
779
  setTestResult(null);
738
- } }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("p", { className: fieldLabelClass(isPage), children: "Model" }), _jsx("input", { type: "text", list: `model-suggestions-${selectedEngine}`, value: selectedModel, onChange: (e) => {
780
+ setOllamaModels(null);
781
+ setOllamaModelsError(null);
782
+ } }), isOllama && isEndpointProvider ? (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-[12px] font-medium text-foreground", children: "Endpoint URL" }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: baseUrlConfigured ? "Configured" : "Optional" })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("input", { type: "url", value: baseUrl, onChange: (e) => {
783
+ const baseUrl = e.target.value;
784
+ setSelectionState((previous) => ({
785
+ ...previous,
786
+ baseUrl,
787
+ clearBaseUrl: baseUrl.trim()
788
+ ? false
789
+ : previous.clearBaseUrl,
790
+ }));
791
+ setOllamaModels(null);
792
+ setOllamaModelsError(null);
793
+ }, onKeyDown: (e) => {
794
+ if (e.key === "Enter")
795
+ void handleSave();
796
+ }, placeholder: baseUrlConfigured
797
+ ? "Leave blank to keep current endpoint"
798
+ : "http://localhost:11434 or local network address like http://192.168.1.123:11434", disabled: clearBaseUrl, spellCheck: false, autoComplete: "off", className: "flex h-9 min-w-0 flex-1 rounded-md border border-border bg-background px-3 text-[12px] text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-accent disabled:opacity-50 placeholder:text-muted-foreground/50", style: CONTROL_STYLE }), _jsxs(Button, { type: "button", intent: "neutral", emphasis: "outline", disabled: saving || ollamaModelsLoading || clearBaseUrl, onClick: handleFindOllamaModels, className: "inline-flex h-9 shrink-0 items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground hover:bg-accent/40 disabled:cursor-not-allowed disabled:opacity-50", children: [ollamaModelsLoading ? (_jsx(IconLoader2, { size: 12, className: "animate-spin" })) : (_jsx(IconSearch, { size: 12 })), "Find models"] })] }), baseUrlConfigured && (_jsxs("label", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground", children: [_jsx(Checkbox, { checked: clearBaseUrl, onChange: (checked) => {
799
+ setSelectionState((previous) => ({
800
+ ...previous,
801
+ clearBaseUrl: checked,
802
+ baseUrl: checked ? "" : previous.baseUrl,
803
+ }));
804
+ }, "aria-label": "Clear saved endpoint override", className: "shrink-0" }), "Clear saved endpoint override"] })), endpointChanged && (_jsx(Button, { type: "button", intent: "neutral", emphasis: "solid", onClick: handleSave, disabled: saving, className: "rounded bg-accent px-2.5 py-1 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40", children: saving ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: 10 })) : ("Save endpoint") }))] })) : null, _jsxs("div", { className: "space-y-1.5", children: [_jsx("p", { className: fieldLabelClass(isPage), children: "Model" }), _jsx("input", { type: "text", list: isOllama
805
+ ? undefined
806
+ : `model-suggestions-${selectedEngine}`, value: selectedModel, onChange: (e) => {
739
807
  const model = e.target.value;
740
808
  setSelectionState((previous) => ({
741
809
  ...previous,
@@ -744,9 +812,25 @@ function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable,
744
812
  setApplyError(null);
745
813
  setApplyNote(false);
746
814
  setTestResult(null);
747
- }, placeholder: selectedEngineInfo?.defaultModel ?? "e.g. model-id", spellCheck: false, autoComplete: "off", className: textInputClass(isPage), style: isPage ? CONTROL_STYLE_PAGE : CONTROL_STYLE }), modelOptions.length > 0 && (_jsx("datalist", { id: `model-suggestions-${selectedEngine}`, children: modelOptions.map((opt) => (_jsx("option", { value: opt.value, label: opt.label }, opt.value))) }))] }), isEndpointProvider && (_jsxs("div", { className: "border-t border-border/70 pt-2", children: [_jsxs(Button, { type: "button", onClick: () => setAdvancedOpen((v) => !v), className: "flex w-full cursor-pointer items-center justify-between gap-2 rounded px-0.5 py-1 text-left hover:text-foreground", children: [_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5 text-[11px] font-medium text-foreground", children: [advancedOpen ? (_jsx(IconChevronDown, { size: 12 })) : (_jsx(IconChevronRight, { size: 12, className: "rtl:-scale-x-100" })), "Advanced"] }), _jsx("span", { className: "truncate text-[10px] text-muted-foreground", children: selectedProvider === "ollama"
748
- ? "Local Ollama endpoint"
749
- : "OpenAI-compatible endpoint" })] }), advancedOpen && (_jsxs("div", { className: "mt-1.5 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-[12px] font-medium text-foreground", children: "Endpoint URL" }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: baseUrlConfigured ? "Configured" : "Optional" })] }), _jsx("input", { type: "url", value: baseUrl, onChange: (e) => {
815
+ }, placeholder: selectedEngineInfo?.defaultModel ?? "e.g. model-id", spellCheck: false, autoComplete: "off", className: textInputClass(isPage), style: isPage ? CONTROL_STYLE_PAGE : CONTROL_STYLE }), !isOllama && modelOptions.length > 0 && (_jsx("datalist", { id: `model-suggestions-${selectedEngine}`, children: modelOptions.map((opt) => (_jsx("option", { value: opt.value, label: opt.label }, opt.value))) })), isOllama ? (_jsxs("div", { className: "space-y-1.5", children: [ollamaModels && ollamaModels.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: ollamaModels.map((modelOption) => (_jsx(Button, { type: "button", disabled: saving, onClick: () => {
816
+ setSelectionState((previous) => ({
817
+ ...previous,
818
+ selectedModel: modelOption,
819
+ }));
820
+ setApplyError(null);
821
+ setApplyNote(false);
822
+ setTestResult(null);
823
+ }, "aria-pressed": selectedModel === modelOption, className: cn("rounded-md border px-2 py-1 text-[11px] font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", selectedModel === modelOption
824
+ ? "border-primary bg-primary/10 text-primary"
825
+ : "border-border bg-background text-foreground hover:bg-accent/40"), children: modelOption }, modelOption))) })) : null, _jsx("p", { className: "text-[10px] leading-relaxed text-muted-foreground", children: ollamaModelsLoading
826
+ ? "Checking installed models…"
827
+ : ollamaModels && ollamaModels.length > 0
828
+ ? `Found ${ollamaModels.length} installed model${ollamaModels.length === 1 ? "" : "s"}.`
829
+ : ollamaModels
830
+ ? "Connected, but no models are pulled yet — run `ollama pull llama3.1`."
831
+ : ollamaModelsError
832
+ ? `${ollamaModelsError} Showing example model names below.`
833
+ : 'Click "Find models" above to list what your Ollama server actually has installed.' })] })) : null] }), !isOllama && isEndpointProvider && (_jsxs("div", { className: "border-t border-border/70 pt-2", children: [_jsxs(Button, { type: "button", onClick: () => setAdvancedOpen((v) => !v), className: "flex w-full cursor-pointer items-center justify-between gap-2 rounded px-0.5 py-1 text-left hover:text-foreground", children: [_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5 text-[11px] font-medium text-foreground", children: [advancedOpen ? (_jsx(IconChevronDown, { size: 12 })) : (_jsx(IconChevronRight, { size: 12, className: "rtl:-scale-x-100" })), "Advanced"] }), _jsx("span", { className: "truncate text-[10px] text-muted-foreground", children: "OpenAI-compatible endpoint" })] }), advancedOpen && (_jsxs("div", { className: "mt-1.5 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-[12px] font-medium text-foreground", children: "Endpoint URL" }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: baseUrlConfigured ? "Configured" : "Optional" })] }), _jsx("input", { type: "url", value: baseUrl, onChange: (e) => {
750
834
  const baseUrl = e.target.value;
751
835
  setSelectionState((previous) => ({
752
836
  ...previous,
@@ -760,9 +844,7 @@ function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable,
760
844
  void handleSave();
761
845
  }, placeholder: baseUrlConfigured
762
846
  ? "Leave blank to keep current endpoint"
763
- : selectedProvider === "ollama"
764
- ? "http://localhost:11434"
765
- : "https://gateway.example/v1", disabled: clearBaseUrl, spellCheck: false, autoComplete: "off", className: "flex h-9 w-full rounded-md border border-border bg-background px-3 text-[12px] text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-accent disabled:opacity-50 placeholder:text-muted-foreground/50", style: CONTROL_STYLE }), baseUrlConfigured && (_jsxs("label", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground", children: [_jsx(Checkbox, { checked: clearBaseUrl, onChange: (checked) => {
847
+ : "https://gateway.example/v1", disabled: clearBaseUrl, spellCheck: false, autoComplete: "off", className: "flex h-9 w-full rounded-md border border-border bg-background px-3 text-[12px] text-foreground outline-none transition-colors hover:bg-accent/40 focus:ring-1 focus:ring-accent disabled:opacity-50 placeholder:text-muted-foreground/50", style: CONTROL_STYLE }), baseUrlConfigured && (_jsxs("label", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground", children: [_jsx(Checkbox, { checked: clearBaseUrl, onChange: (checked) => {
766
848
  setSelectionState((previous) => ({
767
849
  ...previous,
768
850
  clearBaseUrl: checked,
@@ -793,7 +875,34 @@ function friendlyAppName(appId) {
793
875
  }
794
876
  function AppDefaultModelPicker({ engines, value, disabled, onChange, }) {
795
877
  const [open, setOpen] = useState(false);
878
+ const [ollamaModels, setOllamaModels] = useState(null);
796
879
  const visibleEngines = engines.filter((engine) => engine.name !== "ai-sdk:anthropic");
880
+ // The static catalog only has the curated suggestion models for Ollama.
881
+ // Ask the configured Ollama server what it actually has installed and
882
+ // swap those in — a second, later render, so it never blocks this
883
+ // picker's first paint on a local network round trip. Gated on the
884
+ // popover actually being opened (a deliberate user action), not merely
885
+ // Ollama's presence in the catalog, which it always has by default —
886
+ // an unconditional probe would 502 for the vast majority of setups that
887
+ // never touched Ollama and never even open this picker.
888
+ useEffect(() => {
889
+ if (!open)
890
+ return;
891
+ if (!engines.some((engine) => engine.name === "ai-sdk:ollama"))
892
+ return;
893
+ let cancelled = false;
894
+ void fetchOllamaModels()
895
+ .then((models) => {
896
+ if (!cancelled && models.length > 0)
897
+ setOllamaModels(models);
898
+ })
899
+ .catch(() => {
900
+ // No local Ollama server reachable — keep the static suggestions.
901
+ });
902
+ return () => {
903
+ cancelled = true;
904
+ };
905
+ }, [open, engines]);
797
906
  const selectedModel = value.includes("::")
798
907
  ? value.slice(value.indexOf("::") + 2)
799
908
  : null;
@@ -817,7 +926,9 @@ function AppDefaultModelPicker({ engines, value, disabled, onChange, }) {
817
926
  const providerLabel = engine.name === "builder"
818
927
  ? "Builder.io"
819
928
  : engine.label || engine.name;
820
- const modelIds = latestModelsOnly(engine.supportedModels);
929
+ const modelIds = engine.name === "ai-sdk:ollama" && ollamaModels?.length
930
+ ? ollamaModels
931
+ : latestModelsOnly(engine.supportedModels);
821
932
  const models = modelIds.length
822
933
  ? modelIds
823
934
  : engine.defaultModel
@@ -842,6 +953,7 @@ function AppModelDefaultsSectionInner({ grouped = false, open, onToggle, }) {
842
953
  const [saving, setSaving] = useState(false);
843
954
  const [saved, setSaved] = useState(false);
844
955
  const [error, setError] = useState(null);
956
+ const [ollamaModels, setOllamaModels] = useState(null);
845
957
  const load = useCallback(() => {
846
958
  let cancelled = false;
847
959
  setLoading(true);
@@ -868,9 +980,35 @@ function AppModelDefaultsSectionInner({ grouped = false, open, onToggle, }) {
868
980
  };
869
981
  }, []);
870
982
  useEffect(() => load(), [load]);
983
+ // The static catalog only has the curated suggestion models for Ollama.
984
+ // Ask the configured Ollama server what it actually has installed and
985
+ // swap those in — a second, later render, so it never blocks this
986
+ // section's first paint on a local network round trip. Gated on Ollama
987
+ // actually being the selected engine here (not merely present in the
988
+ // catalog, which it always is by default) — an unconditional probe would
989
+ // 502 for the vast majority of setups that never touched Ollama.
990
+ useEffect(() => {
991
+ if (selectedEngine !== "ai-sdk:ollama")
992
+ return;
993
+ let cancelled = false;
994
+ void fetchOllamaModels()
995
+ .then((models) => {
996
+ if (!cancelled && models.length > 0)
997
+ setOllamaModels(models);
998
+ })
999
+ .catch(() => {
1000
+ // No local Ollama server reachable — keep the static suggestions.
1001
+ });
1002
+ return () => {
1003
+ cancelled = true;
1004
+ };
1005
+ }, [selectedEngine]);
871
1006
  if (!loading && !settings)
872
1007
  return null;
873
1008
  const selectedEngineInfo = settings?.engines.find((engine) => engine.name === selectedEngine) ?? null;
1009
+ const selectedEngineModels = selectedEngine === "ai-sdk:ollama" && ollamaModels?.length
1010
+ ? ollamaModels
1011
+ : (selectedEngineInfo?.supportedModels ?? []);
874
1012
  const engineOptions = (settings?.engines ?? [])
875
1013
  .filter((engine) => engine.name === selectedEngine || engine.name !== "ai-sdk:anthropic")
876
1014
  .map((engine) => ({
@@ -990,7 +1128,7 @@ function AppModelDefaultsSectionInner({ grouped = false, open, onToggle, }) {
990
1128
  const info = settings.engines.find((engine) => engine.name === value);
991
1129
  setSelectedModel(info?.defaultModel ?? "");
992
1130
  setError(null);
993
- } }), _jsx(AppDefaultModelField, { engine: selectedEngine, models: selectedEngineInfo?.supportedModels ?? [], value: selectedModel, defaultModel: selectedEngineInfo?.defaultModel, disabled: !settings.canUpdate || saving, onValueChange: (value) => {
1131
+ } }), _jsx(AppDefaultModelField, { engine: selectedEngine, models: selectedEngineModels, value: selectedModel, defaultModel: selectedEngineInfo?.defaultModel, disabled: !settings.canUpdate || saving, onValueChange: (value) => {
994
1132
  setSelectedModel(value);
995
1133
  setError(null);
996
1134
  }, onEnter: () => {
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { DEFAULT_MODEL } from "../agent/default-model.js";
3
3
  import { DEFAULT_REASONING_EFFORT, getReasoningEffortOptionsForModel, resolveReasoningEffortSelection, } from "../shared/reasoning-effort.js";
4
+ import { fetchOllamaModels } from "./agent-engine-key.js";
4
5
  import { buildChatModelGroups, } from "./chat-model-groups.js";
5
6
  import { fetchBuilderStatus, fetchEnvironmentStatus, } from "./client-status-requests.js";
6
7
  import { callAction } from "./use-action.js";
@@ -201,6 +202,36 @@ export function useChatModels({ storageKey = DEFAULT_STORAGE_KEY, enabled = true
201
202
  const nextDefaultModel = currentModel ?? DEFAULT_MODEL;
202
203
  setAvailableModels(groups);
203
204
  setDefaultModel(nextDefaultModel);
205
+ // The static catalog only has the curated suggestion models for
206
+ // Ollama. Once the engine list is in, ask the configured Ollama
207
+ // server what it actually has installed and swap those in — a
208
+ // second, later render, so it never blocks the picker's first
209
+ // paint on a local network round trip. Gated on Ollama actually
210
+ // being the current engine (not merely present in the catalog,
211
+ // which it always is): every app registers it by default, so an
212
+ // unconditional probe would 502 on every chat load for the vast
213
+ // majority of setups that never touched Ollama.
214
+ if (currentEngineName === "ai-sdk:ollama") {
215
+ void fetchOllamaModels()
216
+ .then((liveModels) => {
217
+ if (!isCurrentRefresh() || liveModels.length === 0)
218
+ return;
219
+ const liveEngines = enginesData.engines.map((engine) => engine.name === "ai-sdk:ollama"
220
+ ? { ...engine, supportedModels: liveModels }
221
+ : engine);
222
+ setAvailableModels(buildChatModelGroups({
223
+ engines: liveEngines,
224
+ configuredKeys,
225
+ builderConnected,
226
+ currentEngineName,
227
+ currentModel,
228
+ }));
229
+ })
230
+ .catch(() => {
231
+ // No local Ollama server reachable — keep the static
232
+ // suggestion list already rendered above.
233
+ });
234
+ }
204
235
  const selection = selectionRef.current;
205
236
  // Default only to a CONFIGURED group, and to nothing when there is
206
237
  // none. `DEFAULT_MODEL` is a builder-gateway id that no group carries
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  states: {
66
67
  clientId: number;
67
68
  state: string;
68
69
  }[];
69
- error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
+ error?: undefined;
80
81
  users: {
81
82
  clientId: number;
82
83
  lastSeen: number;
83
84
  }[];
84
- error?: undefined;
85
85
  }>>;
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- ok?: undefined;
30
29
  error: string;
30
+ ok?: undefined;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -9,6 +9,7 @@ const schema = z.object({
9
9
  export default defineAction({
10
10
  description: "Opt the current user into or out of one registered lab. Unset preferences use the app-defined default; labs may expose new or unstable features.",
11
11
  schema,
12
+ http: { method: "POST" },
12
13
  run: async (args, ctx) => {
13
14
  const email = ctx?.userEmail;
14
15
  if (!email)
@@ -1,4 +1,9 @@
1
- const registry = new Map();
1
+ const LABS_REGISTRY_SYMBOL = Symbol.for("agent-native.labs.registry");
2
+ const globalLabsRegistry = globalThis;
3
+ // Dev servers can load app plugins from source while action registries resolve
4
+ // the built package. Keep both module instances on one process-wide registry.
5
+ const registry = globalLabsRegistry[LABS_REGISTRY_SYMBOL] ??
6
+ (globalLabsRegistry[LABS_REGISTRY_SYMBOL] = new Map());
2
7
  function normalizeDefinition(definition) {
3
8
  const key = definition.key.trim();
4
9
  if (!/^[A-Za-z][A-Za-z0-9._-]{0,63}$/.test(key)) {
@@ -643,8 +643,14 @@ declare const messages: {
643
643
  modelId: string;
644
644
  endpointUrl: string;
645
645
  optional: string;
646
- ollamaEndpointHint: string;
647
646
  compatibleEndpointHint: string;
647
+ findModels: string;
648
+ ollamaModelsChecking: string;
649
+ ollamaModelsFound_one: string;
650
+ ollamaModelsFound_other: string;
651
+ ollamaModelsNone: string;
652
+ ollamaModelsError: string;
653
+ ollamaModelsPrompt: string;
648
654
  savingProvider: string;
649
655
  providerSaved: string;
650
656
  useProvider: string;
@@ -650,8 +650,14 @@ const messages = {
650
650
  modelId: "Model ID",
651
651
  endpointUrl: "Endpoint URL",
652
652
  optional: "Optional",
653
- ollamaEndpointHint: "Defaults to Ollama at http://localhost:11434.",
654
653
  compatibleEndpointHint: "Use this for LiteLLM or another OpenAI-compatible gateway.",
654
+ findModels: "Find models",
655
+ ollamaModelsChecking: "Checking installed models…",
656
+ ollamaModelsFound_one: "Found {{count}} installed model.",
657
+ ollamaModelsFound_other: "Found {{count}} installed models.",
658
+ ollamaModelsNone: "Connected, but no models are pulled yet — run `ollama pull llama3.1`.",
659
+ ollamaModelsError: "{{error}} Showing example model names below.",
660
+ ollamaModelsPrompt: 'Click "Find models" above to list what your Ollama server actually has installed.',
655
661
  savingProvider: "Saving...",
656
662
  providerSaved: "Connected",
657
663
  useProvider: "Use {{provider}}",
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
137
137
  inputSchema: {
138
138
  type: string;
139
139
  properties: {
140
+ count?: undefined;
140
141
  query?: undefined;
141
142
  minutes?: undefined;
142
143
  limit?: undefined;
143
144
  clientHint?: undefined;
144
145
  timestamp?: undefined;
145
146
  chapterId?: undefined;
146
- count?: undefined;
147
147
  startAt?: undefined;
148
148
  endAt?: undefined;
149
149
  reason?: undefined;
@@ -159,6 +159,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
159
159
  inputSchema: {
160
160
  type: string;
161
161
  properties: {
162
+ count?: undefined;
162
163
  query: {
163
164
  type: string;
164
165
  description: string;
@@ -174,7 +175,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
174
175
  clientHint?: undefined;
175
176
  timestamp?: undefined;
176
177
  chapterId?: undefined;
177
- count?: undefined;
178
178
  startAt?: undefined;
179
179
  endAt?: undefined;
180
180
  reason?: undefined;
@@ -190,6 +190,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
190
190
  inputSchema: {
191
191
  type: string;
192
192
  properties: {
193
+ count?: undefined;
193
194
  minutes: {
194
195
  type: string;
195
196
  description: string;
@@ -199,7 +200,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
199
200
  clientHint?: undefined;
200
201
  timestamp?: undefined;
201
202
  chapterId?: undefined;
202
- count?: undefined;
203
203
  startAt?: undefined;
204
204
  endAt?: undefined;
205
205
  reason?: undefined;
@@ -216,6 +216,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
216
216
  type: string;
217
217
  required: string[];
218
218
  properties: {
219
+ count?: undefined;
219
220
  query: {
220
221
  type: string;
221
222
  description: string;
@@ -234,7 +235,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
234
235
  };
235
236
  timestamp?: undefined;
236
237
  chapterId?: undefined;
237
- count?: undefined;
238
238
  startAt?: undefined;
239
239
  endAt?: undefined;
240
240
  reason?: undefined;
@@ -250,6 +250,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
250
250
  type: string;
251
251
  required: string[];
252
252
  properties: {
253
+ count?: undefined;
253
254
  query?: undefined;
254
255
  minutes?: undefined;
255
256
  limit?: undefined;
@@ -263,7 +264,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
263
264
  description: string;
264
265
  };
265
266
  chapterId?: undefined;
266
- count?: undefined;
267
267
  startAt?: undefined;
268
268
  endAt?: undefined;
269
269
  includeMicrophone?: undefined;
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
314
314
  type: string;
315
315
  required: string[];
316
316
  properties: {
317
+ count?: undefined;
317
318
  query?: undefined;
318
319
  minutes?: undefined;
319
320
  limit?: undefined;
320
321
  clientHint?: undefined;
321
322
  timestamp?: undefined;
322
323
  chapterId?: undefined;
323
- count?: undefined;
324
324
  startAt: {
325
325
  type: string;
326
326
  description: string;
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
349
349
  type: string;
350
350
  required: string[];
351
351
  properties: {
352
+ count?: undefined;
352
353
  query?: undefined;
353
354
  minutes?: undefined;
354
355
  limit?: undefined;
355
356
  clientHint?: undefined;
356
357
  timestamp?: undefined;
357
358
  chapterId?: undefined;
358
- count?: undefined;
359
359
  startAt?: undefined;
360
360
  endAt?: undefined;
361
361
  reason?: undefined;
@@ -11,14 +11,14 @@
11
11
  * DELETE /_agent-native/notifications/:id — delete
12
12
  */
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
+ error?: undefined;
14
15
  count: number;
15
16
  updated?: undefined;
16
- error?: undefined;
17
17
  ok?: undefined;
18
18
  } | {
19
+ error?: undefined;
19
20
  count?: undefined;
20
21
  updated: number;
21
- error?: undefined;
22
22
  ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
26
26
  error: string;
27
27
  ok?: undefined;
28
28
  } | {
29
+ error?: undefined;
29
30
  count?: undefined;
30
31
  updated?: undefined;
31
- error?: undefined;
32
32
  ok: boolean;
33
33
  }>>;
@@ -41,11 +41,11 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ id?: undefined;
44
45
  error?: undefined;
45
46
  ok?: undefined;
46
47
  summary: import("./types.js").TraceSummary;
47
48
  spans: import("./types.js").TraceSpan[];
48
- id?: undefined;
49
49
  } | {
50
50
  error?: undefined;
51
51
  ok?: undefined;
@@ -53,15 +53,15 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
53
53
  spans?: undefined;
54
54
  id: string;
55
55
  } | {
56
+ id?: undefined;
56
57
  ok?: undefined;
57
58
  summary?: undefined;
58
59
  spans?: undefined;
59
- id?: undefined;
60
60
  error: any;
61
61
  } | {
62
+ id?: undefined;
62
63
  error?: undefined;
63
64
  summary?: undefined;
64
65
  spans?: undefined;
65
- id?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -136,8 +136,8 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
136
136
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
137
137
  name?: undefined;
138
138
  success: boolean;
139
- id?: undefined;
140
139
  role?: undefined;
140
+ id?: undefined;
141
141
  } | {
142
142
  success?: undefined;
143
143
  id: string;
@@ -75,8 +75,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- id?: undefined;
79
78
  message?: undefined;
79
+ id?: undefined;
80
80
  deleted?: undefined;
81
81
  providers: {
82
82
  id: string;
@@ -93,20 +93,20 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
93
93
  registered?: undefined;
94
94
  label?: undefined;
95
95
  } | {
96
- id?: undefined;
97
96
  message?: undefined;
98
- count?: undefined;
97
+ id?: undefined;
99
98
  deleted?: undefined;
100
99
  providers?: undefined;
100
+ count?: undefined;
101
101
  found: boolean;
102
102
  provider: import("../custom-registry.js").CustomProviderConfig;
103
103
  registered?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
106
  message?: undefined;
107
- count?: undefined;
108
107
  deleted?: undefined;
109
108
  providers?: undefined;
109
+ count?: undefined;
110
110
  provider?: undefined;
111
111
  found: boolean;
112
112
  id: string;
@@ -114,8 +114,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
114
114
  label?: undefined;
115
115
  } | {
116
116
  message?: undefined;
117
- count?: undefined;
118
117
  providers?: undefined;
118
+ count?: undefined;
119
119
  provider?: undefined;
120
120
  found?: undefined;
121
121
  deleted: boolean;
@@ -123,9 +123,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
123
123
  registered?: undefined;
124
124
  label?: undefined;
125
125
  } | {
126
- count?: undefined;
127
126
  deleted?: undefined;
128
127
  providers?: undefined;
128
+ count?: undefined;
129
129
  provider?: undefined;
130
130
  found?: undefined;
131
131
  registered: boolean;