@agent-native/core 0.164.12 → 0.164.17
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/netlify.toml +4 -0
- package/corpus/templates/clips/actions/request-transcript.ts +10 -6
- package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
- package/corpus/templates/content/actions/_database-utils.ts +2 -0
- package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
- package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
- package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
- package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +298 -3
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +81 -1
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +18 -4
- package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
- package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
- package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
- package/corpus/templates/content/app/i18n-data.ts +239 -0
- package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
- package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
- package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
- package/corpus/templates/content/app/root.tsx +2 -1
- package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
- package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
- package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
- package/corpus/templates/content/docs/product/roadmap.md +2 -2
- package/corpus/templates/content/docs/solutions/2026-08-18-colored-text-highlighting-shape.md +693 -0
- package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/shared/api.ts +25 -0
- package/corpus/templates/content/shared/nfm.ts +14 -3
- package/corpus/templates/factory/AGENTS.md +24 -29
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +8 -28
- package/corpus/templates/factory/actions/get-triage-config.ts +1 -0
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +67 -59
- package/corpus/templates/factory/actions/list-triage-items.ts +6 -1
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +7 -1
- package/corpus/templates/factory/actions/save-triage-config.ts +27 -7
- package/corpus/templates/factory/actions/start-builder-for-item.ts +113 -31
- package/corpus/templates/factory/actions/view-screen.ts +12 -0
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +457 -0
- package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +53 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +3 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +10 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -0
- package/corpus/templates/factory/app/routes/agents.tsx +22 -1
- package/corpus/templates/factory/app/routes/factory-settings.tsx +34 -0
- package/corpus/templates/factory/app/routes/factory.tsx +33 -323
- package/corpus/templates/factory/server/connectors/slack.ts +62 -0
- package/corpus/templates/factory/server/db/schema.ts +1 -0
- package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +30 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -8
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +7 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +43 -25
- package/corpus/templates/factory/server/triage/ai-services-git.ts +2 -0
- package/corpus/templates/factory/server/triage/pr-monitor.ts +2 -1
- package/corpus/templates/factory/server/triage/pr-policy.ts +197 -19
- package/corpus/templates/factory/server/triage/review-skill-alignment.ts +127 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +79 -6
- package/dist/app-config/auth.d.ts +4 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/cli/code-agent-executor.js +6 -1
- package/dist/cli/code-agent-mcp-config.d.ts +2 -2
- package/dist/cli/code-agent-mcp-config.js +34 -9
- package/dist/client/MultiTabAssistantChat.js +28 -1
- package/dist/client/org/OrgSwitcher.js +8 -4
- package/dist/client/use-chat-threads.d.ts +1 -0
- package/dist/client/use-chat-threads.js +13 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/mcp-client/config.d.ts +11 -0
- package/dist/mcp-client/config.js +33 -26
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +17 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/vite/client.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useActionMutation,
|
|
3
|
+
useActionQuery,
|
|
4
|
+
} from "@agent-native/core/client/hooks";
|
|
5
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
6
|
+
import { buildSettingsRoute } from "@agent-native/core/client/navigation";
|
|
7
|
+
import { SettingsGroup, SettingsRow } from "@agent-native/core/client/settings";
|
|
8
|
+
import { ActionQueryError } from "@agent-native/dispatch/components";
|
|
9
|
+
import { IconLoader2 } from "@tabler/icons-react";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
11
|
+
import { Link } from "react-router";
|
|
12
|
+
import { toast } from "sonner";
|
|
13
|
+
|
|
14
|
+
import { Button } from "@/components/ui/button";
|
|
15
|
+
import { Input } from "@/components/ui/input";
|
|
16
|
+
import { Switch } from "@/components/ui/switch";
|
|
17
|
+
|
|
18
|
+
type TriageConfig = {
|
|
19
|
+
slackWorkspace?: "primary" | "secondary";
|
|
20
|
+
slackChannelId?: string | null;
|
|
21
|
+
slackChannelName?: string | null;
|
|
22
|
+
builderSlackUserId?: string | null;
|
|
23
|
+
pollingEnabled?: boolean;
|
|
24
|
+
githubPollingEnabled?: boolean;
|
|
25
|
+
sentryPollingEnabled?: boolean;
|
|
26
|
+
sentryOrgSlug?: string | null;
|
|
27
|
+
sentryProjectSlug?: string | null;
|
|
28
|
+
sentryEnvironment?: string | null;
|
|
29
|
+
repository?: string | null;
|
|
30
|
+
automationFailureAlertsEnabled?: boolean;
|
|
31
|
+
automationFailureAlertEmail?: string | null;
|
|
32
|
+
emailReadiness?: {
|
|
33
|
+
status: "ready" | "not-configured" | "misconfigured" | "unavailable";
|
|
34
|
+
provider: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type FactoryAutomationHealth = {
|
|
39
|
+
status: "healthy" | "stale" | "error" | "no-data";
|
|
40
|
+
lastCheckedAt?: number | null;
|
|
41
|
+
lastDispatchedAt?: number | null;
|
|
42
|
+
lastError?: string | null;
|
|
43
|
+
runtime?: string | null;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function formatAutomationDate(value: string | number | null | undefined) {
|
|
47
|
+
if (!value) return "-";
|
|
48
|
+
const date = new Date(value);
|
|
49
|
+
return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function FactorySettingsView() {
|
|
53
|
+
const t = useT();
|
|
54
|
+
const [workspace, setWorkspace] = useState<"primary" | "secondary">(
|
|
55
|
+
"primary",
|
|
56
|
+
);
|
|
57
|
+
const [channelId, setChannelId] = useState("");
|
|
58
|
+
const [channelName, setChannelName] = useState("");
|
|
59
|
+
const [builderSlackUserId, setBuilderSlackUserId] = useState("");
|
|
60
|
+
const [repository, setRepository] = useState("");
|
|
61
|
+
const [polling, setPolling] = useState(false);
|
|
62
|
+
const [githubPolling, setGithubPolling] = useState(false);
|
|
63
|
+
const [sentryPolling, setSentryPolling] = useState(false);
|
|
64
|
+
const [sentryOrgSlug, setSentryOrgSlug] = useState("");
|
|
65
|
+
const [sentryProjectSlug, setSentryProjectSlug] = useState("");
|
|
66
|
+
const [sentryEnvironment, setSentryEnvironment] = useState("");
|
|
67
|
+
const [automationFailureAlertsEnabled, setAutomationFailureAlertsEnabled] =
|
|
68
|
+
useState(true);
|
|
69
|
+
const [automationFailureAlertEmail, setAutomationFailureAlertEmail] =
|
|
70
|
+
useState("");
|
|
71
|
+
const query = useActionQuery("get-triage-config", {});
|
|
72
|
+
const schedulerHealthQuery = useActionQuery<FactoryAutomationHealth>(
|
|
73
|
+
"get-factory-automation-health",
|
|
74
|
+
{},
|
|
75
|
+
{ refetchInterval: 60_000 },
|
|
76
|
+
);
|
|
77
|
+
const mutation = useActionMutation("save-triage-config");
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const data = query.data as TriageConfig | undefined;
|
|
81
|
+
if (!data) return;
|
|
82
|
+
setWorkspace(data.slackWorkspace ?? "primary");
|
|
83
|
+
setChannelId(data.slackChannelId ?? "");
|
|
84
|
+
setChannelName(data.slackChannelName ?? "");
|
|
85
|
+
setBuilderSlackUserId(data.builderSlackUserId ?? "");
|
|
86
|
+
setRepository(data.repository ?? "");
|
|
87
|
+
setPolling(data.pollingEnabled ?? false);
|
|
88
|
+
setGithubPolling(data.githubPollingEnabled ?? false);
|
|
89
|
+
setSentryPolling(data.sentryPollingEnabled ?? false);
|
|
90
|
+
setSentryOrgSlug(data.sentryOrgSlug ?? "");
|
|
91
|
+
setSentryProjectSlug(data.sentryProjectSlug ?? "");
|
|
92
|
+
setSentryEnvironment(data.sentryEnvironment ?? "");
|
|
93
|
+
setAutomationFailureAlertsEnabled(
|
|
94
|
+
data.automationFailureAlertsEnabled ?? true,
|
|
95
|
+
);
|
|
96
|
+
setAutomationFailureAlertEmail(data.automationFailureAlertEmail ?? "");
|
|
97
|
+
}, [query.data]);
|
|
98
|
+
|
|
99
|
+
const configLoaded = Boolean(query.data) && !query.isError;
|
|
100
|
+
|
|
101
|
+
const saveSettings = async () => {
|
|
102
|
+
if (!configLoaded) {
|
|
103
|
+
toast.error(t("triage.settingsError"));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
await mutation.mutateAsync({
|
|
108
|
+
slackWorkspace: workspace,
|
|
109
|
+
slackChannelId: channelId,
|
|
110
|
+
slackChannelName: channelName,
|
|
111
|
+
builderSlackUserId,
|
|
112
|
+
repository,
|
|
113
|
+
pollingEnabled: polling,
|
|
114
|
+
githubPollingEnabled: githubPolling,
|
|
115
|
+
sentryPollingEnabled: sentryPolling,
|
|
116
|
+
sentryOrgSlug,
|
|
117
|
+
sentryProjectSlug,
|
|
118
|
+
sentryEnvironment,
|
|
119
|
+
automationFailureAlertsEnabled,
|
|
120
|
+
automationFailureAlertEmail: automationFailureAlertEmail.trim(),
|
|
121
|
+
});
|
|
122
|
+
toast.success(t("triage.settingsSaved"));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
toast.error(
|
|
125
|
+
error instanceof Error ? error.message : t("triage.settingsError"),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
if (query.isLoading) {
|
|
131
|
+
return (
|
|
132
|
+
<div className="mx-auto w-full max-w-3xl space-y-6 p-4 lg:p-6">
|
|
133
|
+
<FactorySettingsSkeleton t={t} />
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!configLoaded) {
|
|
139
|
+
return (
|
|
140
|
+
<div className="mx-auto w-full max-w-3xl p-4 lg:p-6">
|
|
141
|
+
<ActionQueryError
|
|
142
|
+
error={query.error}
|
|
143
|
+
onRetry={() => void query.refetch()}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const fieldControlClass = "h-9 w-full sm:w-64";
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<div className="mx-auto w-full max-w-3xl space-y-6 p-4 lg:p-6">
|
|
153
|
+
<SettingsGroup variant="soft">
|
|
154
|
+
<SettingsRow
|
|
155
|
+
label={t("factoryRoute.workspaceIntegrations")}
|
|
156
|
+
description={t("settings.workspaceDescription")}
|
|
157
|
+
control={
|
|
158
|
+
<Button asChild type="button" variant="outline">
|
|
159
|
+
<Link to={buildSettingsRoute("integrations")}>Manage</Link>
|
|
160
|
+
</Button>
|
|
161
|
+
}
|
|
162
|
+
/>
|
|
163
|
+
<SettingsRow
|
|
164
|
+
label={t("factoryRoute.agentAccess")}
|
|
165
|
+
description={t("settings.agentDescription")}
|
|
166
|
+
control={
|
|
167
|
+
<Button asChild type="button" variant="outline">
|
|
168
|
+
<Link to={buildSettingsRoute("agent")}>Manage</Link>
|
|
169
|
+
</Button>
|
|
170
|
+
}
|
|
171
|
+
/>
|
|
172
|
+
</SettingsGroup>
|
|
173
|
+
|
|
174
|
+
<SettingsGroup variant="soft">
|
|
175
|
+
<SettingsRow
|
|
176
|
+
label={t("triage.slackWorkspace")}
|
|
177
|
+
control={
|
|
178
|
+
<select
|
|
179
|
+
aria-label={t("triage.slackWorkspace")}
|
|
180
|
+
value={workspace}
|
|
181
|
+
onChange={(event) =>
|
|
182
|
+
setWorkspace(event.target.value as "primary" | "secondary")
|
|
183
|
+
}
|
|
184
|
+
className="h-9 w-full rounded-md border border-input bg-background px-3 text-sm sm:w-64"
|
|
185
|
+
>
|
|
186
|
+
<option value="primary">primary</option>
|
|
187
|
+
<option value="secondary">secondary</option>
|
|
188
|
+
</select>
|
|
189
|
+
}
|
|
190
|
+
/>
|
|
191
|
+
<SettingsRow
|
|
192
|
+
label={t("triage.slackChannelId")}
|
|
193
|
+
control={
|
|
194
|
+
<Input
|
|
195
|
+
aria-label={t("triage.slackChannelId")}
|
|
196
|
+
value={channelId}
|
|
197
|
+
onChange={(event) => setChannelId(event.target.value)}
|
|
198
|
+
placeholder={t("triage.slackChannelPlaceholder")}
|
|
199
|
+
className={fieldControlClass}
|
|
200
|
+
/>
|
|
201
|
+
}
|
|
202
|
+
/>
|
|
203
|
+
<SettingsRow
|
|
204
|
+
label={t("triage.slackChannelName")}
|
|
205
|
+
control={
|
|
206
|
+
<Input
|
|
207
|
+
aria-label={t("triage.slackChannelName")}
|
|
208
|
+
value={channelName}
|
|
209
|
+
onChange={(event) => setChannelName(event.target.value)}
|
|
210
|
+
placeholder={t("triage.slackChannelNamePlaceholder")}
|
|
211
|
+
className={fieldControlClass}
|
|
212
|
+
/>
|
|
213
|
+
}
|
|
214
|
+
/>
|
|
215
|
+
<SettingsRow
|
|
216
|
+
label={t("triage.builderSlackUserId")}
|
|
217
|
+
control={
|
|
218
|
+
<Input
|
|
219
|
+
aria-label={t("triage.builderSlackUserId")}
|
|
220
|
+
value={builderSlackUserId}
|
|
221
|
+
onChange={(event) => setBuilderSlackUserId(event.target.value)}
|
|
222
|
+
placeholder={t("triage.builderSlackUserIdPlaceholder")}
|
|
223
|
+
className={fieldControlClass}
|
|
224
|
+
/>
|
|
225
|
+
}
|
|
226
|
+
/>
|
|
227
|
+
<SettingsRow
|
|
228
|
+
label={t("triage.repository")}
|
|
229
|
+
control={
|
|
230
|
+
<Input
|
|
231
|
+
aria-label={t("triage.repository")}
|
|
232
|
+
value={repository}
|
|
233
|
+
onChange={(event) => setRepository(event.target.value)}
|
|
234
|
+
placeholder={t("triage.repositoryPlaceholder")}
|
|
235
|
+
className={fieldControlClass}
|
|
236
|
+
/>
|
|
237
|
+
}
|
|
238
|
+
/>
|
|
239
|
+
<SettingsRow
|
|
240
|
+
label={t("triage.enablePolling")}
|
|
241
|
+
control={
|
|
242
|
+
<Switch
|
|
243
|
+
aria-label={t("triage.enablePolling")}
|
|
244
|
+
checked={polling}
|
|
245
|
+
onCheckedChange={(checked) => setPolling(checked === true)}
|
|
246
|
+
/>
|
|
247
|
+
}
|
|
248
|
+
/>
|
|
249
|
+
<SettingsRow
|
|
250
|
+
label={t("triage.enableGithubPolling")}
|
|
251
|
+
control={
|
|
252
|
+
<Switch
|
|
253
|
+
aria-label={t("triage.enableGithubPolling")}
|
|
254
|
+
checked={githubPolling}
|
|
255
|
+
onCheckedChange={(checked) => setGithubPolling(checked === true)}
|
|
256
|
+
/>
|
|
257
|
+
}
|
|
258
|
+
/>
|
|
259
|
+
<SettingsRow
|
|
260
|
+
label={t("triage.sentryOrgSlug")}
|
|
261
|
+
control={
|
|
262
|
+
<Input
|
|
263
|
+
aria-label={t("triage.sentryOrgSlug")}
|
|
264
|
+
value={sentryOrgSlug}
|
|
265
|
+
onChange={(event) => setSentryOrgSlug(event.target.value)}
|
|
266
|
+
placeholder={t("triage.sentryOrgPlaceholder")}
|
|
267
|
+
className={fieldControlClass}
|
|
268
|
+
/>
|
|
269
|
+
}
|
|
270
|
+
/>
|
|
271
|
+
<SettingsRow
|
|
272
|
+
label={t("triage.sentryProjectSlug")}
|
|
273
|
+
control={
|
|
274
|
+
<Input
|
|
275
|
+
aria-label={t("triage.sentryProjectSlug")}
|
|
276
|
+
value={sentryProjectSlug}
|
|
277
|
+
onChange={(event) => setSentryProjectSlug(event.target.value)}
|
|
278
|
+
placeholder={t("triage.sentryProjectPlaceholder")}
|
|
279
|
+
className={fieldControlClass}
|
|
280
|
+
/>
|
|
281
|
+
}
|
|
282
|
+
/>
|
|
283
|
+
<SettingsRow
|
|
284
|
+
label={t("triage.sentryEnvironment")}
|
|
285
|
+
control={
|
|
286
|
+
<Input
|
|
287
|
+
aria-label={t("triage.sentryEnvironment")}
|
|
288
|
+
value={sentryEnvironment}
|
|
289
|
+
onChange={(event) => setSentryEnvironment(event.target.value)}
|
|
290
|
+
placeholder={t("triage.sentryEnvironmentPlaceholder")}
|
|
291
|
+
className={fieldControlClass}
|
|
292
|
+
/>
|
|
293
|
+
}
|
|
294
|
+
/>
|
|
295
|
+
<SettingsRow
|
|
296
|
+
label={t("triage.enableSentryPolling")}
|
|
297
|
+
control={
|
|
298
|
+
<Switch
|
|
299
|
+
aria-label={t("triage.enableSentryPolling")}
|
|
300
|
+
checked={sentryPolling}
|
|
301
|
+
onCheckedChange={(checked) => setSentryPolling(checked === true)}
|
|
302
|
+
/>
|
|
303
|
+
}
|
|
304
|
+
/>
|
|
305
|
+
</SettingsGroup>
|
|
306
|
+
|
|
307
|
+
<SettingsGroup variant="soft">
|
|
308
|
+
<SettingsRow
|
|
309
|
+
label={t("factoryRoute.automationFailureAlertsTitle")}
|
|
310
|
+
description={t("factoryRoute.automationFailureAlertsDescription")}
|
|
311
|
+
control={
|
|
312
|
+
<Switch
|
|
313
|
+
aria-label={t("factoryRoute.automationFailureAlertsEnabled")}
|
|
314
|
+
checked={automationFailureAlertsEnabled}
|
|
315
|
+
onCheckedChange={(checked) =>
|
|
316
|
+
setAutomationFailureAlertsEnabled(checked === true)
|
|
317
|
+
}
|
|
318
|
+
/>
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
321
|
+
<SettingsRow
|
|
322
|
+
label={t("factoryRoute.automationFailureAlertEmail")}
|
|
323
|
+
description={t("factoryRoute.automationFailureAlertEmailPlaceholder")}
|
|
324
|
+
control={
|
|
325
|
+
<Input
|
|
326
|
+
aria-label={t("factoryRoute.automationFailureAlertEmail")}
|
|
327
|
+
type="email"
|
|
328
|
+
value={automationFailureAlertEmail}
|
|
329
|
+
onChange={(event) =>
|
|
330
|
+
setAutomationFailureAlertEmail(event.target.value)
|
|
331
|
+
}
|
|
332
|
+
placeholder={t(
|
|
333
|
+
"factoryRoute.automationFailureAlertEmailPlaceholder",
|
|
334
|
+
)}
|
|
335
|
+
className={fieldControlClass}
|
|
336
|
+
/>
|
|
337
|
+
}
|
|
338
|
+
/>
|
|
339
|
+
<SettingsRow
|
|
340
|
+
label={t("factoryRoute.automationFailureEmailReadiness")}
|
|
341
|
+
description={t("factoryRoute.automationEmailReadinessHint")}
|
|
342
|
+
control={
|
|
343
|
+
<span className="rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground">
|
|
344
|
+
{(query.data as TriageConfig | undefined)?.emailReadiness
|
|
345
|
+
?.status ?? "unknown"}
|
|
346
|
+
</span>
|
|
347
|
+
}
|
|
348
|
+
/>
|
|
349
|
+
</SettingsGroup>
|
|
350
|
+
|
|
351
|
+
<div className="flex flex-wrap items-center justify-end gap-3">
|
|
352
|
+
<Button
|
|
353
|
+
onClick={() => void saveSettings()}
|
|
354
|
+
disabled={mutation.isPending || !configLoaded}
|
|
355
|
+
>
|
|
356
|
+
{mutation.isPending && <IconLoader2 className="animate-spin" />}
|
|
357
|
+
{t("triage.saveSettings")}
|
|
358
|
+
</Button>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<SchedulerHealthStatus
|
|
362
|
+
health={schedulerHealthQuery.data}
|
|
363
|
+
isError={schedulerHealthQuery.isError}
|
|
364
|
+
error={schedulerHealthQuery.error}
|
|
365
|
+
t={t}
|
|
366
|
+
/>
|
|
367
|
+
</div>
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function FactorySettingsSkeleton({ t }: { t: ReturnType<typeof useT> }) {
|
|
372
|
+
return (
|
|
373
|
+
<div className="space-y-6" aria-label={t("triage.loading")}>
|
|
374
|
+
{[2, 11, 3].map((rowCount, index) => (
|
|
375
|
+
<div key={index} className="grid gap-2">
|
|
376
|
+
<div className="grid gap-2">
|
|
377
|
+
{Array.from({ length: rowCount }).map((_, rowIndex) => (
|
|
378
|
+
<div
|
|
379
|
+
key={rowIndex}
|
|
380
|
+
className="flex min-h-20 items-center justify-between gap-4 rounded-xl bg-card px-5 py-4 shadow-sm sm:px-6"
|
|
381
|
+
>
|
|
382
|
+
<div className="h-3.5 w-36 animate-pulse rounded bg-muted" />
|
|
383
|
+
<div className="h-9 w-24 animate-pulse rounded bg-muted" />
|
|
384
|
+
</div>
|
|
385
|
+
))}
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
))}
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function SchedulerHealthStatus({
|
|
394
|
+
health,
|
|
395
|
+
isError,
|
|
396
|
+
error,
|
|
397
|
+
t,
|
|
398
|
+
}: {
|
|
399
|
+
health?: FactoryAutomationHealth;
|
|
400
|
+
isError: boolean;
|
|
401
|
+
error: unknown;
|
|
402
|
+
t: ReturnType<typeof useT>;
|
|
403
|
+
}) {
|
|
404
|
+
const healthLabel = isError
|
|
405
|
+
? t("factoryRoute.automationHealthError")
|
|
406
|
+
: health
|
|
407
|
+
? {
|
|
408
|
+
healthy: t("factoryRoute.automationHealthHealthy"),
|
|
409
|
+
stale: t("factoryRoute.automationHealthStale"),
|
|
410
|
+
error: t("factoryRoute.automationHealthError"),
|
|
411
|
+
"no-data": t("factoryRoute.automationHealthNoData"),
|
|
412
|
+
}[health.status]
|
|
413
|
+
: t("factoryRoute.automationHealthNoData");
|
|
414
|
+
const hasNoHeartbeat =
|
|
415
|
+
!isError &&
|
|
416
|
+
(!health || health.status === "no-data") &&
|
|
417
|
+
!health?.lastCheckedAt;
|
|
418
|
+
const healthDescription = isError
|
|
419
|
+
? `${t("factoryRoute.automationDiagnosticsLoadError")} ${error instanceof Error ? error.message : String(error)}`
|
|
420
|
+
: health?.lastError
|
|
421
|
+
? `${t("factoryRoute.automationHealthErrorDetail")}: ${health.lastError}`
|
|
422
|
+
: health?.status === "stale"
|
|
423
|
+
? t("factoryRoute.automationHealthStaleHint")
|
|
424
|
+
: hasNoHeartbeat
|
|
425
|
+
? t("factoryRoute.automationHealthNoDataHint")
|
|
426
|
+
: undefined;
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
<SettingsGroup variant="soft">
|
|
430
|
+
<SettingsRow
|
|
431
|
+
label={t("factoryRoute.automationHealthTitle")}
|
|
432
|
+
description={
|
|
433
|
+
healthDescription ?? t("factoryRoute.automationHealthDescription")
|
|
434
|
+
}
|
|
435
|
+
control={
|
|
436
|
+
<div className="flex flex-wrap items-center justify-end gap-x-4 gap-y-1">
|
|
437
|
+
<span className="rounded-full bg-muted px-2.5 py-1 text-xs font-medium">
|
|
438
|
+
{healthLabel}
|
|
439
|
+
</span>
|
|
440
|
+
{health?.lastCheckedAt && (
|
|
441
|
+
<span className="text-xs text-muted-foreground">
|
|
442
|
+
{t("factoryRoute.automationLastCheck")}:{" "}
|
|
443
|
+
{formatAutomationDate(health.lastCheckedAt)}
|
|
444
|
+
</span>
|
|
445
|
+
)}
|
|
446
|
+
{health?.lastDispatchedAt && (
|
|
447
|
+
<span className="text-xs text-muted-foreground">
|
|
448
|
+
{t("factoryRoute.automationLastDispatch")}:{" "}
|
|
449
|
+
{formatAutomationDate(health.lastDispatchedAt)}
|
|
450
|
+
</span>
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
453
|
+
}
|
|
454
|
+
/>
|
|
455
|
+
</SettingsGroup>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AgentToggleButton } from "@agent-native/core/client/agent-chat";
|
|
2
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
3
|
+
import { IconPlus } from "@tabler/icons-react";
|
|
4
|
+
import { Link, useLocation, useSearchParams } from "react-router";
|
|
5
|
+
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
|
|
8
|
+
export const FACTORY_AGENTS_HREF = "/factory?tab=agents";
|
|
9
|
+
export const FACTORY_SETTINGS_HREF = "/factory-settings";
|
|
10
|
+
export const FACTORY_NEW_HREF = "/factory?new=1";
|
|
11
|
+
|
|
12
|
+
export function FactoryWorkspaceActions({
|
|
13
|
+
onNewFactory,
|
|
14
|
+
}: {
|
|
15
|
+
onNewFactory?: () => void;
|
|
16
|
+
}) {
|
|
17
|
+
const t = useT();
|
|
18
|
+
const location = useLocation();
|
|
19
|
+
const [searchParams] = useSearchParams();
|
|
20
|
+
const onAgents =
|
|
21
|
+
location.pathname === "/agents" ||
|
|
22
|
+
(location.pathname === "/factory" &&
|
|
23
|
+
searchParams.get("tab") === "agents" &&
|
|
24
|
+
!searchParams.get("factoryId"));
|
|
25
|
+
const onSettings = location.pathname === "/factory-settings";
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex items-center gap-2">
|
|
29
|
+
<Button asChild variant={onAgents ? "default" : "outline"} size="sm">
|
|
30
|
+
<Link to={FACTORY_AGENTS_HREF}>{t("factoryRoute.agentsTab")}</Link>
|
|
31
|
+
</Button>
|
|
32
|
+
<Button asChild variant={onSettings ? "default" : "outline"} size="sm">
|
|
33
|
+
<Link to={FACTORY_SETTINGS_HREF}>
|
|
34
|
+
{t("factoryRoute.factorySettings")}
|
|
35
|
+
</Link>
|
|
36
|
+
</Button>
|
|
37
|
+
{onNewFactory ? (
|
|
38
|
+
<Button type="button" size="sm" onClick={onNewFactory}>
|
|
39
|
+
<IconPlus className="size-4" />
|
|
40
|
+
{t("factoryRoute.newFactory")}
|
|
41
|
+
</Button>
|
|
42
|
+
) : (
|
|
43
|
+
<Button asChild size="sm">
|
|
44
|
+
<Link to={FACTORY_NEW_HREF}>
|
|
45
|
+
<IconPlus className="size-4" />
|
|
46
|
+
{t("factoryRoute.newFactory")}
|
|
47
|
+
</Link>
|
|
48
|
+
</Button>
|
|
49
|
+
)}
|
|
50
|
+
<AgentToggleButton />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -42,6 +42,8 @@ function routeOwnsToolbar(pathname: string): boolean {
|
|
|
42
42
|
pathname === "/chat" ||
|
|
43
43
|
pathname.startsWith("/chat/") ||
|
|
44
44
|
pathname === "/factory" ||
|
|
45
|
+
pathname === "/factory-settings" ||
|
|
46
|
+
pathname === "/agents" ||
|
|
45
47
|
pathname === "/database" ||
|
|
46
48
|
pathname.startsWith("/extensions")
|
|
47
49
|
);
|
|
@@ -444,7 +444,9 @@ export function Sidebar({
|
|
|
444
444
|
const isActive =
|
|
445
445
|
item.href === "/"
|
|
446
446
|
? isChatRoute
|
|
447
|
-
:
|
|
447
|
+
: item.href === "/factory"
|
|
448
|
+
? location.pathname === "/factory"
|
|
449
|
+
: location.pathname.startsWith(item.href);
|
|
448
450
|
const link = (
|
|
449
451
|
<Link
|
|
450
452
|
to={item.href}
|
|
@@ -25,27 +25,27 @@ export function useNavigationState() {
|
|
|
25
25
|
view: viewForPath(pathname),
|
|
26
26
|
path: appPath(pathname),
|
|
27
27
|
...(threadId ? { threadId } : {}),
|
|
28
|
-
...(pathname
|
|
28
|
+
...(pathname === "/factory" && searchParams.get("factoryId")
|
|
29
29
|
? { factoryId: searchParams.get("factoryId") ?? undefined }
|
|
30
30
|
: {}),
|
|
31
|
-
...(pathname
|
|
31
|
+
...(pathname === "/factory" && searchParams.get("tab")
|
|
32
32
|
? { factoryTab: searchParams.get("tab") ?? undefined }
|
|
33
33
|
: {}),
|
|
34
|
-
...(pathname
|
|
34
|
+
...(pathname === "/factory" && searchParams.get("automationId")
|
|
35
35
|
? {
|
|
36
36
|
factoryAutomationId:
|
|
37
37
|
searchParams.get("automationId") ?? undefined,
|
|
38
38
|
}
|
|
39
39
|
: {}),
|
|
40
|
-
...(pathname
|
|
40
|
+
...(pathname === "/factory" && searchParams.get("auditRunId")
|
|
41
41
|
? {
|
|
42
42
|
factoryAuditRunId: searchParams.get("auditRunId") ?? undefined,
|
|
43
43
|
}
|
|
44
44
|
: {}),
|
|
45
|
-
...(pathname
|
|
45
|
+
...(pathname === "/factory" && searchParams.get("node")
|
|
46
46
|
? { factoryNodeId: searchParams.get("node") ?? undefined }
|
|
47
47
|
: {}),
|
|
48
|
-
...(pathname
|
|
48
|
+
...(pathname === "/factory" && searchParams.get("edge")
|
|
49
49
|
? { factoryEdgeId: searchParams.get("edge") ?? undefined }
|
|
50
50
|
: {}),
|
|
51
51
|
};
|
|
@@ -73,7 +73,8 @@ function viewForPath(pathname: string): string {
|
|
|
73
73
|
if (pathname.startsWith("/extensions")) return "extensions";
|
|
74
74
|
if (pathname.startsWith("/observability")) return "observability";
|
|
75
75
|
if (pathname.startsWith("/agents")) return "agents";
|
|
76
|
-
if (pathname
|
|
76
|
+
if (pathname === "/factory-settings") return "factory-settings";
|
|
77
|
+
if (pathname === "/factory") return "factory";
|
|
77
78
|
if (pathname.startsWith("/settings/agent") || pathname.startsWith("/agent")) {
|
|
78
79
|
return "agent";
|
|
79
80
|
}
|
|
@@ -96,6 +97,8 @@ function pathForView(view?: string): string {
|
|
|
96
97
|
return "/observability";
|
|
97
98
|
case "factory":
|
|
98
99
|
return "/factory";
|
|
100
|
+
case "factory-settings":
|
|
101
|
+
return "/factory-settings";
|
|
99
102
|
case "agents":
|
|
100
103
|
return "/agents";
|
|
101
104
|
case "agent":
|
|
@@ -108,6 +108,8 @@ const messages = {
|
|
|
108
108
|
slackChannelPlaceholder: "For example, C0123456789",
|
|
109
109
|
slackChannelName: "Channel name",
|
|
110
110
|
slackChannelNamePlaceholder: "For example, product-agent-native-feedback",
|
|
111
|
+
builderSlackUserId: "@Builder.io Slack Member ID",
|
|
112
|
+
builderSlackUserIdPlaceholder: "For example, U096KN3EL2Y",
|
|
111
113
|
repository: "Repository",
|
|
112
114
|
repositoryPlaceholder: "For example, BuilderIO/agent-native",
|
|
113
115
|
enablePolling: "Enable one-minute observation polling",
|
|
@@ -165,6 +167,7 @@ const messages = {
|
|
|
165
167
|
rulesTab: "Rules",
|
|
166
168
|
automationsTab: "Automations",
|
|
167
169
|
agentsTab: "Agents",
|
|
170
|
+
factorySettings: "Factory Settings",
|
|
168
171
|
agenticAppsTitle: "Agentic apps",
|
|
169
172
|
agenticAppsEmpty: "No agentic apps in this workspace yet.",
|
|
170
173
|
createApp: "Create app",
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { IconArrowLeft } from "@tabler/icons-react";
|
|
3
|
+
import { Link } from "react-router";
|
|
4
|
+
|
|
1
5
|
import { FactoryAgentsView } from "@/components/factory/FactoryAgentsView";
|
|
6
|
+
import { FactoryWorkspaceActions } from "@/components/factory/FactoryWorkspaceActions";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
2
8
|
|
|
3
9
|
export default function AgentsRoute() {
|
|
10
|
+
const t = useT();
|
|
11
|
+
|
|
4
12
|
return (
|
|
5
|
-
<div className="h-full min-h-0 overflow-y-auto bg-background">
|
|
13
|
+
<div className="flex h-full min-h-0 flex-col overflow-y-auto bg-background">
|
|
14
|
+
<div className="flex items-center gap-3 px-4 py-4 lg:px-6">
|
|
15
|
+
<Button asChild type="button" variant="ghost" size="icon">
|
|
16
|
+
<Link to="/factory" aria-label={t("factoryRoute.backToFactories")}>
|
|
17
|
+
<IconArrowLeft className="size-4" />
|
|
18
|
+
</Link>
|
|
19
|
+
</Button>
|
|
20
|
+
<h1 className="text-sm font-medium sm:text-base">
|
|
21
|
+
{t("factoryRoute.agentsTitle")}
|
|
22
|
+
</h1>
|
|
23
|
+
<div className="ms-auto">
|
|
24
|
+
<FactoryWorkspaceActions />
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
6
27
|
<FactoryAgentsView />
|
|
7
28
|
</div>
|
|
8
29
|
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { IconArrowLeft } from "@tabler/icons-react";
|
|
3
|
+
import { Link } from "react-router";
|
|
4
|
+
|
|
5
|
+
import { FactorySettingsView } from "@/components/factory/FactorySettingsView";
|
|
6
|
+
import { FactoryWorkspaceActions } from "@/components/factory/FactoryWorkspaceActions";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
|
|
9
|
+
export function meta() {
|
|
10
|
+
return [{ title: "Factory" }];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function FactorySettingsRoute() {
|
|
14
|
+
const t = useT();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex h-full min-h-0 flex-col overflow-y-auto bg-background">
|
|
18
|
+
<div className="flex items-center gap-3 px-4 py-4 lg:px-6">
|
|
19
|
+
<Button asChild type="button" variant="ghost" size="icon">
|
|
20
|
+
<Link to="/factory" aria-label={t("factoryRoute.backToFactories")}>
|
|
21
|
+
<IconArrowLeft className="size-4" />
|
|
22
|
+
</Link>
|
|
23
|
+
</Button>
|
|
24
|
+
<h1 className="text-sm font-medium sm:text-base">
|
|
25
|
+
{t("factoryRoute.factorySettings")}
|
|
26
|
+
</h1>
|
|
27
|
+
<div className="ms-auto">
|
|
28
|
+
<FactoryWorkspaceActions />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<FactorySettingsView />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|