@agent-native/core 0.79.21 → 0.79.22
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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/deployment.md +82 -82
- package/corpus/core/docs/content/faq.md +1 -1
- package/corpus/core/docs/content/tracking.md +3 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/openai-compatible-endpoint.ts +25 -0
- package/corpus/core/src/agent/engine/provider-env-vars.ts +2 -0
- package/corpus/core/src/agent/engine/registry.ts +59 -11
- package/corpus/core/src/client/FeedbackButton.tsx +11 -1
- package/corpus/core/src/client/agent-engine-key.ts +57 -10
- package/corpus/core/src/client/analytics.ts +78 -27
- package/corpus/core/src/client/blocks/library/HighlightedCode.tsx +1 -1
- package/corpus/core/src/client/index.ts +2 -0
- package/corpus/core/src/client/session-replay.ts +159 -17
- package/corpus/core/src/client/settings/SettingsPanel.tsx +119 -4
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +5 -0
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +8 -3
- package/corpus/core/src/scripts/agent-engines/test-agent-engine.ts +54 -6
- package/corpus/core/src/server/agent-engine-api-key-route.ts +83 -12
- package/corpus/core/src/server/core-routes-plugin.ts +26 -2
- package/corpus/core/src/styles/blocks.css +23 -7
- package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +22 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +64 -50
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +38 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-sync.ts +89 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +138 -59
- package/corpus/templates/analytics/changelog/2026-06-27-dashboard-charts-drag-smoothly-when-rearranging-panels.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-dashboard-email-reports-still-send-when-the-live-screenshot-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-fixed-dashboard-edits-so-charts-no-longer-briefly-snap-back-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +1 -0
- package/corpus/templates/analytics/changelog/2026-06-27-session-replay-lists-now-show-only-signed-in-recordings-with.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-27-sidebar-loading-placeholders-are-visible-in-light-mode.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/scripts/seed-session-replay.ts +1 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +1 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +120 -29
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/session-replay.ts +158 -46
- package/corpus/templates/assets/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +1 -0
- package/corpus/templates/clips/app/components/capture-install-options.tsx +0 -9
- package/corpus/templates/clips/changelog/2026-06-27-clips-no-longer-shows-desktop-app-prompts-on-mobile-screens.md +1 -0
- package/corpus/templates/clips/changelog/2026-06-27-recorder-download-options-now-show-the-chrome-extension-and-.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-27-recorder-setup-mobile-capture-options.md +1 -0
- package/corpus/templates/clips/chrome-extension/package.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/sentry.ts +17 -1
- package/corpus/templates/clips/desktop/package.json +1 -0
- package/corpus/templates/clips/desktop/scripts/check-tauri-prereqs.mjs +45 -0
- package/corpus/templates/clips/desktop/src/lib/sentry.ts +71 -0
- package/corpus/templates/clips/desktop/src/main.tsx +2 -0
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -1
- package/corpus/templates/clips/desktop/src-tauri/build.rs +29 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/logfile.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/sentry_report.rs +142 -0
- package/corpus/templates/clips/desktop/vite.config.ts +66 -0
- package/corpus/templates/design/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +1 -0
- package/corpus/templates/forms/actions/export-responses.ts +3 -2
- package/corpus/templates/forms/actions/list-responses.ts +2 -1
- package/corpus/templates/forms/actions/response-insights.ts +13 -4
- package/corpus/templates/forms/changelog/2026-06-27-anonymous-feedback-no-longer-shows-internal-agent-native-ano.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +5 -8
- package/corpus/templates/forms/server/lib/integrations.ts +9 -6
- package/corpus/templates/forms/shared/submitter-email.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +1 -1
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +49 -10
- package/corpus/templates/slides/changelog/2026-06-27-mobile-navigation-chrome-and-motion.md +1 -0
- package/dist/agent/engine/openai-compatible-endpoint.d.ts +3 -0
- package/dist/agent/engine/openai-compatible-endpoint.d.ts.map +1 -0
- package/dist/agent/engine/openai-compatible-endpoint.js +23 -0
- package/dist/agent/engine/openai-compatible-endpoint.js.map +1 -0
- package/dist/agent/engine/provider-env-vars.d.ts +1 -0
- package/dist/agent/engine/provider-env-vars.d.ts.map +1 -1
- package/dist/agent/engine/provider-env-vars.js +1 -0
- package/dist/agent/engine/provider-env-vars.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +39 -12
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +6 -1
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/agent-engine-key.d.ts +14 -0
- package/dist/client/agent-engine-key.d.ts.map +1 -1
- package/dist/client/agent-engine-key.js +35 -10
- package/dist/client/agent-engine-key.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +71 -24
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +1 -1
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +115 -17
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +42 -6
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +7 -7
- package/dist/progress/routes.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +4 -0
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -4
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.js +37 -5
- package/dist/scripts/agent-engines/test-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +4 -4
- package/dist/server/agent-engine-api-key-route.d.ts.map +1 -1
- package/dist/server/agent-engine-api-key-route.js +71 -11
- package/dist/server/agent-engine-api-key-route.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/styles/blocks.css +23 -7
- package/docs/content/deployment.md +82 -82
- package/docs/content/faq.md +1 -1
- package/docs/content/tracking.md +3 -3
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
2
2
|
import {
|
|
3
3
|
IconChevronDown,
|
|
4
|
+
IconChevronRight,
|
|
4
5
|
IconCheck,
|
|
5
6
|
IconExternalLink,
|
|
6
7
|
IconBrain,
|
|
@@ -33,7 +34,7 @@ import React, {
|
|
|
33
34
|
} from "react";
|
|
34
35
|
|
|
35
36
|
import { PROVIDER_ENV_PLACEHOLDERS } from "../../agent/engine/provider-env-vars.js";
|
|
36
|
-
import {
|
|
37
|
+
import { saveAgentEngineProviderSettings } from "../agent-engine-key.js";
|
|
37
38
|
import { agentNativePath } from "../api-path.js";
|
|
38
39
|
import { BuilderBMark } from "../builder-mark.js";
|
|
39
40
|
import {
|
|
@@ -619,6 +620,10 @@ function LLMSectionInner({
|
|
|
619
620
|
const [currentModel, setCurrentModel] = useState("");
|
|
620
621
|
const [selectedEngine, setSelectedEngine] = useState("anthropic");
|
|
621
622
|
const [selectedModel, setSelectedModel] = useState("");
|
|
623
|
+
const [baseUrl, setBaseUrl] = useState("");
|
|
624
|
+
const [baseUrlConfigured, setBaseUrlConfigured] = useState(false);
|
|
625
|
+
const [clearBaseUrl, setClearBaseUrl] = useState(false);
|
|
626
|
+
const [advancedOpen, setAdvancedOpen] = useState(false);
|
|
622
627
|
const [applyNote, setApplyNote] = useState(false);
|
|
623
628
|
const [testing, setTesting] = useState(false);
|
|
624
629
|
const [testResult, setTestResult] = useState<
|
|
@@ -651,6 +656,7 @@ function LLMSectionInner({
|
|
|
651
656
|
fetch(agentNativePath("/_agent-native/agent-engine/status"))
|
|
652
657
|
.then((r) => (r.ok ? r.json() : null))
|
|
653
658
|
.then((data) => {
|
|
659
|
+
setBaseUrlConfigured(Boolean(data?.openAiBaseUrlConfigured));
|
|
654
660
|
if (
|
|
655
661
|
data?.configured &&
|
|
656
662
|
typeof data.engine === "string" &&
|
|
@@ -715,6 +721,9 @@ function LLMSectionInner({
|
|
|
715
721
|
|
|
716
722
|
const engineChanged =
|
|
717
723
|
selectedEngine !== currentEngine || selectedModel !== currentModel;
|
|
724
|
+
const isOpenAiEngine = selectedEngine === "ai-sdk:openai";
|
|
725
|
+
const endpointChanged = isOpenAiEngine && (!!baseUrl.trim() || clearBaseUrl);
|
|
726
|
+
const providerSettingsChanged = !!apiKey.trim() || endpointChanged;
|
|
718
727
|
|
|
719
728
|
// Hide the Anthropic-via-AI-SDK alias (redundant with the native entry)
|
|
720
729
|
// and Ollama (no API key to set here). The currently-selected engine is
|
|
@@ -732,12 +741,22 @@ function LLMSectionInner({
|
|
|
732
741
|
).map((m) => ({ value: m, label: friendlyModelName(m) }));
|
|
733
742
|
|
|
734
743
|
const handleSave = async () => {
|
|
735
|
-
if (!
|
|
744
|
+
if (!providerSettingsChanged || !envVar) return;
|
|
736
745
|
setSaving(true);
|
|
737
746
|
try {
|
|
738
|
-
|
|
747
|
+
const nextBaseUrl = isOpenAiEngine ? baseUrl.trim() : "";
|
|
748
|
+
await saveAgentEngineProviderSettings({
|
|
749
|
+
key: envVar,
|
|
750
|
+
...(apiKey.trim() ? { apiKey } : {}),
|
|
751
|
+
...(nextBaseUrl ? { baseUrl: nextBaseUrl } : {}),
|
|
752
|
+
...(isOpenAiEngine && clearBaseUrl ? { clearBaseUrl: true } : {}),
|
|
753
|
+
});
|
|
739
754
|
setSaved(true);
|
|
740
755
|
setApiKey("");
|
|
756
|
+
setBaseUrl("");
|
|
757
|
+
setClearBaseUrl(false);
|
|
758
|
+
if (nextBaseUrl) setBaseUrlConfigured(true);
|
|
759
|
+
if (clearBaseUrl) setBaseUrlConfigured(false);
|
|
741
760
|
refreshSettingsStatus();
|
|
742
761
|
notifyConfigChanged();
|
|
743
762
|
setTimeout(() => setSaved(false), 2000);
|
|
@@ -885,6 +904,9 @@ function LLMSectionInner({
|
|
|
885
904
|
const info = engines.find((e) => e.name === val);
|
|
886
905
|
setSelectedModel(info?.defaultModel ?? "");
|
|
887
906
|
setApiKey("");
|
|
907
|
+
setBaseUrl("");
|
|
908
|
+
setClearBaseUrl(false);
|
|
909
|
+
setAdvancedOpen(false);
|
|
888
910
|
}}
|
|
889
911
|
/>
|
|
890
912
|
|
|
@@ -920,6 +942,99 @@ function LLMSectionInner({
|
|
|
920
942
|
)}
|
|
921
943
|
</div>
|
|
922
944
|
|
|
945
|
+
{isOpenAiEngine && (
|
|
946
|
+
<div className="border-t border-border/70 pt-2">
|
|
947
|
+
<button
|
|
948
|
+
type="button"
|
|
949
|
+
onClick={() => setAdvancedOpen((v) => !v)}
|
|
950
|
+
className="flex w-full cursor-pointer items-center justify-between gap-2 rounded px-0.5 py-1 text-left hover:text-foreground"
|
|
951
|
+
>
|
|
952
|
+
<span className="inline-flex min-w-0 items-center gap-1.5 text-[11px] font-medium text-foreground">
|
|
953
|
+
{advancedOpen ? (
|
|
954
|
+
<IconChevronDown size={12} />
|
|
955
|
+
) : (
|
|
956
|
+
<IconChevronRight
|
|
957
|
+
size={12}
|
|
958
|
+
className="rtl:-scale-x-100"
|
|
959
|
+
/>
|
|
960
|
+
)}
|
|
961
|
+
Advanced
|
|
962
|
+
</span>
|
|
963
|
+
<span className="truncate text-[10px] text-muted-foreground">
|
|
964
|
+
OpenAI-compatible endpoint
|
|
965
|
+
</span>
|
|
966
|
+
</button>
|
|
967
|
+
|
|
968
|
+
{advancedOpen && (
|
|
969
|
+
<div className="mt-1.5 space-y-1.5">
|
|
970
|
+
<div className="flex items-center justify-between gap-2">
|
|
971
|
+
<p className="text-[12px] font-medium text-foreground">
|
|
972
|
+
Endpoint URL
|
|
973
|
+
</p>
|
|
974
|
+
<span className="text-[10px] text-muted-foreground">
|
|
975
|
+
{baseUrlConfigured ? "Configured" : "Optional"}
|
|
976
|
+
</span>
|
|
977
|
+
</div>
|
|
978
|
+
<input
|
|
979
|
+
type="url"
|
|
980
|
+
value={baseUrl}
|
|
981
|
+
onChange={(e) => {
|
|
982
|
+
setBaseUrl(e.target.value);
|
|
983
|
+
if (e.target.value.trim()) setClearBaseUrl(false);
|
|
984
|
+
}}
|
|
985
|
+
onKeyDown={(e) => {
|
|
986
|
+
if (e.key === "Enter") handleSave();
|
|
987
|
+
}}
|
|
988
|
+
placeholder={
|
|
989
|
+
baseUrlConfigured
|
|
990
|
+
? "Leave blank to keep current endpoint"
|
|
991
|
+
: "https://gateway.example/v1"
|
|
992
|
+
}
|
|
993
|
+
disabled={clearBaseUrl}
|
|
994
|
+
spellCheck={false}
|
|
995
|
+
autoComplete="off"
|
|
996
|
+
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"
|
|
997
|
+
style={CONTROL_STYLE}
|
|
998
|
+
/>
|
|
999
|
+
<p className="text-[10px] leading-relaxed text-muted-foreground">
|
|
1000
|
+
Use for LiteLLM or another OpenAI-compatible chat
|
|
1001
|
+
gateway. Leave blank for OpenAI.
|
|
1002
|
+
</p>
|
|
1003
|
+
{baseUrlConfigured && (
|
|
1004
|
+
<label className="flex items-center gap-1.5 text-[10px] text-muted-foreground">
|
|
1005
|
+
<input
|
|
1006
|
+
type="checkbox"
|
|
1007
|
+
checked={clearBaseUrl}
|
|
1008
|
+
onChange={(e) => {
|
|
1009
|
+
setClearBaseUrl(e.target.checked);
|
|
1010
|
+
if (e.target.checked) setBaseUrl("");
|
|
1011
|
+
}}
|
|
1012
|
+
className="h-3 w-3 accent-current"
|
|
1013
|
+
/>
|
|
1014
|
+
Clear saved endpoint override
|
|
1015
|
+
</label>
|
|
1016
|
+
)}
|
|
1017
|
+
{envVar && envConfigured && endpointChanged && (
|
|
1018
|
+
<button
|
|
1019
|
+
type="button"
|
|
1020
|
+
onClick={handleSave}
|
|
1021
|
+
disabled={saving}
|
|
1022
|
+
className="rounded bg-accent px-2.5 py-1 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40"
|
|
1023
|
+
>
|
|
1024
|
+
{saving ? (
|
|
1025
|
+
<IconLoader2 size={10} className="animate-spin" />
|
|
1026
|
+
) : saved ? (
|
|
1027
|
+
<IconCheck size={10} />
|
|
1028
|
+
) : (
|
|
1029
|
+
"Save endpoint"
|
|
1030
|
+
)}
|
|
1031
|
+
</button>
|
|
1032
|
+
)}
|
|
1033
|
+
</div>
|
|
1034
|
+
)}
|
|
1035
|
+
</div>
|
|
1036
|
+
)}
|
|
1037
|
+
|
|
923
1038
|
{envVar && envConfigured ? (
|
|
924
1039
|
<div className="flex items-center gap-1.5 text-[10px] text-green-500">
|
|
925
1040
|
<IconCheck size={10} />
|
|
@@ -939,7 +1054,7 @@ function LLMSectionInner({
|
|
|
939
1054
|
/>
|
|
940
1055
|
<button
|
|
941
1056
|
onClick={handleSave}
|
|
942
|
-
disabled={!
|
|
1057
|
+
disabled={!providerSettingsChanged || saving}
|
|
943
1058
|
className="rounded bg-accent px-2 py-1 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40"
|
|
944
1059
|
>
|
|
945
1060
|
{saving ? (
|
|
@@ -56,6 +56,11 @@ export const tool: ActionTool = {
|
|
|
56
56
|
description:
|
|
57
57
|
"Model ID (e.g. 'gpt-5.5', 'claude-sonnet-4-6', 'gemini-3-1-pro'). Required for \"set-app-default\"; optional for \"set\" and \"test\" where it defaults to the engine's default model.",
|
|
58
58
|
},
|
|
59
|
+
baseUrl: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description:
|
|
62
|
+
'Optional OpenAI-compatible endpoint URL for action="test" with engine="ai-sdk:openai". Saved endpoint settings are used when omitted.',
|
|
63
|
+
},
|
|
59
64
|
appId: {
|
|
60
65
|
type: "string",
|
|
61
66
|
description:
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
listAgentEngines,
|
|
7
7
|
getAgentEngineEntry,
|
|
8
8
|
isAgentEnginePackageInstalled,
|
|
9
|
+
isStoredEngineUsableForRequest,
|
|
9
10
|
normalizeModelForEngine,
|
|
10
11
|
registerBuiltinEngines,
|
|
11
12
|
} from "../../agent/engine/index.js";
|
|
@@ -67,9 +68,13 @@ export async function run(args: Record<string, string>): Promise<string> {
|
|
|
67
68
|
entry.supportedModels.length === 0 ||
|
|
68
69
|
entry.supportedModels.includes(resolvedModel);
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const usable = await isStoredEngineUsableForRequest(
|
|
72
|
+
{ engine: engineName },
|
|
73
|
+
entry,
|
|
74
|
+
);
|
|
75
|
+
if (!usable) {
|
|
76
|
+
const missingEnvVars = entry.requiredEnvVars.join(", ");
|
|
77
|
+
return `Warning: Engine "${engineName}" requires the following credentials which are not configured for this request: ${missingEnvVars}. The engine will fail at runtime without them.`;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
await putSetting("agent-engine", {
|
|
@@ -5,8 +5,18 @@
|
|
|
5
5
|
import {
|
|
6
6
|
getAgentEngineEntry,
|
|
7
7
|
registerBuiltinEngines,
|
|
8
|
+
type AgentEngineEntry,
|
|
8
9
|
} from "../../agent/engine/index.js";
|
|
10
|
+
import {
|
|
11
|
+
normalizeOpenAiBaseUrl,
|
|
12
|
+
OPENAI_BASE_URL_ENV_VAR,
|
|
13
|
+
} from "../../agent/engine/openai-compatible-endpoint.js";
|
|
9
14
|
import type { ActionTool } from "../../agent/types.js";
|
|
15
|
+
import {
|
|
16
|
+
canUseDeployCredentialFallbackForRequest,
|
|
17
|
+
readDeployCredentialEnv,
|
|
18
|
+
resolveSecret,
|
|
19
|
+
} from "../../server/credential-provider.js";
|
|
10
20
|
|
|
11
21
|
export const tool: ActionTool = {
|
|
12
22
|
description:
|
|
@@ -24,11 +34,54 @@ export const tool: ActionTool = {
|
|
|
24
34
|
description:
|
|
25
35
|
"Model to use for the test. Defaults to the engine's default model.",
|
|
26
36
|
},
|
|
37
|
+
baseUrl: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description:
|
|
40
|
+
'Optional OpenAI-compatible endpoint URL to test with "ai-sdk:openai". Uses the saved endpoint when omitted.',
|
|
41
|
+
},
|
|
27
42
|
},
|
|
28
43
|
required: [],
|
|
29
44
|
},
|
|
30
45
|
};
|
|
31
46
|
|
|
47
|
+
async function resolveAgentEngineSecret(
|
|
48
|
+
key: string,
|
|
49
|
+
): Promise<string | undefined> {
|
|
50
|
+
try {
|
|
51
|
+
const value = await resolveSecret(key);
|
|
52
|
+
if (value) return value;
|
|
53
|
+
} catch {
|
|
54
|
+
// Fall through to deploy env when this request is allowed to use it.
|
|
55
|
+
}
|
|
56
|
+
return canUseDeployCredentialFallbackForRequest()
|
|
57
|
+
? readDeployCredentialEnv(key)
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function createEngineConfig(
|
|
62
|
+
entry: AgentEngineEntry,
|
|
63
|
+
args: Record<string, string>,
|
|
64
|
+
): Promise<Record<string, unknown>> {
|
|
65
|
+
const config: Record<string, unknown> = {
|
|
66
|
+
apiKey:
|
|
67
|
+
entry.requiredEnvVars.length > 0
|
|
68
|
+
? await resolveAgentEngineSecret(entry.requiredEnvVars[0])
|
|
69
|
+
: undefined,
|
|
70
|
+
allowEnvFallback: canUseDeployCredentialFallbackForRequest(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (entry.name === "ai-sdk:openai") {
|
|
74
|
+
const rawBaseUrl = args.baseUrl?.trim()
|
|
75
|
+
? args.baseUrl
|
|
76
|
+
: await resolveAgentEngineSecret(OPENAI_BASE_URL_ENV_VAR);
|
|
77
|
+
if (rawBaseUrl) {
|
|
78
|
+
config.baseUrl = normalizeOpenAiBaseUrl(rawBaseUrl);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return config;
|
|
83
|
+
}
|
|
84
|
+
|
|
32
85
|
export async function run(args: Record<string, string>): Promise<string> {
|
|
33
86
|
registerBuiltinEngines();
|
|
34
87
|
|
|
@@ -44,12 +97,7 @@ export async function run(args: Record<string, string>): Promise<string> {
|
|
|
44
97
|
const model = args.model ?? entry.defaultModel;
|
|
45
98
|
|
|
46
99
|
try {
|
|
47
|
-
const engine = entry.create(
|
|
48
|
-
apiKey:
|
|
49
|
-
entry.requiredEnvVars.length > 0
|
|
50
|
-
? process.env[entry.requiredEnvVars[0]]
|
|
51
|
-
: undefined,
|
|
52
|
-
});
|
|
100
|
+
const engine = entry.create(await createEngineConfig(entry, args));
|
|
53
101
|
|
|
54
102
|
const start = Date.now();
|
|
55
103
|
const controller = new AbortController();
|
|
@@ -5,9 +5,13 @@ import {
|
|
|
5
5
|
type H3Event,
|
|
6
6
|
} from "h3";
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { normalizeOpenAiBaseUrl } from "../agent/engine/openai-compatible-endpoint.js";
|
|
9
|
+
import {
|
|
10
|
+
OPENAI_BASE_URL_ENV_VAR,
|
|
11
|
+
PROVIDER_ENV_META,
|
|
12
|
+
} from "../agent/engine/provider-env-vars.js";
|
|
9
13
|
import { getOrgContext } from "../org/context.js";
|
|
10
|
-
import { writeAppSecret } from "../secrets/storage.js";
|
|
14
|
+
import { deleteAppSecret, writeAppSecret } from "../secrets/storage.js";
|
|
11
15
|
import { getSession } from "./auth.js";
|
|
12
16
|
import { readBody } from "./h3-helpers.js";
|
|
13
17
|
|
|
@@ -30,7 +34,9 @@ export function normalizeAgentEngineApiKeyPayload(body: unknown):
|
|
|
30
34
|
| {
|
|
31
35
|
ok: true;
|
|
32
36
|
key: string;
|
|
33
|
-
value
|
|
37
|
+
value?: string;
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
clearBaseUrl: boolean;
|
|
34
40
|
scope: AgentEngineApiKeyScope;
|
|
35
41
|
}
|
|
36
42
|
| { ok: false; statusCode: number; error: string } {
|
|
@@ -40,6 +46,9 @@ export function normalizeAgentEngineApiKeyPayload(body: unknown):
|
|
|
40
46
|
provider?: unknown;
|
|
41
47
|
value?: unknown;
|
|
42
48
|
apiKey?: unknown;
|
|
49
|
+
baseUrl?: unknown;
|
|
50
|
+
endpointUrl?: unknown;
|
|
51
|
+
clearBaseUrl?: unknown;
|
|
43
52
|
scope?: unknown;
|
|
44
53
|
};
|
|
45
54
|
|
|
@@ -63,8 +72,48 @@ export function normalizeAgentEngineApiKeyPayload(body: unknown):
|
|
|
63
72
|
: typeof raw.apiKey === "string"
|
|
64
73
|
? raw.apiKey.trim()
|
|
65
74
|
: "";
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
|
|
76
|
+
const rawBaseUrl =
|
|
77
|
+
typeof raw.baseUrl === "string"
|
|
78
|
+
? raw.baseUrl
|
|
79
|
+
: typeof raw.endpointUrl === "string"
|
|
80
|
+
? raw.endpointUrl
|
|
81
|
+
: "";
|
|
82
|
+
let baseUrl: string | undefined;
|
|
83
|
+
if (rawBaseUrl.trim()) {
|
|
84
|
+
if (key !== PROVIDER_TO_ENV_VAR.get("openai")) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
statusCode: 400,
|
|
88
|
+
error: "Endpoint URL is only supported for OpenAI.",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
baseUrl = normalizeOpenAiBaseUrl(rawBaseUrl);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
statusCode: 400,
|
|
97
|
+
error: err instanceof Error ? err.message : String(err),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const clearBaseUrl = raw.clearBaseUrl === true && baseUrl == null;
|
|
103
|
+
if (clearBaseUrl && key !== PROVIDER_TO_ENV_VAR.get("openai")) {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
statusCode: 400,
|
|
107
|
+
error: "Endpoint URL is only supported for OpenAI.",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!value && !baseUrl && !clearBaseUrl) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
statusCode: 400,
|
|
115
|
+
error: "value or baseUrl is required",
|
|
116
|
+
};
|
|
68
117
|
}
|
|
69
118
|
|
|
70
119
|
if (raw.scope != null && raw.scope !== "user" && raw.scope !== "org") {
|
|
@@ -78,7 +127,9 @@ export function normalizeAgentEngineApiKeyPayload(body: unknown):
|
|
|
78
127
|
return {
|
|
79
128
|
ok: true,
|
|
80
129
|
key,
|
|
81
|
-
value,
|
|
130
|
+
...(value ? { value } : {}),
|
|
131
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
132
|
+
clearBaseUrl,
|
|
82
133
|
scope: raw.scope === "org" ? "org" : "user",
|
|
83
134
|
};
|
|
84
135
|
}
|
|
@@ -144,16 +195,36 @@ export function createAgentEngineApiKeyHandler() {
|
|
|
144
195
|
return { error: resolved.error };
|
|
145
196
|
}
|
|
146
197
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
198
|
+
if (payload.value) {
|
|
199
|
+
await writeAppSecret({
|
|
200
|
+
key: payload.key,
|
|
201
|
+
value: payload.value,
|
|
202
|
+
scope: resolved.target.scope,
|
|
203
|
+
scopeId: resolved.target.scopeId,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (payload.baseUrl) {
|
|
208
|
+
await writeAppSecret({
|
|
209
|
+
key: OPENAI_BASE_URL_ENV_VAR,
|
|
210
|
+
value: payload.baseUrl,
|
|
211
|
+
scope: resolved.target.scope,
|
|
212
|
+
scopeId: resolved.target.scopeId,
|
|
213
|
+
});
|
|
214
|
+
} else if (payload.clearBaseUrl) {
|
|
215
|
+
await deleteAppSecret({
|
|
216
|
+
key: OPENAI_BASE_URL_ENV_VAR,
|
|
217
|
+
scope: resolved.target.scope,
|
|
218
|
+
scopeId: resolved.target.scopeId,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
153
221
|
|
|
154
222
|
return {
|
|
155
223
|
ok: true,
|
|
156
224
|
key: payload.key,
|
|
225
|
+
...(payload.baseUrl || payload.clearBaseUrl
|
|
226
|
+
? { baseUrlKey: OPENAI_BASE_URL_ENV_VAR }
|
|
227
|
+
: {}),
|
|
157
228
|
scope: resolved.target.scope,
|
|
158
229
|
};
|
|
159
230
|
});
|
|
@@ -14,7 +14,10 @@ import { readMultipartFormData } from "h3";
|
|
|
14
14
|
|
|
15
15
|
import { DEFAULT_MODEL } from "../agent/default-model.js";
|
|
16
16
|
import { registerBuiltinEngines } from "../agent/engine/builtin.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
OPENAI_BASE_URL_ENV_VAR,
|
|
19
|
+
PROVIDER_ENV_META,
|
|
20
|
+
} from "../agent/engine/provider-env-vars.js";
|
|
18
21
|
import {
|
|
19
22
|
isAgentEngineSettingConfigured,
|
|
20
23
|
getAgentEngineEntry,
|
|
@@ -123,6 +126,7 @@ import {
|
|
|
123
126
|
import type { EnvKeyConfig } from "./create-server.js";
|
|
124
127
|
import {
|
|
125
128
|
canUseDeployCredentialFallbackForRequest,
|
|
129
|
+
readDeployCredentialEnv,
|
|
126
130
|
resolveSecret,
|
|
127
131
|
} from "./credential-provider.js";
|
|
128
132
|
import { createEmbedStartRouteHandler } from "./embed-route.js";
|
|
@@ -2311,6 +2315,20 @@ export function createCoreRoutesPlugin(
|
|
|
2311
2315
|
/* org module not present in this template */
|
|
2312
2316
|
}
|
|
2313
2317
|
}
|
|
2318
|
+
const openAiBaseUrlConfigured = await runWithRequestContext(
|
|
2319
|
+
{ userEmail, orgId },
|
|
2320
|
+
async () => {
|
|
2321
|
+
try {
|
|
2322
|
+
if (await resolveSecret(OPENAI_BASE_URL_ENV_VAR)) return true;
|
|
2323
|
+
} catch {
|
|
2324
|
+
/* fall through to deployment env when allowed */
|
|
2325
|
+
}
|
|
2326
|
+
return (
|
|
2327
|
+
canUseDeployCredentialFallbackForRequest() &&
|
|
2328
|
+
!!readDeployCredentialEnv(OPENAI_BASE_URL_ENV_VAR)
|
|
2329
|
+
);
|
|
2330
|
+
},
|
|
2331
|
+
);
|
|
2314
2332
|
const stored = (await getSetting("agent-engine")) as {
|
|
2315
2333
|
engine?: string;
|
|
2316
2334
|
model?: string;
|
|
@@ -2323,6 +2341,7 @@ export function createCoreRoutesPlugin(
|
|
|
2323
2341
|
engine,
|
|
2324
2342
|
model: stored?.model ?? entry?.defaultModel ?? DEFAULT_MODEL,
|
|
2325
2343
|
source: "settings" as const,
|
|
2344
|
+
openAiBaseUrlConfigured,
|
|
2326
2345
|
};
|
|
2327
2346
|
}
|
|
2328
2347
|
const envEntry = process.env.AGENT_ENGINE
|
|
@@ -2338,7 +2357,7 @@ export function createCoreRoutesPlugin(
|
|
|
2338
2357
|
),
|
|
2339
2358
|
);
|
|
2340
2359
|
if (!envUsable) {
|
|
2341
|
-
return { configured: false };
|
|
2360
|
+
return { configured: false, openAiBaseUrlConfigured };
|
|
2342
2361
|
}
|
|
2343
2362
|
return {
|
|
2344
2363
|
configured: true,
|
|
@@ -2346,6 +2365,7 @@ export function createCoreRoutesPlugin(
|
|
|
2346
2365
|
model: envEntry.defaultModel ?? DEFAULT_MODEL,
|
|
2347
2366
|
source: "env" as const,
|
|
2348
2367
|
envVar: "AGENT_ENGINE",
|
|
2368
|
+
openAiBaseUrlConfigured,
|
|
2349
2369
|
};
|
|
2350
2370
|
}
|
|
2351
2371
|
// Per-user app_secrets — a user who connected Builder (or pasted
|
|
@@ -2363,6 +2383,7 @@ export function createCoreRoutesPlugin(
|
|
|
2363
2383
|
model: detectedFromUser.defaultModel ?? DEFAULT_MODEL,
|
|
2364
2384
|
source: "app_secrets" as const,
|
|
2365
2385
|
envVar: detectedFromUser.requiredEnvVars[0],
|
|
2386
|
+
openAiBaseUrlConfigured,
|
|
2366
2387
|
};
|
|
2367
2388
|
}
|
|
2368
2389
|
if (stored && typeof stored.engine === "string") {
|
|
@@ -2379,6 +2400,7 @@ export function createCoreRoutesPlugin(
|
|
|
2379
2400
|
model: stored.model ?? entry.defaultModel ?? DEFAULT_MODEL,
|
|
2380
2401
|
source: "env" as const,
|
|
2381
2402
|
envVar: entry.requiredEnvVars[0],
|
|
2403
|
+
openAiBaseUrlConfigured,
|
|
2382
2404
|
};
|
|
2383
2405
|
}
|
|
2384
2406
|
}
|
|
@@ -2389,6 +2411,7 @@ export function createCoreRoutesPlugin(
|
|
|
2389
2411
|
model: detectedFromUser.defaultModel ?? DEFAULT_MODEL,
|
|
2390
2412
|
source: "app_secrets" as const,
|
|
2391
2413
|
envVar: detectedFromUser.requiredEnvVars[0],
|
|
2414
|
+
openAiBaseUrlConfigured,
|
|
2392
2415
|
};
|
|
2393
2416
|
}
|
|
2394
2417
|
const canUseDeployEnv = await runWithRequestContext(
|
|
@@ -2403,6 +2426,7 @@ export function createCoreRoutesPlugin(
|
|
|
2403
2426
|
model: detected.defaultModel ?? DEFAULT_MODEL,
|
|
2404
2427
|
source: "env" as const,
|
|
2405
2428
|
envVar: detected.requiredEnvVars[0],
|
|
2429
|
+
openAiBaseUrlConfigured,
|
|
2406
2430
|
};
|
|
2407
2431
|
}
|
|
2408
2432
|
} catch {}
|
|
@@ -1231,6 +1231,7 @@
|
|
|
1231
1231
|
--wf-muted: var(--plan-muted, hsl(var(--muted-foreground)));
|
|
1232
1232
|
--wf-line: var(--plan-line, hsl(var(--border)));
|
|
1233
1233
|
--wf-sketch: var(--plan-muted-line, hsl(var(--border)));
|
|
1234
|
+
--wf-diagram-line: var(--wf-line);
|
|
1234
1235
|
--wf-paper: var(--plan-document, hsl(var(--background)));
|
|
1235
1236
|
--wf-card: var(--plan-block, hsl(var(--card)));
|
|
1236
1237
|
--wf-accent: hsl(var(--ring));
|
|
@@ -1256,6 +1257,10 @@
|
|
|
1256
1257
|
line-height: 1.4;
|
|
1257
1258
|
}
|
|
1258
1259
|
|
|
1260
|
+
.plan-diagram-frame[data-theme="dark"] {
|
|
1261
|
+
--wf-diagram-line: color-mix(in srgb, var(--wf-sketch) 72%, var(--wf-line));
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1259
1264
|
.plan-diagram-frame[data-style="sketchy"] {
|
|
1260
1265
|
--wf-font: var(--wf-font-hand);
|
|
1261
1266
|
line-height: 1.32;
|
|
@@ -1275,9 +1280,7 @@
|
|
|
1275
1280
|
}
|
|
1276
1281
|
|
|
1277
1282
|
.plan-diagram-frame-content
|
|
1278
|
-
>
|
|
1279
|
-
[data-diagram-frame]
|
|
1280
|
-
) {
|
|
1283
|
+
> .diagram-panel:not([data-rough]):not([data-diagram-frame]) {
|
|
1281
1284
|
--rough-skip: 1;
|
|
1282
1285
|
background: transparent;
|
|
1283
1286
|
border-color: transparent;
|
|
@@ -1318,7 +1321,8 @@
|
|
|
1318
1321
|
|
|
1319
1322
|
.plan-diagram-frame
|
|
1320
1323
|
:is(.diagram-panel, .diagram-card, .diagram-node, .diagram-box) {
|
|
1321
|
-
|
|
1324
|
+
--rough-stroke: var(--wf-diagram-line);
|
|
1325
|
+
border: 1.25px solid var(--wf-diagram-line);
|
|
1322
1326
|
border-radius: var(--wf-radius);
|
|
1323
1327
|
background: var(--wf-card);
|
|
1324
1328
|
}
|
|
@@ -1333,7 +1337,8 @@
|
|
|
1333
1337
|
[class*="panel"],
|
|
1334
1338
|
[class*="box"]
|
|
1335
1339
|
) {
|
|
1336
|
-
|
|
1340
|
+
--rough-stroke: var(--wf-diagram-line);
|
|
1341
|
+
border-color: var(--wf-diagram-line) !important;
|
|
1337
1342
|
background: var(--wf-card) !important;
|
|
1338
1343
|
color: var(--wf-ink) !important;
|
|
1339
1344
|
}
|
|
@@ -1366,7 +1371,8 @@
|
|
|
1366
1371
|
|
|
1367
1372
|
.plan-diagram-frame
|
|
1368
1373
|
:is(.diagram-pill, [class*="pill"], [class*="badge"], [class*="chip"]) {
|
|
1369
|
-
|
|
1374
|
+
--rough-stroke: var(--wf-diagram-line);
|
|
1375
|
+
border-color: var(--wf-diagram-line) !important;
|
|
1370
1376
|
background: var(--wf-paper) !important;
|
|
1371
1377
|
color: var(--wf-ink) !important;
|
|
1372
1378
|
}
|
|
@@ -1411,7 +1417,7 @@
|
|
|
1411
1417
|
* variant used across the docs diagrams stays pill-shaped, not a full bar. */
|
|
1412
1418
|
width: fit-content;
|
|
1413
1419
|
max-width: 100%;
|
|
1414
|
-
border: 1px solid var(--wf-line);
|
|
1420
|
+
border: 1px solid var(--wf-diagram-line);
|
|
1415
1421
|
border-radius: 999px;
|
|
1416
1422
|
padding: 3px 11px;
|
|
1417
1423
|
color: var(--wf-ink);
|
|
@@ -1453,6 +1459,16 @@
|
|
|
1453
1459
|
[data-rough],
|
|
1454
1460
|
hr
|
|
1455
1461
|
) {
|
|
1462
|
+
border-color: color-mix(
|
|
1463
|
+
in srgb,
|
|
1464
|
+
var(--wf-diagram-line) 54%,
|
|
1465
|
+
transparent
|
|
1466
|
+
) !important;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.plan-diagram-frame[data-rough-ready]
|
|
1470
|
+
.plan-diagram-frame-content
|
|
1471
|
+
> .diagram-panel:not([data-rough]):not([data-diagram-frame]) {
|
|
1456
1472
|
border-color: transparent !important;
|
|
1457
1473
|
}
|
|
1458
1474
|
|
|
@@ -26,7 +26,7 @@ export default defineAction({
|
|
|
26
26
|
app: z.string().optional().describe("Optional app filter"),
|
|
27
27
|
template: z.string().optional().describe("Optional template filter"),
|
|
28
28
|
sessionId: z.string().optional().describe("Optional analytics session id"),
|
|
29
|
-
userId: z.string().optional().describe("Optional signed-in user
|
|
29
|
+
userId: z.string().optional().describe("Optional signed-in user email"),
|
|
30
30
|
anonymousId: z.string().optional().describe("Optional anonymous user id"),
|
|
31
31
|
path: z.string().optional().describe("Optional exact path filter"),
|
|
32
32
|
from: z
|