@agent-native/core 0.168.6 → 0.168.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/mutate-dashboard.ts +8 -1
  3. package/corpus/templates/calendar/actions/delete-event.ts +34 -18
  4. package/corpus/templates/calendar/actions/delete-events.ts +13 -1
  5. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +22 -3
  6. package/corpus/templates/calendar/app/lib/calendar-timezone.ts +19 -0
  7. package/corpus/templates/calendar/server/lib/google-api.ts +18 -1
  8. package/corpus/templates/clips/actions/finalize-meeting.ts +16 -0
  9. package/corpus/templates/clips/actions/list-meetings.ts +1 -0
  10. package/corpus/templates/clips/actions/list-recordings.ts +14 -0
  11. package/corpus/templates/clips/actions/sync-calendars.ts +10 -29
  12. package/corpus/templates/clips/app/components/library/recording-card.tsx +2 -2
  13. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +20 -3
  14. package/corpus/templates/clips/app/hooks/use-library.ts +1 -0
  15. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +12 -5
  16. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +28 -12
  17. package/corpus/templates/clips/desktop/src/lib/meeting-stop.ts +16 -0
  18. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +19 -9
  19. package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +5 -0
  20. package/corpus/templates/factory/AGENTS.md +12 -22
  21. package/corpus/templates/factory/README.md +11 -8
  22. package/corpus/templates/factory/actions/approve-factory-item.ts +35 -8
  23. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +24 -9
  24. package/corpus/templates/factory/actions/create-factory.ts +211 -0
  25. package/corpus/templates/factory/actions/evaluate-triage-item.ts +29 -5
  26. package/corpus/templates/factory/actions/get-factory-graph.ts +1 -1
  27. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -11
  28. package/corpus/templates/factory/actions/get-triage-config.ts +13 -12
  29. package/corpus/templates/factory/actions/get-triage-item.ts +28 -6
  30. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +33 -17
  31. package/corpus/templates/factory/actions/ingest-github-observation.ts +10 -4
  32. package/corpus/templates/factory/actions/list-factory-audit.ts +11 -4
  33. package/corpus/templates/factory/actions/list-factory-automations.ts +34 -12
  34. package/corpus/templates/factory/actions/list-triage-items.ts +11 -3
  35. package/corpus/templates/factory/actions/list-triage-rules.ts +11 -4
  36. package/corpus/templates/factory/actions/poll-github-sources.ts +24 -10
  37. package/corpus/templates/factory/actions/poll-sentry-errors.ts +26 -11
  38. package/corpus/templates/factory/actions/poll-slack-channel.ts +31 -13
  39. package/corpus/templates/factory/actions/reconcile-triage-run.ts +59 -9
  40. package/corpus/templates/factory/actions/record-triage-feedback.ts +14 -3
  41. package/corpus/templates/factory/actions/run-factory-automation.ts +12 -2
  42. package/corpus/templates/factory/actions/save-factory-automation.ts +82 -21
  43. package/corpus/templates/factory/actions/save-factory-graph.ts +10 -1
  44. package/corpus/templates/factory/actions/save-triage-config.ts +90 -36
  45. package/corpus/templates/factory/actions/save-triage-rule.ts +16 -3
  46. package/corpus/templates/factory/actions/start-builder-for-item.ts +28 -20
  47. package/corpus/templates/factory/actions/suggest-factory-rules.ts +11 -4
  48. package/corpus/templates/factory/actions/view-screen.ts +22 -30
  49. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +7 -2
  50. package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +7 -22
  51. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +260 -0
  52. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  53. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +2 -1
  54. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -3
  55. package/corpus/templates/factory/app/i18n/en-US.ts +18 -1
  56. package/corpus/templates/factory/app/routes/factory.tsx +78 -62
  57. package/corpus/templates/factory/app/routes/{factory-settings.tsx → new-factory.tsx} +12 -9
  58. package/corpus/templates/factory/server/db/schema.ts +18 -3
  59. package/corpus/templates/factory/server/factory-graph/contracts.ts +24 -0
  60. package/corpus/templates/factory/server/factory-graph/store.ts +16 -9
  61. package/corpus/templates/factory/server/lib/factory-automation-plan.ts +136 -0
  62. package/corpus/templates/factory/server/lib/factory-automation-repair.ts +31 -0
  63. package/corpus/templates/factory/server/lib/factory-scope.ts +416 -0
  64. package/corpus/templates/factory/server/lib/require-factory-automation.ts +11 -0
  65. package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -2
  66. package/corpus/templates/factory/server/plugins/factory-migrations.ts +80 -0
  67. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +142 -43
  68. package/corpus/templates/factory/server/triage/audit.ts +4 -0
  69. package/corpus/templates/factory/server/triage/ids.ts +4 -0
  70. package/dist/cli/native-dependencies.js +4 -1
  71. package/dist/client/AgentPanel.d.ts +5 -1
  72. package/dist/client/AgentPanel.js +9 -4
  73. package/dist/client/agent-chat/index.d.ts +1 -1
  74. package/dist/client/agent-chat/index.js +1 -1
  75. package/dist/client/chat-first/apps-rail.js +6 -6
  76. package/dist/client/chat-first/primary-nav.js +14 -6
  77. package/dist/client/integrations/IntegrationsPanel.d.ts +15 -0
  78. package/dist/client/integrations/IntegrationsPanel.js +29 -21
  79. package/dist/client/integrations/index.d.ts +1 -1
  80. package/dist/client/integrations/index.js +1 -1
  81. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  82. package/dist/collab/routes.d.ts +1 -1
  83. package/dist/notifications/routes.d.ts +1 -1
  84. package/dist/progress/routes.d.ts +1 -1
  85. package/dist/resources/handlers.d.ts +3 -3
  86. package/dist/server/ssr-handler.js +19 -1
  87. package/package.json +1 -1
@@ -41,18 +41,6 @@ export default defineAction({
41
41
  const screen: Record<string, unknown> = {};
42
42
  if (navigation) screen.navigation = navigation;
43
43
 
44
- if (
45
- navigation &&
46
- typeof navigation === "object" &&
47
- "view" in navigation &&
48
- navigation.view === "factory-settings"
49
- ) {
50
- screen.factorySettings = {
51
- scope: "workspace",
52
- path: "/factory-settings",
53
- };
54
- }
55
-
56
44
  if (
57
45
  navigation &&
58
46
  typeof navigation === "object" &&
@@ -63,25 +51,29 @@ export default defineAction({
63
51
  workspaceMemberIdentityFromContext(context),
64
52
  );
65
53
  const state = navigation as Record<string, unknown>;
66
- const factoryId =
67
- typeof state.factoryId === "string" && state.factoryId.trim()
68
- ? state.factoryId
69
- : DEFAULT_FACTORY_ID;
70
- const row = await readFactoryDefinition(orgId, factoryId);
71
- const fallback = defaultFactoryDefinition();
72
- const graph = row ? parseFactoryGraph(row.graphJson) : fallback.graph;
73
- const selectedNodeId =
74
- typeof state.factoryNodeId === "string" ? state.factoryNodeId : null;
75
- const selectedEdgeId =
76
- typeof state.factoryEdgeId === "string" ? state.factoryEdgeId : null;
54
+ if (state.creatingFactory === true) {
55
+ screen.factory = { creating: true };
56
+ } else {
57
+ const factoryId =
58
+ typeof state.factoryId === "string" && state.factoryId.trim()
59
+ ? state.factoryId
60
+ : DEFAULT_FACTORY_ID;
61
+ const row = await readFactoryDefinition(orgId, factoryId);
62
+ const fallback = defaultFactoryDefinition();
63
+ const graph = row ? parseFactoryGraph(row.graphJson) : fallback.graph;
64
+ const selectedNodeId =
65
+ typeof state.factoryNodeId === "string" ? state.factoryNodeId : null;
66
+ const selectedEdgeId =
67
+ typeof state.factoryEdgeId === "string" ? state.factoryEdgeId : null;
77
68
 
78
- screen.factory = {
79
- id: factoryId,
80
- name: row?.name ?? fallback.name,
81
- graphVersion: row?.graphVersion ?? graph.version,
82
- selectedNode: graph.nodes.find((node) => node.id === selectedNodeId),
83
- selectedEdge: graph.edges.find((edge) => edge.id === selectedEdgeId),
84
- };
69
+ screen.factory = {
70
+ id: factoryId,
71
+ name: row?.name ?? fallback.name,
72
+ graphVersion: row?.graphVersion ?? graph.version,
73
+ selectedNode: graph.nodes.find((node) => node.id === selectedNodeId),
74
+ selectedEdge: graph.edges.find((edge) => edge.id === selectedEdgeId),
75
+ };
76
+ }
85
77
 
86
78
  if (state.factoryTab === "agents") {
87
79
  const [apps, agents] = await Promise.all([
@@ -49,7 +49,11 @@ function formatAutomationDate(value: string | number | null | undefined) {
49
49
  return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString();
50
50
  }
51
51
 
52
- export function FactorySettingsView() {
52
+ export function FactorySettingsView({
53
+ factoryId = "product-feedback",
54
+ }: {
55
+ factoryId?: string;
56
+ }) {
53
57
  const t = useT();
54
58
  const [workspace, setWorkspace] = useState<"primary" | "secondary">(
55
59
  "primary",
@@ -68,7 +72,7 @@ export function FactorySettingsView() {
68
72
  useState(true);
69
73
  const [automationFailureAlertEmail, setAutomationFailureAlertEmail] =
70
74
  useState("");
71
- const query = useActionQuery("get-triage-config", {});
75
+ const query = useActionQuery("get-triage-config", { factoryId });
72
76
  const schedulerHealthQuery = useActionQuery<FactoryAutomationHealth>(
73
77
  "get-factory-automation-health",
74
78
  {},
@@ -105,6 +109,7 @@ export function FactorySettingsView() {
105
109
  }
106
110
  try {
107
111
  await mutation.mutateAsync({
112
+ factoryId,
108
113
  slackWorkspace: workspace,
109
114
  slackChannelId: channelId,
110
115
  slackChannelName: channelName,
@@ -6,14 +6,9 @@ import { Link, useLocation, useSearchParams } from "react-router";
6
6
  import { Button } from "@/components/ui/button";
7
7
 
8
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";
9
+ export const NEW_FACTORY_HREF = "/new-factory";
11
10
 
12
- export function FactoryWorkspaceActions({
13
- onNewFactory,
14
- }: {
15
- onNewFactory?: () => void;
16
- }) {
11
+ export function FactoryWorkspaceActions() {
17
12
  const t = useT();
18
13
  const location = useLocation();
19
14
  const [searchParams] = useSearchParams();
@@ -22,31 +17,21 @@ export function FactoryWorkspaceActions({
22
17
  (location.pathname === "/factory" &&
23
18
  searchParams.get("tab") === "agents" &&
24
19
  !searchParams.get("factoryId"));
25
- const onSettings = location.pathname === "/factory-settings";
20
+ const onNewFactoryPage = location.pathname === "/new-factory";
26
21
 
27
22
  return (
28
23
  <div className="flex items-center gap-2">
29
24
  <Button asChild variant={onAgents ? "default" : "outline"} size="sm">
30
25
  <Link to={FACTORY_AGENTS_HREF}>{t("factoryRoute.agentsTab")}</Link>
31
26
  </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}>
27
+ {!onNewFactoryPage ? (
28
+ <Button asChild variant="outline" size="sm">
29
+ <Link to={NEW_FACTORY_HREF}>
45
30
  <IconPlus className="size-4" />
46
31
  {t("factoryRoute.newFactory")}
47
32
  </Link>
48
33
  </Button>
49
- )}
34
+ ) : null}
50
35
  <AgentToggleButton />
51
36
  </div>
52
37
  );
@@ -0,0 +1,260 @@
1
+ import { useActionMutation } from "@agent-native/core/client/hooks";
2
+ import { useT } from "@agent-native/core/client/i18n";
3
+ import {
4
+ IconAlertCircle,
5
+ IconBrandGithub,
6
+ IconBrandSlack,
7
+ IconLoader2,
8
+ } from "@tabler/icons-react";
9
+ import { useState } from "react";
10
+ import { Link } from "react-router";
11
+ import { toast } from "sonner";
12
+
13
+ import { Button } from "@/components/ui/button";
14
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
15
+ import { Input } from "@/components/ui/input";
16
+ import { Label } from "@/components/ui/label";
17
+ import { Switch } from "@/components/ui/switch";
18
+ import { Textarea } from "@/components/ui/textarea";
19
+
20
+ type CreateFactoryResult = {
21
+ ok: boolean;
22
+ factoryId: string;
23
+ name: string;
24
+ };
25
+
26
+ export function NewFactoryForm({
27
+ onCreated,
28
+ }: {
29
+ onCreated: (factoryId: string) => void;
30
+ }) {
31
+ const t = useT();
32
+ const createMutation = useActionMutation("create-factory");
33
+ const [name, setName] = useState("");
34
+ const [description, setDescription] = useState("");
35
+ const [slackChannelId, setSlackChannelId] = useState("");
36
+ const [slackChannelName, setSlackChannelName] = useState("");
37
+ const [observeSlack, setObserveSlack] = useState(false);
38
+ const [repository, setRepository] = useState("");
39
+ const [observeGithub, setObserveGithub] = useState(false);
40
+ const [sentryOrgSlug, setSentryOrgSlug] = useState("");
41
+ const [sentryProjectSlug, setSentryProjectSlug] = useState("");
42
+ const [sentryEnvironment, setSentryEnvironment] = useState("");
43
+ const [observeSentry, setObserveSentry] = useState(false);
44
+
45
+ async function handleSubmit(event: React.FormEvent) {
46
+ event.preventDefault();
47
+ const trimmedName = name.trim();
48
+ if (!trimmedName) {
49
+ toast.error(t("factoryRoute.createFactoryNameRequired"));
50
+ return;
51
+ }
52
+ try {
53
+ const result = (await createMutation.mutateAsync({
54
+ name: trimmedName,
55
+ ...(description.trim() ? { description: description.trim() } : {}),
56
+ ...(slackChannelId.trim()
57
+ ? { slackChannelId: slackChannelId.trim() }
58
+ : {}),
59
+ ...(slackChannelName.trim()
60
+ ? { slackChannelName: slackChannelName.trim() }
61
+ : {}),
62
+ ...(observeSlack ? { observeSlack: true } : {}),
63
+ ...(repository.trim() ? { repository: repository.trim() } : {}),
64
+ ...(observeGithub ? { observeGithub: true } : {}),
65
+ ...(sentryOrgSlug.trim()
66
+ ? { sentryOrgSlug: sentryOrgSlug.trim() }
67
+ : {}),
68
+ ...(sentryProjectSlug.trim()
69
+ ? { sentryProjectSlug: sentryProjectSlug.trim() }
70
+ : {}),
71
+ ...(sentryEnvironment.trim()
72
+ ? { sentryEnvironment: sentryEnvironment.trim() }
73
+ : {}),
74
+ ...(observeSentry ? { observeSentry: true } : {}),
75
+ })) as CreateFactoryResult;
76
+ toast.success(t("factoryRoute.createFactorySuccess"));
77
+ onCreated(result.factoryId);
78
+ } catch (error) {
79
+ toast.error(
80
+ error instanceof Error
81
+ ? error.message
82
+ : t("factoryRoute.createFactoryFailed"),
83
+ );
84
+ }
85
+ }
86
+
87
+ return (
88
+ <form
89
+ className="mx-auto flex w-full max-w-2xl flex-col gap-6"
90
+ onSubmit={(event) => void handleSubmit(event)}
91
+ >
92
+ <div className="space-y-2">
93
+ <Label htmlFor="new-factory-name">
94
+ {t("factoryRoute.createFactoryNameLabel")}
95
+ </Label>
96
+ <Input
97
+ id="new-factory-name"
98
+ value={name}
99
+ onChange={(event) => setName(event.target.value)}
100
+ placeholder={t("factoryRoute.newFactory")}
101
+ autoFocus
102
+ />
103
+ </div>
104
+ <div className="space-y-2">
105
+ <Label htmlFor="new-factory-description">
106
+ {t("factoryRoute.createFactoryDescriptionLabel")}
107
+ </Label>
108
+ <Textarea
109
+ id="new-factory-description"
110
+ value={description}
111
+ onChange={(event) => setDescription(event.target.value)}
112
+ rows={2}
113
+ />
114
+ </div>
115
+ <div className="space-y-4">
116
+ <Card>
117
+ <CardHeader className="px-4 pb-0 pt-4">
118
+ <CardTitle className="flex items-center gap-2 text-base">
119
+ <IconBrandSlack className="size-4" />
120
+ {t("factoryRoute.createFactorySlackSourceOptional")}
121
+ </CardTitle>
122
+ </CardHeader>
123
+ <CardContent className="space-y-3 px-4 pb-4">
124
+ <div className="space-y-2">
125
+ <Label htmlFor="new-factory-slack-channel">
126
+ {t("triage.slackChannelId")}
127
+ </Label>
128
+ <Input
129
+ id="new-factory-slack-channel"
130
+ value={slackChannelId}
131
+ onChange={(event) => setSlackChannelId(event.target.value)}
132
+ placeholder={t("triage.slackChannelPlaceholder")}
133
+ />
134
+ </div>
135
+ <div className="space-y-2">
136
+ <Label htmlFor="new-factory-slack-channel-name">
137
+ {t("triage.slackChannelName")}
138
+ </Label>
139
+ <Input
140
+ id="new-factory-slack-channel-name"
141
+ value={slackChannelName}
142
+ onChange={(event) => setSlackChannelName(event.target.value)}
143
+ placeholder={t("triage.slackChannelNamePlaceholder")}
144
+ />
145
+ </div>
146
+ <div className="flex items-center justify-between gap-3">
147
+ <Label htmlFor="new-factory-observe-slack">
148
+ {t("factoryRoute.createFactoryObserveSlack")}
149
+ </Label>
150
+ <Switch
151
+ id="new-factory-observe-slack"
152
+ checked={observeSlack}
153
+ onCheckedChange={(checked) => setObserveSlack(checked === true)}
154
+ />
155
+ </div>
156
+ </CardContent>
157
+ </Card>
158
+ <Card>
159
+ <CardHeader className="px-4 pb-0 pt-4">
160
+ <CardTitle className="flex items-center gap-2 text-base">
161
+ <IconBrandGithub className="size-4" />
162
+ {t("factoryRoute.createFactoryGithubSourceOptional")}
163
+ </CardTitle>
164
+ </CardHeader>
165
+ <CardContent className="space-y-3 px-4 pb-4">
166
+ <div className="space-y-2">
167
+ <Label htmlFor="new-factory-repository">
168
+ {t("triage.repository")}
169
+ </Label>
170
+ <Input
171
+ id="new-factory-repository"
172
+ value={repository}
173
+ onChange={(event) => setRepository(event.target.value)}
174
+ placeholder={t("triage.repositoryPlaceholder")}
175
+ />
176
+ </div>
177
+ <div className="flex items-center justify-between gap-3">
178
+ <Label htmlFor="new-factory-observe-github">
179
+ {t("factoryRoute.createFactoryObserveGithub")}
180
+ </Label>
181
+ <Switch
182
+ id="new-factory-observe-github"
183
+ checked={observeGithub}
184
+ onCheckedChange={(checked) =>
185
+ setObserveGithub(checked === true)
186
+ }
187
+ />
188
+ </div>
189
+ </CardContent>
190
+ </Card>
191
+ <Card>
192
+ <CardHeader className="px-4 pb-0 pt-4">
193
+ <CardTitle className="flex items-center gap-2 text-base">
194
+ <IconAlertCircle className="size-4" />
195
+ {t("factoryRoute.createFactorySentrySourceOptional")}
196
+ </CardTitle>
197
+ </CardHeader>
198
+ <CardContent className="space-y-3 px-4 pb-4">
199
+ <div className="space-y-2">
200
+ <Label htmlFor="new-factory-sentry-org">
201
+ {t("triage.sentryOrgSlug")}
202
+ </Label>
203
+ <Input
204
+ id="new-factory-sentry-org"
205
+ value={sentryOrgSlug}
206
+ onChange={(event) => setSentryOrgSlug(event.target.value)}
207
+ placeholder={t("triage.sentryOrgPlaceholder")}
208
+ />
209
+ </div>
210
+ <div className="space-y-2">
211
+ <Label htmlFor="new-factory-sentry-project">
212
+ {t("triage.sentryProjectSlug")}
213
+ </Label>
214
+ <Input
215
+ id="new-factory-sentry-project"
216
+ value={sentryProjectSlug}
217
+ onChange={(event) => setSentryProjectSlug(event.target.value)}
218
+ placeholder={t("triage.sentryProjectPlaceholder")}
219
+ />
220
+ </div>
221
+ <div className="space-y-2">
222
+ <Label htmlFor="new-factory-sentry-environment">
223
+ {t("triage.sentryEnvironment")}
224
+ </Label>
225
+ <Input
226
+ id="new-factory-sentry-environment"
227
+ value={sentryEnvironment}
228
+ onChange={(event) => setSentryEnvironment(event.target.value)}
229
+ placeholder={t("triage.sentryEnvironmentPlaceholder")}
230
+ />
231
+ </div>
232
+ <div className="flex items-center justify-between gap-3">
233
+ <Label htmlFor="new-factory-observe-sentry">
234
+ {t("factoryRoute.createFactoryObserveSentry")}
235
+ </Label>
236
+ <Switch
237
+ id="new-factory-observe-sentry"
238
+ checked={observeSentry}
239
+ onCheckedChange={(checked) =>
240
+ setObserveSentry(checked === true)
241
+ }
242
+ />
243
+ </div>
244
+ </CardContent>
245
+ </Card>
246
+ </div>
247
+ <div className="flex justify-end gap-2">
248
+ <Button asChild type="button" variant="outline">
249
+ <Link to="/factory">{t("factoryRoute.createFactoryCancel")}</Link>
250
+ </Button>
251
+ <Button type="submit" disabled={createMutation.isPending}>
252
+ {createMutation.isPending ? (
253
+ <IconLoader2 className="size-4 animate-spin" />
254
+ ) : null}
255
+ {t("factoryRoute.createFactorySubmit")}
256
+ </Button>
257
+ </div>
258
+ </form>
259
+ );
260
+ }
@@ -42,7 +42,7 @@ function routeOwnsToolbar(pathname: string): boolean {
42
42
  pathname === "/chat" ||
43
43
  pathname.startsWith("/chat/") ||
44
44
  pathname === "/factory" ||
45
- pathname === "/factory-settings" ||
45
+ pathname === "/new-factory" ||
46
46
  pathname === "/agents" ||
47
47
  pathname === "/database" ||
48
48
  pathname.startsWith("/extensions")
@@ -445,7 +445,8 @@ export function Sidebar({
445
445
  item.href === "/"
446
446
  ? isChatRoute
447
447
  : item.href === "/factory"
448
- ? location.pathname === "/factory"
448
+ ? location.pathname === "/factory" ||
449
+ location.pathname === "/new-factory"
449
450
  : location.pathname.startsWith(item.href);
450
451
  const link = (
451
452
  <Link
@@ -13,6 +13,7 @@ export interface NavigationState {
13
13
  factoryAuditRunId?: string;
14
14
  factoryNodeId?: string;
15
15
  factoryEdgeId?: string;
16
+ creatingFactory?: boolean;
16
17
  }
17
18
 
18
19
  export function useNavigationState() {
@@ -48,6 +49,7 @@ export function useNavigationState() {
48
49
  ...(pathname === "/factory" && searchParams.get("edge")
49
50
  ? { factoryEdgeId: searchParams.get("edge") ?? undefined }
50
51
  : {}),
52
+ ...(pathname === "/new-factory" ? { creatingFactory: true } : {}),
51
53
  };
52
54
  },
53
55
  getCommandPath: (command) =>
@@ -73,7 +75,7 @@ function viewForPath(pathname: string): string {
73
75
  if (pathname.startsWith("/extensions")) return "extensions";
74
76
  if (pathname.startsWith("/observability")) return "observability";
75
77
  if (pathname.startsWith("/agents")) return "agents";
76
- if (pathname === "/factory-settings") return "factory-settings";
78
+ if (pathname === "/new-factory") return "factory";
77
79
  if (pathname === "/factory") return "factory";
78
80
  if (pathname.startsWith("/settings/agent") || pathname.startsWith("/agent")) {
79
81
  return "agent";
@@ -97,8 +99,6 @@ function pathForView(view?: string): string {
97
99
  return "/observability";
98
100
  case "factory":
99
101
  return "/factory";
100
- case "factory-settings":
101
- return "/factory-settings";
102
102
  case "agents":
103
103
  return "/agents";
104
104
  case "agent":
@@ -133,6 +133,21 @@ const messages = {
133
133
  newFactory: "New agent factory",
134
134
  newFactoryDescription:
135
135
  "Describe the work this factory turns into shipped outcomes.",
136
+ createFactoryTitle: "New Factory",
137
+ createFactorySubmit: "Create factory",
138
+ createFactoryCancel: "Cancel",
139
+ createFactoryNameLabel: "Name",
140
+ createFactoryDescriptionLabel: "Description (optional)",
141
+ createFactorySourcesOptional: "Sources (optional)",
142
+ createFactorySlackSourceOptional: "Slack source (optional)",
143
+ createFactoryGithubSourceOptional: "GitHub source (optional)",
144
+ createFactorySentrySourceOptional: "Sentry source (optional)",
145
+ createFactoryObserveSlack: "Observe Slack channel",
146
+ createFactoryObserveGithub: "Observe GitHub issues",
147
+ createFactoryObserveSentry: "Observe Sentry errors",
148
+ createFactorySuccess: "Factory created",
149
+ createFactoryFailed: "Could not create factory",
150
+ createFactoryNameRequired: "Name is required",
136
151
  selectFactory: "Select factory",
137
152
  backToFactories: "Back to factories",
138
153
  workspaceIntegrations: "Workspace integrations",
@@ -167,7 +182,7 @@ const messages = {
167
182
  rulesTab: "Rules",
168
183
  automationsTab: "Automations",
169
184
  agentsTab: "Agents",
170
- factorySettings: "Factory Settings",
185
+ factorySettings: "Settings",
171
186
  agenticAppsTitle: "Agentic apps",
172
187
  agenticAppsEmpty: "No agentic apps in this workspace yet.",
173
188
  createApp: "Create app",
@@ -260,6 +275,8 @@ const messages = {
260
275
  automationTimezone: "Timezone",
261
276
  selectAutomation: "Select an automation to edit.",
262
277
  automationModel: "Model",
278
+ automationDisplayName: "Display name",
279
+ automationDisplayNamePlaceholder: "For example, Slack feedback",
263
280
  automationModelPlaceholder: "For example, gpt-5",
264
281
  automationSchedule: "Schedule",
265
282
  automationSchedulePlaceholder: "For example, every weekday at 9:00 AM",