@agent-native/core 0.172.4 → 0.172.8
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 +1 -1
- package/corpus/templates/analytics/changelog/2026-08-24-chat-draft-preservation.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-24-muted-microphone-start-guidance.md +5 -0
- package/corpus/templates/clips/desktop/src/app.tsx +4 -24
- package/corpus/templates/clips/desktop/src/components/MicOffConfirmation.tsx +3 -20
- package/corpus/templates/clips/desktop/src/styles.css +0 -20
- package/corpus/templates/factory/AGENTS.md +5 -3
- package/corpus/templates/factory/actions/babysit-pull-request.ts +6 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -2
- package/corpus/templates/factory/actions/save-factory-automation.ts +0 -5
- package/corpus/templates/factory/changelog/2026-08-24-slack-checking-works-for-teammate-created-factories.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-24-teammates-can-edit-and-run-factory-jobs.md +6 -0
- package/corpus/templates/factory/server/lib/factory-scope.ts +8 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +1 -9
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +2 -1
- package/dist/automations/service.d.ts +6 -0
- package/dist/automations/service.js +20 -0
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/settings/SettingsPanel.js +80 -33
- package/dist/jobs/run-now.js +2 -2
- package/dist/server/attribution.d.ts +33 -3
- package/dist/server/attribution.js +24 -2
- package/dist/server/auth.js +20 -5
- package/dist/server/better-auth-instance.d.ts +54 -2
- package/dist/server/better-auth-instance.js +94 -46
- package/dist/server/google-oauth.d.ts +9 -0
- package/dist/server/google-oauth.js +7 -5
- package/dist/server/identity-sso.js +4 -0
- package/dist/server/request-context.d.ts +6 -0
- package/dist/tracking/providers.js +3 -0
- package/dist/vite/client.js +5 -2
- package/docs/content/docs-components.mdx +20 -1
- package/docs/content/template-calendar-agent.mdx +63 -26
- package/docs/content/template-calendar-developers.mdx +165 -40
- package/docs/content/template-calendar-features.mdx +276 -0
- package/docs/content/template-calendar-integrations.mdx +78 -0
- package/docs/content/template-calendar.mdx +56 -123
- package/package.json +3 -3
- package/docs/content/template-calendar-booking-links.mdx +0 -73
- package/docs/content/template-calendar-scheduling.mdx +0 -128
package/corpus/README.md
CHANGED
|
@@ -1087,8 +1087,6 @@ export function App({
|
|
|
1087
1087
|
);
|
|
1088
1088
|
const [micOn, setMicOn] = useState<boolean>(() => loadBool(MIC_ON_KEY, true));
|
|
1089
1089
|
const [micOffConfirmOpen, setMicOffConfirmOpen] = useState(false);
|
|
1090
|
-
const pendingStartOptionsRef =
|
|
1091
|
-
useRef<Parameters<typeof handleStartRecording>[0]>(undefined);
|
|
1092
1090
|
const [systemAudioOn, setSystemAudioOn] = useState<boolean>(() =>
|
|
1093
1091
|
loadBool(SYSTEM_AUDIO_KEY, true),
|
|
1094
1092
|
);
|
|
@@ -3591,16 +3589,14 @@ export function App({
|
|
|
3591
3589
|
}, []);
|
|
3592
3590
|
|
|
3593
3591
|
// Gates every start-recording gesture (button, global shortcut, permission
|
|
3594
|
-
// retry) on the mic toggle. When the mic is off we
|
|
3595
|
-
//
|
|
3596
|
-
//
|
|
3597
|
-
// activation handleStartRecording needs, same as the direct gesture would.
|
|
3592
|
+
// retry) on the mic toggle. When the mic is off we show the informational
|
|
3593
|
+
// mic-off screen and wait for the user to go back and change that setting
|
|
3594
|
+
// before the actual getDisplayMedia/getUserMedia call runs.
|
|
3598
3595
|
function beginRecording(
|
|
3599
3596
|
options?: Parameters<typeof handleStartRecording>[0],
|
|
3600
3597
|
beginOptions?: { revealPopoverIfMicOff?: boolean },
|
|
3601
3598
|
) {
|
|
3602
3599
|
if (!micOn) {
|
|
3603
|
-
pendingStartOptionsRef.current = options;
|
|
3604
3600
|
if (beginOptions?.revealPopoverIfMicOff) {
|
|
3605
3601
|
invoke("show_popover").catch(() => {});
|
|
3606
3602
|
}
|
|
@@ -3611,21 +3607,9 @@ export function App({
|
|
|
3611
3607
|
}
|
|
3612
3608
|
|
|
3613
3609
|
function closeMicOffConfirmation() {
|
|
3614
|
-
pendingStartOptionsRef.current = undefined;
|
|
3615
3610
|
setMicOffConfirmOpen(false);
|
|
3616
3611
|
}
|
|
3617
3612
|
|
|
3618
|
-
function unmuteFromConfirmation() {
|
|
3619
|
-
setMicOn(true);
|
|
3620
|
-
closeMicOffConfirmation();
|
|
3621
|
-
}
|
|
3622
|
-
|
|
3623
|
-
function continueWithoutMic() {
|
|
3624
|
-
const options = pendingStartOptionsRef.current;
|
|
3625
|
-
closeMicOffConfirmation();
|
|
3626
|
-
void handleStartRecording(options);
|
|
3627
|
-
}
|
|
3628
|
-
|
|
3629
3613
|
recordShortcutHandlerRef.current = () => {
|
|
3630
3614
|
if (recorder) {
|
|
3631
3615
|
emit("clips:recorder-stop").catch(() => {});
|
|
@@ -4319,11 +4303,7 @@ export function App({
|
|
|
4319
4303
|
return (
|
|
4320
4304
|
<div className="app app-recorder" ref={appRef}>
|
|
4321
4305
|
{micOffConfirmOpen ? (
|
|
4322
|
-
<MicOffConfirmation
|
|
4323
|
-
onBack={closeMicOffConfirmation}
|
|
4324
|
-
onUnmute={unmuteFromConfirmation}
|
|
4325
|
-
onContinue={continueWithoutMic}
|
|
4326
|
-
/>
|
|
4306
|
+
<MicOffConfirmation onBack={closeMicOffConfirmation} />
|
|
4327
4307
|
) : null}
|
|
4328
4308
|
|
|
4329
4309
|
<div
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { IconArrowLeft, IconMicrophone2 } from "@tabler/icons-react";
|
|
2
2
|
|
|
3
|
-
export function MicOffConfirmation({
|
|
4
|
-
onBack,
|
|
5
|
-
onUnmute,
|
|
6
|
-
onContinue,
|
|
7
|
-
}: {
|
|
8
|
-
onBack: () => void;
|
|
9
|
-
onUnmute: () => void;
|
|
10
|
-
onContinue: () => void;
|
|
11
|
-
}) {
|
|
3
|
+
export function MicOffConfirmation({ onBack }: { onBack: () => void }) {
|
|
12
4
|
return (
|
|
13
5
|
<section className="mic-off-confirmation" aria-labelledby="mic-off-title">
|
|
14
6
|
<header className="mic-off-confirmation-header">
|
|
@@ -29,19 +21,10 @@ export function MicOffConfirmation({
|
|
|
29
21
|
</div>
|
|
30
22
|
<h2 id="mic-off-title">Your mic is muted</h2>
|
|
31
23
|
<p>
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
You can change your microphone setting and then come back to start
|
|
25
|
+
recording.
|
|
34
26
|
</p>
|
|
35
27
|
</div>
|
|
36
|
-
|
|
37
|
-
<footer className="mic-off-confirmation-actions">
|
|
38
|
-
<button type="button" className="secondary" onClick={onUnmute}>
|
|
39
|
-
Unmute
|
|
40
|
-
</button>
|
|
41
|
-
<button type="button" className="primary" onClick={onContinue}>
|
|
42
|
-
Continue
|
|
43
|
-
</button>
|
|
44
|
-
</footer>
|
|
45
28
|
</section>
|
|
46
29
|
);
|
|
47
30
|
}
|
|
@@ -2177,26 +2177,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
2177
2177
|
line-height: 1.55;
|
|
2178
2178
|
}
|
|
2179
2179
|
|
|
2180
|
-
.mic-off-confirmation-actions {
|
|
2181
|
-
display: grid;
|
|
2182
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2183
|
-
gap: 10px;
|
|
2184
|
-
padding: 16px 18px 18px;
|
|
2185
|
-
border-top: 1px solid var(--border);
|
|
2186
|
-
background: var(--surface);
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
.mic-off-confirmation-actions .primary,
|
|
2190
|
-
.mic-off-confirmation-actions .secondary {
|
|
2191
|
-
width: auto;
|
|
2192
|
-
min-width: 0;
|
|
2193
|
-
height: 42px;
|
|
2194
|
-
padding: 0 12px;
|
|
2195
|
-
border-radius: var(--radius);
|
|
2196
|
-
font-size: 13px;
|
|
2197
|
-
white-space: nowrap;
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
2180
|
/* ------------------------------------------------------------------------- */
|
|
2201
2181
|
/* Alert dialog */
|
|
2202
2182
|
/* ------------------------------------------------------------------------- */
|
|
@@ -76,9 +76,11 @@ decisions, feedback, agent runs, and provider audit records.
|
|
|
76
76
|
|
|
77
77
|
Rules start in shadow mode; hard guards apply. Organization automations use
|
|
78
78
|
stored prompts; external mutations require durable, idempotent runs and
|
|
79
|
-
provider confirmation.
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
provider confirmation. Poll and Builder/PR dispatch run only as this factory's
|
|
80
|
+
scheduled job, not chat and not a workspace-owner email match; teammates may
|
|
81
|
+
edit and Run now Factory jobs. Use the visual editor for graph changes and
|
|
82
|
+
agent chat for proposals; persist complete graphs with `save-factory-graph`.
|
|
83
|
+
Change rules through triage actions, never graph JSON.
|
|
82
84
|
|
|
83
85
|
## Source Changes
|
|
84
86
|
|
|
@@ -2,6 +2,7 @@ import { defineAction } from "@agent-native/core/action";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import { resolveConnectorSecret } from "../server/connectors/credentials.js";
|
|
5
|
+
import { requireFactoryAutomation } from "../server/lib/require-factory-automation.js";
|
|
5
6
|
import {
|
|
6
7
|
requireWorkspaceMember,
|
|
7
8
|
workspaceMemberIdentityFromContext,
|
|
@@ -106,6 +107,11 @@ export function createBabysitPullRequestAction(
|
|
|
106
107
|
const { userEmail, orgId } = await requireWorkspaceMember(
|
|
107
108
|
workspaceMemberIdentityFromContext(context),
|
|
108
109
|
);
|
|
110
|
+
await requireFactoryAutomation(
|
|
111
|
+
context,
|
|
112
|
+
{ userEmail, orgId },
|
|
113
|
+
"prBabysit",
|
|
114
|
+
);
|
|
109
115
|
const { comments, truncated } = await fetchComments({
|
|
110
116
|
repo: input.repo,
|
|
111
117
|
pullRequestNumber: input.pullRequestNumber,
|
|
@@ -47,7 +47,7 @@ export default defineAction({
|
|
|
47
47
|
factoryId,
|
|
48
48
|
);
|
|
49
49
|
return Promise.all(
|
|
50
|
-
scoped.map(async ({ resource, name, meta, body
|
|
50
|
+
scoped.map(async ({ resource, name, meta, body }) => {
|
|
51
51
|
const runs = await listAutomationRuns({
|
|
52
52
|
owners: [resource.owner],
|
|
53
53
|
automation: automationRunHistoryKey(resource.path),
|
|
@@ -72,7 +72,7 @@ export default defineAction({
|
|
|
72
72
|
Number.isFinite(resource.updatedAt) && resource.updatedAt > 0
|
|
73
73
|
? new Date(resource.updatedAt).toISOString()
|
|
74
74
|
: null,
|
|
75
|
-
canUpdate,
|
|
75
|
+
canUpdate: true,
|
|
76
76
|
runs: runs.filter((run) => run.path === resource.path),
|
|
77
77
|
};
|
|
78
78
|
}),
|
|
@@ -76,11 +76,6 @@ export default defineAction({
|
|
|
76
76
|
"Factory automation id and name do not refer to the same automation.",
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
|
-
if (!definition.canUpdate) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
"Only the automation's creator or an organization admin can update it.",
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
79
|
if (definition.meta.triggerType === "schedule" && !isValidCron(schedule)) {
|
|
85
80
|
throw new Error(`Invalid cron expression "${schedule}".`);
|
|
86
81
|
}
|
|
@@ -352,6 +352,14 @@ export function resolveAutomationDisplayName(
|
|
|
352
352
|
return readAutomationDisplayName(content) ?? automationName;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
export function assignCreatedByIfMissing(
|
|
356
|
+
content: string,
|
|
357
|
+
createdBy: string,
|
|
358
|
+
): string {
|
|
359
|
+
if (readFrontmatterField(content, "createdBy")) return content;
|
|
360
|
+
return setAutomationFrontmatterField(content, "createdBy", createdBy);
|
|
361
|
+
}
|
|
362
|
+
|
|
355
363
|
export function setAutomationFrontmatterField(
|
|
356
364
|
content: string,
|
|
357
365
|
key: string,
|
|
@@ -25,12 +25,6 @@ const FACTORY_AUTOMATION_NAMES = {
|
|
|
25
25
|
|
|
26
26
|
export type FactoryAutomationRole = keyof typeof FACTORY_AUTOMATION_NAMES;
|
|
27
27
|
|
|
28
|
-
function workspaceOwnerEmail(): string | undefined {
|
|
29
|
-
const email = process.env.WORKSPACE_OWNER_EMAIL?.trim().toLowerCase(); // guard:allow-env-credential - deployment owner identity, not a user credential
|
|
30
|
-
if (!email || /[\r\n]/.test(email)) return undefined;
|
|
31
|
-
return email;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
28
|
export async function requireFactoryAutomation(
|
|
35
29
|
context: ActionRunContext | undefined,
|
|
36
30
|
identity: Pick<WorkspaceMemberIdentity, "userEmail" | "orgId">,
|
|
@@ -61,15 +55,13 @@ export async function requireFactoryAutomation(
|
|
|
61
55
|
"organization",
|
|
62
56
|
)
|
|
63
57
|
).find((entry) => entry.resource.id === lineage.triggerId);
|
|
64
|
-
const ownerEmail = workspaceOwnerEmail();
|
|
65
58
|
if (
|
|
66
59
|
!definition ||
|
|
67
60
|
definition.name !== lineage.triggerName ||
|
|
68
61
|
definition.meta.domain !== "factory" ||
|
|
69
62
|
definition.meta.orgId !== identity.orgId ||
|
|
70
63
|
definition.meta.runAs !== "creator" ||
|
|
71
|
-
!
|
|
72
|
-
definition.meta.createdBy?.trim().toLowerCase() !== ownerEmail
|
|
64
|
+
!definition.meta.createdBy?.trim()
|
|
73
65
|
) {
|
|
74
66
|
throw new Error(
|
|
75
67
|
"The action was not invoked by a governed Factory automation.",
|
|
@@ -21,6 +21,7 @@ import { triageConfig } from "../db/schema.js";
|
|
|
21
21
|
import { repairFactoryAutomationsFromConfig } from "../lib/factory-automation-repair.js";
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_FACTORY_ID,
|
|
24
|
+
assignCreatedByIfMissing,
|
|
24
25
|
factoryAutomationJobPath,
|
|
25
26
|
factoryConfigRowId,
|
|
26
27
|
patchAutomationResource,
|
|
@@ -541,7 +542,7 @@ export async function ensureFactoryAutomations(
|
|
|
541
542
|
repaired = setFrontmatterField(repaired, "appId", "factory");
|
|
542
543
|
repaired = setFrontmatterField(repaired, "orgId", orgId);
|
|
543
544
|
repaired = setFrontmatterField(repaired, "factoryId", factoryId);
|
|
544
|
-
repaired =
|
|
545
|
+
repaired = assignCreatedByIfMissing(repaired, ownerEmail);
|
|
545
546
|
repaired = setFrontmatterField(repaired, "runAs", "creator");
|
|
546
547
|
if (!frontmatterField(repaired, "model")) {
|
|
547
548
|
repaired = setFrontmatterField(repaired, "model", seed.model);
|
|
@@ -67,6 +67,12 @@ export interface UpdateAutomationInput {
|
|
|
67
67
|
* automation classifier.
|
|
68
68
|
*/
|
|
69
69
|
export declare function canUpdateAutomationResource(actorInput: AutomationActor, resource: Resource): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Factory is a shared team workspace: any current org member may queue Run now
|
|
72
|
+
* for that app's Factory-domain org jobs. Mail/CRM and other automations stay
|
|
73
|
+
* on creator-or-admin `canUpdate`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function canQueueAutomationRunNow(actorInput: AutomationActor, resource: Resource, scope: AutomationScope): Promise<boolean>;
|
|
70
76
|
export declare function listAutomationDefinitions(actorInput: AutomationActor, scope: AutomationScope): Promise<AutomationDefinition[]>;
|
|
71
77
|
export declare function defineAutomation(actorInput: AutomationActor, input: DefineAutomationInput): Promise<DefinedAutomation>;
|
|
72
78
|
export declare function updateAutomation(actorInput: AutomationActor, input: UpdateAutomationInput): Promise<AutomationDefinition>;
|
|
@@ -108,6 +108,26 @@ export async function canUpdateAutomationResource(actorInput, resource) {
|
|
|
108
108
|
return false;
|
|
109
109
|
return (await mutationAccess(actor, resource, meta)).canUpdate;
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Factory is a shared team workspace: any current org member may queue Run now
|
|
113
|
+
* for that app's Factory-domain org jobs. Mail/CRM and other automations stay
|
|
114
|
+
* on creator-or-admin `canUpdate`.
|
|
115
|
+
*/
|
|
116
|
+
export async function canQueueAutomationRunNow(actorInput, resource, scope) {
|
|
117
|
+
const { meta } = parseJobResource(resource.content);
|
|
118
|
+
const actor = normalizeActor(actorInput);
|
|
119
|
+
if (!jobBelongsToApp(meta, actor.appId))
|
|
120
|
+
return false;
|
|
121
|
+
const access = await mutationAccess(actor, resource, meta);
|
|
122
|
+
if (access.canUpdate)
|
|
123
|
+
return true;
|
|
124
|
+
const resourceOrgId = organizationIdFromResourceOwner(resource.owner);
|
|
125
|
+
return (scope === "organization" &&
|
|
126
|
+
Boolean(resourceOrgId) &&
|
|
127
|
+
actor.orgId === resourceOrgId &&
|
|
128
|
+
actor.appId === "factory" &&
|
|
129
|
+
meta.domain === "factory");
|
|
130
|
+
}
|
|
111
131
|
function assertExplicitAutomation(resource) {
|
|
112
132
|
const parsed = parseJobResource(resource.content);
|
|
113
133
|
if (parsed.classification.kind !== "automation") {
|
|
@@ -1458,7 +1458,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1458
1458
|
// Cleared on the next message send.
|
|
1459
1459
|
const [composerError, setComposerError] = useState(null);
|
|
1460
1460
|
const [composerText, setComposerText] = useState("");
|
|
1461
|
-
const composerDraftScope =
|
|
1461
|
+
const composerDraftScope = tabId || threadId;
|
|
1462
1462
|
const initialComposerText = useMemo(() => readAssistantChatComposerDraft(composerDraftScope), [composerDraftScope]);
|
|
1463
1463
|
const handleComposerTextChange = useCallback((text) => {
|
|
1464
1464
|
setComposerText(text);
|
|
@@ -12,6 +12,7 @@ import { AgentWorkspaceContent } from "../agent-page/AgentWorkspaceContent.js";
|
|
|
12
12
|
import { AGENT_PROVIDER_CATALOG, getAgentProviderOption, providerIdForEngine, } from "../agent-provider-catalog.js";
|
|
13
13
|
import { agentNativePath } from "../api-path.js";
|
|
14
14
|
import { BuilderBMark } from "../builder-mark.js";
|
|
15
|
+
import { fetchAgentEngineStatus, fetchEnvironmentStatus, } from "../client-status-requests.js";
|
|
15
16
|
import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
16
17
|
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
17
18
|
import { useT } from "../i18n.js";
|
|
@@ -332,7 +333,7 @@ const PROVIDER_DOCS = {
|
|
|
332
333
|
"ai-sdk:mistral": "https://console.mistral.ai/api-keys/",
|
|
333
334
|
"ai-sdk:cohere": "https://dashboard.cohere.com/api-keys",
|
|
334
335
|
};
|
|
335
|
-
function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, orgName, envManaged, credentialSource, grouped = false, open, onToggle, }) {
|
|
336
|
+
function LLMSectionInner({ builderFlow, builderLoading, builderStatusAvailable, connectUrl, connected, orgName, envManaged, credentialSource, grouped = false, open, onToggle, }) {
|
|
336
337
|
const isPage = useSettingsSurface() === "page";
|
|
337
338
|
const t = useT();
|
|
338
339
|
const [envKeys, setEnvKeys] = useState([]);
|
|
@@ -354,24 +355,40 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
354
355
|
const [testResult, setTestResult] = useState(null);
|
|
355
356
|
const [settingsStatus, setSettingsStatus] = useState(null);
|
|
356
357
|
const [disconnectError, setDisconnectError] = useState(null);
|
|
357
|
-
const [
|
|
358
|
+
const [envProbeAvailable, setEnvProbeAvailable] = useState(false);
|
|
358
359
|
const [enginesLoaded, setEnginesLoaded] = useState(false);
|
|
359
|
-
const [
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
360
|
+
const [statusProbeAvailable, setStatusProbeAvailable] = useState(false);
|
|
361
|
+
const probeGenerationRef = useRef({ env: 0, status: 0 });
|
|
362
|
+
const initialLoading = !enginesLoaded || !!builderLoading;
|
|
363
|
+
const refreshEnvKeys = useCallback(() => {
|
|
364
|
+
const generation = ++probeGenerationRef.current.env;
|
|
365
|
+
setEnvProbeAvailable(false);
|
|
366
|
+
setEnvKeys([]);
|
|
367
|
+
void fetchEnvironmentStatus().then((result) => {
|
|
368
|
+
if (generation !== probeGenerationRef.current.env)
|
|
369
|
+
return;
|
|
370
|
+
if (result.state !== "available" || !Array.isArray(result.value)) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
setEnvKeys(result.value);
|
|
374
|
+
setEnvProbeAvailable(true);
|
|
375
|
+
});
|
|
376
|
+
}, []);
|
|
368
377
|
const notifyConfigChanged = useCallback(() => {
|
|
369
378
|
window.dispatchEvent(new CustomEvent("agent-engine:configured-changed"));
|
|
370
379
|
}, []);
|
|
371
380
|
const refreshSettingsStatus = useCallback(() => {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
381
|
+
const generation = ++probeGenerationRef.current.status;
|
|
382
|
+
setStatusProbeAvailable(false);
|
|
383
|
+
setSettingsStatus(null);
|
|
384
|
+
setBaseUrlConfigured(false);
|
|
385
|
+
void fetchAgentEngineStatus()
|
|
386
|
+
.then((result) => {
|
|
387
|
+
if (generation !== probeGenerationRef.current.status)
|
|
388
|
+
return;
|
|
389
|
+
if (result.state !== "available")
|
|
390
|
+
return;
|
|
391
|
+
const data = result.value;
|
|
375
392
|
setBaseUrlConfigured(Boolean(data?.openAiBaseUrlConfigured));
|
|
376
393
|
if (data?.configured &&
|
|
377
394
|
typeof data.engine === "string" &&
|
|
@@ -387,13 +404,34 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
387
404
|
else {
|
|
388
405
|
setSettingsStatus(null);
|
|
389
406
|
}
|
|
407
|
+
setStatusProbeAvailable(true);
|
|
390
408
|
})
|
|
391
|
-
.catch(() => { })
|
|
392
|
-
.finally(() => setStatusLoaded(true));
|
|
409
|
+
.catch(() => { });
|
|
393
410
|
}, []);
|
|
411
|
+
useEffect(() => {
|
|
412
|
+
refreshEnvKeys();
|
|
413
|
+
}, [refreshEnvKeys]);
|
|
394
414
|
useEffect(() => {
|
|
395
415
|
refreshSettingsStatus();
|
|
396
416
|
}, [refreshSettingsStatus]);
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
const refresh = () => {
|
|
419
|
+
refreshEnvKeys();
|
|
420
|
+
refreshSettingsStatus();
|
|
421
|
+
};
|
|
422
|
+
window.addEventListener("agent-engine:configured-changed", refresh);
|
|
423
|
+
window.addEventListener("focus", refresh);
|
|
424
|
+
const onVisibilityChange = () => {
|
|
425
|
+
if (document.visibilityState === "visible")
|
|
426
|
+
refresh();
|
|
427
|
+
};
|
|
428
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
429
|
+
return () => {
|
|
430
|
+
window.removeEventListener("agent-engine:configured-changed", refresh);
|
|
431
|
+
window.removeEventListener("focus", refresh);
|
|
432
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
433
|
+
};
|
|
434
|
+
}, [refreshEnvKeys, refreshSettingsStatus]);
|
|
397
435
|
useEffect(() => {
|
|
398
436
|
callAction("manage-agent-engine", { action: "list" })
|
|
399
437
|
.then((data) => {
|
|
@@ -441,14 +479,21 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
441
479
|
}
|
|
442
480
|
return configured;
|
|
443
481
|
}, [envKeys, settingsStatus]);
|
|
444
|
-
const builderConnected = connected || builderFlow.configured;
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
482
|
+
const builderConnected = builderStatusAvailable && (connected || builderFlow.configured);
|
|
483
|
+
const configurationKnown = envProbeAvailable && statusProbeAvailable;
|
|
484
|
+
const builderEngineSelected = selectedEngine === "builder";
|
|
485
|
+
const selectedConfigurationKnown = builderEngineSelected
|
|
486
|
+
? builderStatusAvailable
|
|
487
|
+
: configurationKnown;
|
|
488
|
+
const anyKeyConfigured = (builderEngineSelected && builderConnected) ||
|
|
489
|
+
(!builderEngineSelected &&
|
|
490
|
+
configurationKnown &&
|
|
491
|
+
selectedEnginePackageInstalled &&
|
|
492
|
+
(envConfigured || settingsConfigured));
|
|
448
493
|
const sourceBadge = computeSourceBadge({
|
|
449
|
-
settingsConfigured,
|
|
450
|
-
settingsStatus,
|
|
451
|
-
envConfigured,
|
|
494
|
+
settingsConfigured: configurationKnown && settingsConfigured,
|
|
495
|
+
settingsStatus: configurationKnown ? settingsStatus : null,
|
|
496
|
+
envConfigured: configurationKnown && envConfigured,
|
|
452
497
|
envVar,
|
|
453
498
|
builderConnected,
|
|
454
499
|
});
|
|
@@ -479,7 +524,6 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
479
524
|
setBaseUrlConfigured(true);
|
|
480
525
|
if (clearBaseUrl)
|
|
481
526
|
setBaseUrlConfigured(false);
|
|
482
|
-
refreshSettingsStatus();
|
|
483
527
|
notifyConfigChanged();
|
|
484
528
|
setTimeout(() => setSaved(false), 2000);
|
|
485
529
|
}
|
|
@@ -496,7 +540,6 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
496
540
|
if (res.ok) {
|
|
497
541
|
setTestResult(null);
|
|
498
542
|
setApplyNote(false);
|
|
499
|
-
refreshSettingsStatus();
|
|
500
543
|
notifyConfigChanged();
|
|
501
544
|
return;
|
|
502
545
|
}
|
|
@@ -560,7 +603,6 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
560
603
|
setCurrentEngine(selectedEngine);
|
|
561
604
|
setCurrentModel(selectedModel);
|
|
562
605
|
setApplyNote(true);
|
|
563
|
-
refreshSettingsStatus();
|
|
564
606
|
notifyConfigChanged();
|
|
565
607
|
setTimeout(() => setApplyNote(false), 4000);
|
|
566
608
|
}
|
|
@@ -568,7 +610,9 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
568
610
|
setApplyError(err instanceof Error ? err.message : String(err));
|
|
569
611
|
}
|
|
570
612
|
};
|
|
571
|
-
return (_jsx(SettingsSection, { id: settingsSectionDomId("llm"), icon: _jsx(IconBrain, { size: 14 }), title: "LLM", required: true, connected: initialLoading
|
|
613
|
+
return (_jsx(SettingsSection, { id: settingsSectionDomId("llm"), icon: _jsx(IconBrain, { size: 14 }), title: "LLM", required: true, connected: initialLoading || !selectedConfigurationKnown
|
|
614
|
+
? undefined
|
|
615
|
+
: anyKeyConfigured, subtitle: isPage
|
|
572
616
|
? undefined
|
|
573
617
|
: t("agentPanel.builderOrOwnKeys", {
|
|
574
618
|
defaultValue: "Choose Builder.io or custom keys.",
|
|
@@ -576,11 +620,11 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
576
620
|
? "flex items-center justify-between gap-4 px-5 py-4 sm:px-6"
|
|
577
621
|
: "flex items-center justify-between gap-2"), children: [isPage && (_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: "AI provider" }), _jsx("p", { className: "mt-1 text-sm leading-6 text-muted-foreground", children: t("agentPanel.builderOrOwnKeys", {
|
|
578
622
|
defaultValue: "Choose Builder.io or custom keys.",
|
|
579
|
-
}) })] })), _jsxs("div", { className: cn("flex flex-wrap items-center justify-end gap-2"), children: [!anyKeyConfigured && (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected:
|
|
623
|
+
}) })] })), _jsxs("div", { className: cn("flex flex-wrap items-center justify-end gap-2"), children: [selectedConfigurationKnown && !anyKeyConfigured && (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: builderConnected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "llm_settings", trackingFlow: "connect_llm", label: "Connect Builder.io", compact: true })), _jsx(ManualSetupCard, { id: "llm-manual-setup", title: "Custom keys", hint: manualSetupHint, sourceBadge: builderConnected ? undefined : sourceBadge, bare: isPage, popover: true, popoverLabel: settingsConfigured
|
|
580
624
|
? "Manage"
|
|
581
625
|
: t("agentPanel.addOwnKeys", {
|
|
582
626
|
defaultValue: "Custom keys",
|
|
583
|
-
}), summaryContent: anyKeyConfigured ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected:
|
|
627
|
+
}), summaryContent: selectedConfigurationKnown && anyKeyConfigured ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: builderConnected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "llm_settings", trackingFlow: "connect_llm", label: "Connect Builder.io" })) : undefined, children: _jsxs("div", { className: "space-y-2 mb-1", children: [_jsx(AgentProviderPicker, { value: selectedProvider, configuredProviders: configurationKnown ? configuredProviderIds : undefined, layout: isPage ? "page" : "compact", onChange: (provider) => {
|
|
584
628
|
const option = getAgentProviderOption(provider);
|
|
585
629
|
setSelectedEngine(option.engine);
|
|
586
630
|
setSelectedModel(option.defaultModel);
|
|
@@ -611,7 +655,9 @@ function LLMSectionInner({ builderFlow, builderLoading, connectUrl, connected, o
|
|
|
611
655
|
: `${envVar} configured`] })) : envVar ? (_jsxs("div", { className: "flex gap-1.5", children: [_jsx("input", { type: "password", value: apiKey, onChange: (e) => setApiKey(e.target.value), onKeyDown: (e) => {
|
|
612
656
|
if (e.key === "Enter")
|
|
613
657
|
handleSave();
|
|
614
|
-
}, placeholder: PROVIDER_ENV_PLACEHOLDERS[envVar] ?? "...", className: cn(textInputClass(isPage), "flex-1"), style: isPage ? CONTROL_STYLE_PAGE : undefined }), _jsx(Button, { intent: "primary", emphasis: "solid", onClick: handleSave, disabled: !providerSettingsChanged || saving, className: pillButtonClass(isPage, "solid"), children: saving ? (_jsx(IconLoader2, { size: isPage ? 14 : 10, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: isPage ? 14 : 10 })) : ("Save") })] })) : null, _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { intent: "neutral", emphasis: "outline", onClick: handleTest, disabled: testing
|
|
658
|
+
}, placeholder: PROVIDER_ENV_PLACEHOLDERS[envVar] ?? "...", className: cn(textInputClass(isPage), "flex-1"), style: isPage ? CONTROL_STYLE_PAGE : undefined }), _jsx(Button, { intent: "primary", emphasis: "solid", onClick: handleSave, disabled: !providerSettingsChanged || saving, className: pillButtonClass(isPage, "solid"), children: saving ? (_jsx(IconLoader2, { size: isPage ? 14 : 10, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: isPage ? 14 : 10 })) : ("Save") })] })) : null, _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { intent: "neutral", emphasis: "outline", onClick: handleTest, disabled: testing ||
|
|
659
|
+
!selectedConfigurationKnown ||
|
|
660
|
+
!anyKeyConfigured, className: pillButtonClass(isPage, "outline"), children: testing ? (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(IconLoader2, { size: isPage ? 14 : 10, className: "animate-spin" }), "Testing\u2026"] })) : ("Test") }), PROVIDER_DOCS[selectedEngine] ? (_jsxs("a", { href: PROVIDER_DOCS[selectedEngine], target: "_blank", rel: "noopener noreferrer", className: cn(pillButtonClass(isPage, "outline"), "no-underline"), children: ["Get an API key", _jsx(IconExternalLink, { size: isPage ? 14 : 10 })] })) : null, engineChanged && (_jsx(Button, { intent: "primary", emphasis: "solid", onClick: handleApply, className: pillButtonClass(isPage, "solid"), children: "Apply" })), settingsStatus != null && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { intent: "danger", emphasis: "outline", onClick: handleDisconnect, className: cn(pillButtonClass(isPage, "outline"), "ms-auto text-muted-foreground hover:bg-destructive/10 hover:text-destructive hover:border-destructive/40"), children: "Disconnect" }) }), _jsx(TooltipContent, { children: "Clear the saved engine \u2014 the app will fall back to the default until you re-apply." })] }))] }), testResult && testResult.ok && (_jsxs("p", { className: cn("flex items-center gap-1 text-primary", isPage ? "text-xs" : "text-[10px]"), children: [_jsx(IconCheck, { size: isPage ? 14 : 10 }), "Test passed \u2014 ", testResult.latencyMs, "ms"] })), testResult && testResult.ok === false && (_jsxs("p", { className: cn("text-destructive", isPage ? "text-xs" : "text-[10px]"), children: ["Test failed: ", testResult.error] })), disconnectError && (_jsxs("p", { className: cn("text-destructive", isPage ? "text-xs" : "text-[10px]"), children: ["Disconnect failed: ", disconnectError] })), applyError && (_jsxs("p", { className: cn("text-destructive", isPage ? "text-xs" : "text-[10px]"), children: ["Apply failed: ", applyError] })), applyNote && (_jsx("p", { className: cn("text-muted-foreground", isPage ? "text-xs" : "text-[10px]"), children: "Changes take effect on next conversation" }))] }) })] })] })) }));
|
|
615
661
|
}
|
|
616
662
|
function friendlyAppName(appId) {
|
|
617
663
|
return appId
|
|
@@ -1056,10 +1102,11 @@ function CapabilityStatusStrip({ isDevMode, builderConnected, builderLoading, bu
|
|
|
1056
1102
|
return (_jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-2.5 py-2", children: [_jsx("div", { className: "mb-1.5 text-[10px] font-medium text-muted-foreground", children: "Available now" }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(CapabilityStatusRow, { label: "App", value: "Chat + actions", active: true }), _jsx(CapabilityStatusRow, { label: "Code", value: codeLabel, active: codeAvailable }), _jsx(CapabilityStatusRow, { label: "Builder", active: builderConnected, value: builderLoading ? (_jsx(Skeleton, { className: "h-3 w-16" })) : builderConnected ? ("Connected") : (_jsx(Button, { type: "button", intent: "neutral", emphasis: "outline", onClick: onOpenLlm, className: "rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground transition-colors hover:bg-accent/40 hover:text-foreground", children: "Connect" })) })] })] }));
|
|
1057
1103
|
}
|
|
1058
1104
|
function SettingsPanelContent({ isDevMode, initialSection, sectionRequestKey, sections = ALL_SETTINGS_SECTIONS, showCapabilityStrip = true, className, surface = "sidebar", builderConnectionOwnedExternally = false, agentAdditionalContent, }) {
|
|
1059
|
-
const { status: builder, loading: builderLoading } = useBuilderStatus({
|
|
1105
|
+
const { status: builder, loading: builderLoading, stale: builderStatusStale, } = useBuilderStatus({
|
|
1060
1106
|
enabled: !builderConnectionOwnedExternally,
|
|
1061
1107
|
});
|
|
1062
1108
|
const connected = builder?.configured ?? false;
|
|
1109
|
+
const builderStatusAvailable = !builderLoading && !builderStatusStale && builder != null;
|
|
1063
1110
|
const connectUrl = builder?.connectUrl;
|
|
1064
1111
|
const orgName = builder?.orgName;
|
|
1065
1112
|
const envManaged = !!builder?.envManaged;
|
|
@@ -1088,7 +1135,7 @@ function SettingsPanelContent({ isDevMode, initialSection, sectionRequestKey, se
|
|
|
1088
1135
|
const isPage = surface === "page";
|
|
1089
1136
|
const isWorkspacePage = isPage && sections.includes("hosting");
|
|
1090
1137
|
return (_jsx(SettingsSurfaceProvider, { surface: surface, children: _jsxs("div", { className: cn(isPage ? "space-y-8" : "flex-1 min-h-0 overflow-y-auto p-3 space-y-2", className), style: isPage ? undefined : { overflowY: "auto" }, children: [showCapabilityStrip && (_jsx(CapabilityStatusStrip, { isDevMode: isDevMode, builderConnected: connected, builderLoading: builderLoading, builderBranchesAvailable: builderBranchesAvailable, onOpenLlm: () => openSettingsSection("llm", true) })), isPage &&
|
|
1091
|
-
["llm", "app-models", "limits", "voice"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent", children: [shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, grouped: true, open: openSection === "llm", onToggle: () => toggle("llm") })), shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { grouped: true, open: openSection === "app-models", onToggle: () => toggle("app-models") })), shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { grouped: true, open: openSection === "limits", onToggle: () => toggle("limits") })), shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "Choose how voice input is transcribed.", grouped: true, flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, { compact: true }) }))] })), isPage && agentAdditionalContent ? (_jsx(SettingsGroup, { title: "Notifications", children: agentAdditionalContent })) : null, isPage &&
|
|
1138
|
+
["llm", "app-models", "limits", "voice"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent", children: [shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, builderStatusAvailable: builderStatusAvailable, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, grouped: true, open: openSection === "llm", onToggle: () => toggle("llm") })), shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { grouped: true, open: openSection === "app-models", onToggle: () => toggle("app-models") })), shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { grouped: true, open: openSection === "limits", onToggle: () => toggle("limits") })), shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "Choose how voice input is transcribed.", grouped: true, flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, { compact: true }) }))] })), isPage && agentAdditionalContent ? (_jsx(SettingsGroup, { title: "Notifications", children: agentAdditionalContent })) : null, isPage &&
|
|
1092
1139
|
["automations", "background"].some((section) => shouldShowSection(section)) && (_jsxs(SettingsGroup, { title: "Agent workflows", children: [shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", grouped: true, flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(SettingsRow, { label: "Automations", description: "Schedule agent tasks or run them from events.", control: _jsxs("a", { href: "/settings/agent/automations", className: "inline-flex items-center gap-1 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground no-underline transition-colors hover:bg-accent/40", children: ["Open automations", _jsx(IconExternalLink, { size: 14 })] }) }) })), shouldShowSection("background") && (_jsx(SettingsSection, { id: settingsSectionDomId("background"), icon: _jsx(IconGitBranch, { size: 14 }), title: "Background Agent", subtitle: "Make code changes from production mode via Builder.", grouped: true, flat: true, connected: connected, open: openSection === "background", onToggle: () => toggle("background"), children: _jsx(SettingsRow, { label: "Background agent", description: "Make code changes from production mode via Builder.", control: _jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "background_agent_settings", trackingFlow: "background_agent", compact: true }) }) }))] })), isWorkspacePage && (_jsxs(SettingsGroup, { title: "Workspace", children: [shouldShowSection("demo-mode") && (_jsx(SettingsRow, { id: settingsSectionDomId("demo-mode"), label: "Demo mode", description: "Use sample data in this browser for presentations.", control: _jsx(DemoModeSection, { compact: true }) })), shouldShowSection("hosting") && (_jsx(SettingsRow, { id: settingsSectionDomId("hosting"), label: "Hosting", description: "Deploy the app to the cloud.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [!connected && (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting", compact: true })), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: docsUrl("deployment", {
|
|
1093
1140
|
campaign: "onboarding",
|
|
1094
1141
|
content: "deployment_settings",
|
|
@@ -1101,7 +1148,7 @@ function SettingsPanelContent({ isDevMode, initialSection, sectionRequestKey, se
|
|
|
1101
1148
|
}), dim: connected, bare: true, popover: true, popoverLabel: "Manage", summaryContent: connected ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "file_upload_settings", trackingFlow: "file_upload" })) : undefined, children: _jsx(FileStorageSettingsForm, {}) })] }) })), shouldShowSection("auth") && (_jsx(SettingsRow, { id: settingsSectionDomId("auth"), label: "Authentication", description: "Set up sign-in and access control.", control: _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [!connected && (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth", compact: true })), _jsx(ManualSetupCard, { title: "Set up manually", hint: "Configure Better Auth and optional Google or GitHub providers.", docsUrl: docsUrl("authentication", {
|
|
1102
1149
|
campaign: "onboarding",
|
|
1103
1150
|
content: "authentication_settings",
|
|
1104
|
-
}), dim: connected, bare: true, popover: true, popoverLabel: "Manage", summaryContent: connected ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth" })) : undefined })] }) }))] })), !isPage && shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, open: openSection === "llm", onToggle: () => toggle("llm") })), !isPage && shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { open: openSection === "app-models", onToggle: () => toggle("app-models") })), !isPage && shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { open: openSection === "limits", onToggle: () => toggle("limits") })), !isPage && shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "How the composer microphone turns your voice into text.", flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, {}) })), !isPage && shouldShowSection("demo-mode") && (_jsx(SettingsSection, { id: settingsSectionDomId("demo-mode"), icon: _jsx(IconEyeOff, { size: 14 }), title: "Demo mode", subtitle: "Replace displayed emails with realistic fake data in this browser and reshape supported charts for presentations. Backend, agent integrations, and agent results stay real and access-scoped.", flat: true, open: openSection === "demo-mode", onToggle: () => toggle("demo-mode"), children: _jsx(DemoModeSection, {}) })), !isPage && shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(AutomationsSection, {}) })), shouldShowSection("secrets") && (_jsx(SettingsSection, { id: settingsSectionDomId("secrets"), icon: _jsx(IconKey, { size: 14 }), title: "API keys", subtitle: "Service credentials and automation keys.", flat: true, open: openSection === "secrets", onToggle: () => toggle("secrets"), children: _jsx(SecretsSection, { focusKey: focusSecretKey }) })), !isPage && shouldShowSection("hosting") && (_jsx(SettingsSection, { id: settingsSectionDomId("hosting"), icon: _jsx(IconCloud, { size: 14 }), title: "Hosting", subtitle: "Deploy your app to the cloud.", flat: true, connected: connected, open: openSection === "hosting", onToggle: () => toggle("hosting"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting" }), _jsx(ManualSetupCard, { hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: docsUrl("deployment", {
|
|
1151
|
+
}), dim: connected, bare: true, popover: true, popoverLabel: "Manage", summaryContent: connected ? (_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "auth_settings", trackingFlow: "auth" })) : undefined })] }) }))] })), !isPage && shouldShowSection("llm") && (_jsx(LLMSectionInner, { builderFlow: builderFlow, builderLoading: builderLoading, builderStatusAvailable: builderStatusAvailable, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, open: openSection === "llm", onToggle: () => toggle("llm") })), !isPage && shouldShowSection("app-models") && (_jsx(AppModelDefaultsSectionInner, { open: openSection === "app-models", onToggle: () => toggle("app-models") })), !isPage && shouldShowSection("limits") && (_jsx(AgentLimitsSectionInner, { open: openSection === "limits", onToggle: () => toggle("limits") })), !isPage && shouldShowSection("voice") && (_jsx(SettingsSection, { id: settingsSectionDomId("voice"), icon: _jsx(IconMicrophone, { size: 14 }), title: "Voice Transcription", subtitle: "How the composer microphone turns your voice into text.", flat: true, open: openSection === "voice", onToggle: () => toggle("voice"), children: _jsx(VoiceTranscriptionSection, {}) })), !isPage && shouldShowSection("demo-mode") && (_jsx(SettingsSection, { id: settingsSectionDomId("demo-mode"), icon: _jsx(IconEyeOff, { size: 14 }), title: "Demo mode", subtitle: "Replace displayed emails with realistic fake data in this browser and reshape supported charts for presentations. Backend, agent integrations, and agent results stay real and access-scoped.", flat: true, open: openSection === "demo-mode", onToggle: () => toggle("demo-mode"), children: _jsx(DemoModeSection, {}) })), !isPage && shouldShowSection("automations") && (_jsx(SettingsSection, { id: settingsSectionDomId("automations"), icon: _jsx(IconBolt, { size: 14 }), title: "Automations", subtitle: "Scheduled and event-triggered agent tasks.", flat: true, open: openSection === "automations", onToggle: () => toggle("automations"), children: _jsx(AutomationsSection, {}) })), shouldShowSection("secrets") && (_jsx(SettingsSection, { id: settingsSectionDomId("secrets"), icon: _jsx(IconKey, { size: 14 }), title: "API keys", subtitle: "Service credentials and automation keys.", flat: true, open: openSection === "secrets", onToggle: () => toggle("secrets"), children: _jsx(SecretsSection, { focusKey: focusSecretKey }) })), !isPage && shouldShowSection("hosting") && (_jsx(SettingsSection, { id: settingsSectionDomId("hosting"), icon: _jsx(IconCloud, { size: 14 }), title: "Hosting", subtitle: "Deploy your app to the cloud.", flat: true, connected: connected, open: openSection === "hosting", onToggle: () => toggle("hosting"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "hosting_settings", trackingFlow: "hosting" }), _jsx(ManualSetupCard, { hint: "Deploy manually to Netlify, Vercel, Cloudflare, or any Nitro-supported target.", docsUrl: docsUrl("deployment", {
|
|
1105
1152
|
campaign: "onboarding",
|
|
1106
1153
|
content: "deployment_settings",
|
|
1107
1154
|
}), dim: connected })] }) })), !isPage && shouldShowSection("database") && (_jsx(SettingsSection, { id: settingsSectionDomId("database"), icon: _jsx(IconDatabase, { size: 14 }), title: "Database", subtitle: "Connect a cloud database for persistent storage.", flat: true, connected: connected, open: openSection === "database", onToggle: () => toggle("database"), children: _jsxs("div", { className: "space-y-2", children: [_jsx(UseBuilderCard, { builderFlow: builderFlow, connectUrl: connectUrl, connected: connected, orgName: orgName, envManaged: envManaged, credentialSource: credentialSource, trackingSource: "database_settings", trackingFlow: "database" }), _jsx(ManualSetupCard, { hint: "Set DATABASE_URL in your .env to connect Neon, Supabase, Turso, any Postgres/SQLite database, or local PGlite with pglite:./data/pglite.", docsUrl: docsUrl("database", {
|