@agent-native/core 0.168.7 → 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 (84) 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/sync-calendars.ts +10 -29
  11. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +28 -12
  12. package/corpus/templates/clips/desktop/src/lib/meeting-stop.ts +16 -0
  13. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +19 -9
  14. package/corpus/templates/factory/.agents/skills/factory-graphs/SKILL.md +5 -0
  15. package/corpus/templates/factory/AGENTS.md +12 -22
  16. package/corpus/templates/factory/README.md +11 -8
  17. package/corpus/templates/factory/actions/approve-factory-item.ts +35 -8
  18. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +24 -9
  19. package/corpus/templates/factory/actions/create-factory.ts +211 -0
  20. package/corpus/templates/factory/actions/evaluate-triage-item.ts +29 -5
  21. package/corpus/templates/factory/actions/get-factory-graph.ts +1 -1
  22. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -11
  23. package/corpus/templates/factory/actions/get-triage-config.ts +13 -12
  24. package/corpus/templates/factory/actions/get-triage-item.ts +28 -6
  25. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +33 -17
  26. package/corpus/templates/factory/actions/ingest-github-observation.ts +10 -4
  27. package/corpus/templates/factory/actions/list-factory-audit.ts +11 -4
  28. package/corpus/templates/factory/actions/list-factory-automations.ts +34 -12
  29. package/corpus/templates/factory/actions/list-triage-items.ts +11 -3
  30. package/corpus/templates/factory/actions/list-triage-rules.ts +11 -4
  31. package/corpus/templates/factory/actions/poll-github-sources.ts +24 -10
  32. package/corpus/templates/factory/actions/poll-sentry-errors.ts +26 -11
  33. package/corpus/templates/factory/actions/poll-slack-channel.ts +31 -13
  34. package/corpus/templates/factory/actions/reconcile-triage-run.ts +59 -9
  35. package/corpus/templates/factory/actions/record-triage-feedback.ts +14 -3
  36. package/corpus/templates/factory/actions/run-factory-automation.ts +12 -2
  37. package/corpus/templates/factory/actions/save-factory-automation.ts +82 -21
  38. package/corpus/templates/factory/actions/save-factory-graph.ts +10 -1
  39. package/corpus/templates/factory/actions/save-triage-config.ts +90 -36
  40. package/corpus/templates/factory/actions/save-triage-rule.ts +16 -3
  41. package/corpus/templates/factory/actions/start-builder-for-item.ts +28 -20
  42. package/corpus/templates/factory/actions/suggest-factory-rules.ts +11 -4
  43. package/corpus/templates/factory/actions/view-screen.ts +22 -30
  44. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +7 -2
  45. package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +7 -22
  46. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +260 -0
  47. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  48. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +2 -1
  49. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -3
  50. package/corpus/templates/factory/app/i18n/en-US.ts +18 -1
  51. package/corpus/templates/factory/app/routes/factory.tsx +78 -62
  52. package/corpus/templates/factory/app/routes/{factory-settings.tsx → new-factory.tsx} +12 -9
  53. package/corpus/templates/factory/server/db/schema.ts +18 -3
  54. package/corpus/templates/factory/server/factory-graph/contracts.ts +24 -0
  55. package/corpus/templates/factory/server/factory-graph/store.ts +16 -9
  56. package/corpus/templates/factory/server/lib/factory-automation-plan.ts +136 -0
  57. package/corpus/templates/factory/server/lib/factory-automation-repair.ts +31 -0
  58. package/corpus/templates/factory/server/lib/factory-scope.ts +416 -0
  59. package/corpus/templates/factory/server/lib/require-factory-automation.ts +11 -0
  60. package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -2
  61. package/corpus/templates/factory/server/plugins/factory-migrations.ts +80 -0
  62. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +142 -43
  63. package/corpus/templates/factory/server/triage/audit.ts +4 -0
  64. package/corpus/templates/factory/server/triage/ids.ts +4 -0
  65. package/dist/cli/native-dependencies.js +4 -1
  66. package/dist/client/AgentPanel.d.ts +5 -1
  67. package/dist/client/AgentPanel.js +9 -4
  68. package/dist/client/agent-chat/index.d.ts +1 -1
  69. package/dist/client/agent-chat/index.js +1 -1
  70. package/dist/client/chat-first/apps-rail.js +6 -6
  71. package/dist/client/chat-first/primary-nav.js +14 -6
  72. package/dist/client/integrations/IntegrationsPanel.d.ts +15 -0
  73. package/dist/client/integrations/IntegrationsPanel.js +29 -21
  74. package/dist/client/integrations/index.d.ts +1 -1
  75. package/dist/client/integrations/index.js +1 -1
  76. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  77. package/dist/collab/struct-routes.d.ts +1 -1
  78. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  79. package/dist/observability/routes.d.ts +3 -3
  80. package/dist/progress/routes.d.ts +1 -1
  81. package/dist/secrets/routes.d.ts +6 -6
  82. package/dist/server/realtime-token.d.ts +1 -1
  83. package/dist/server/transcribe-voice.d.ts +1 -1
  84. package/package.json +1 -1
@@ -29,6 +29,7 @@ import {
29
29
  } from "@/components/factory/FactoryCanvas";
30
30
  import { FactoryHistoryView } from "@/components/factory/FactoryHistoryView";
31
31
  import { FactoryInspector } from "@/components/factory/FactoryInspector";
32
+ import { FactorySettingsView } from "@/components/factory/FactorySettingsView";
32
33
  import { FactoryWorkspaceActions } from "@/components/factory/FactoryWorkspaceActions";
33
34
  import { TriageStatusPill } from "@/components/triage/triage-status-pill";
34
35
  import { Button } from "@/components/ui/button";
@@ -106,6 +107,7 @@ type WorkspaceTab =
106
107
  | "map"
107
108
  | "inbox"
108
109
  | "rules"
110
+ | "settings"
109
111
  | "automations"
110
112
  | "agents"
111
113
  | "audit"
@@ -124,6 +126,7 @@ type FactoryAutomationRun = {
124
126
  type FactoryAutomation = {
125
127
  id: string;
126
128
  name: string;
129
+ displayName: string;
127
130
  prompt?: string | null;
128
131
  body?: string | null;
129
132
  model?: string | null;
@@ -151,7 +154,6 @@ export default function FactoryRoute() {
151
154
  const activeTab = parseWorkspaceTab(searchParams.get("tab"));
152
155
  const selectedFactoryId = searchParams.get("factoryId");
153
156
  const factoryId = selectedFactoryId ?? DEFAULT_FACTORY_ID;
154
- const [creating, setCreating] = useState(false);
155
157
  const [draftGraph, setDraftGraph] = useState<FactoryCanvasGraph | null>(null);
156
158
  const [dirty, setDirty] = useState(false);
157
159
  const [saveError, setSaveError] = useState<string | null>(null);
@@ -183,7 +185,6 @@ export default function FactoryRoute() {
183
185
  nextFactoryId: string,
184
186
  options?: { tab?: WorkspaceTab; replace?: boolean },
185
187
  ) {
186
- setCreating(false);
187
188
  setDraftGraph(null);
188
189
  setDirty(false);
189
190
  setSelectedNodeId(null);
@@ -241,12 +242,12 @@ export default function FactoryRoute() {
241
242
  const factoryList = (factoryListQuery.data ?? []) as FactorySummary[];
242
243
 
243
244
  useEffect(() => {
244
- if (!graphData || creating || dirty) return;
245
+ if (!graphData || dirty) return;
245
246
  setDraftGraph(graphData.graph);
246
247
  setDirty(false);
247
248
  setSelectedNodeId(null);
248
249
  setSelectedEdgeId(null);
249
- }, [creating, dirty, graphData]);
250
+ }, [dirty, graphData]);
250
251
 
251
252
  useEffect(() => {
252
253
  setSelectedNodeId(searchParams.get("node"));
@@ -255,7 +256,6 @@ export default function FactoryRoute() {
255
256
 
256
257
  useEffect(() => {
257
258
  if (selectedFactoryId) return;
258
- setCreating(false);
259
259
  setDraftGraph(null);
260
260
  setDirty(false);
261
261
  setSelectedNodeId(null);
@@ -352,37 +352,6 @@ export default function FactoryRoute() {
352
352
  });
353
353
  }
354
354
 
355
- function startNewFactory() {
356
- const id = `factory-${Date.now().toString(36)}`;
357
- openFactory(id, { tab: "overview" });
358
- setCreating(true);
359
- setDraftGraph({
360
- version: 1,
361
- name: t("factoryRoute.newFactory"),
362
- description: t("factoryRoute.newFactoryDescription"),
363
- executionMode: "blueprint",
364
- nodes: [
365
- {
366
- id: "start",
367
- label: t("factoryRoute.newStep"),
368
- description: t("factoryRoute.newStepDescription"),
369
- kind: "decision",
370
- provider: "factory",
371
- position: { x: 240, y: 220 },
372
- },
373
- ],
374
- edges: [],
375
- });
376
- setDirty(true);
377
- setSelectedNodeId(null);
378
- setSelectedEdgeId(null);
379
- }
380
-
381
- useEffect(() => {
382
- if (searchParams.get("new") !== "1") return;
383
- startNewFactory();
384
- }, [searchParams]);
385
-
386
355
  async function saveGraph() {
387
356
  if (!graph || !selectedFactoryId) return;
388
357
  const submittedDraftRevision = draftRevisionRef.current;
@@ -392,15 +361,12 @@ export default function FactoryRoute() {
392
361
  factoryId: selectedFactoryId,
393
362
  name: graph.name,
394
363
  description: graph.description,
395
- prompt: creating ? "" : (graphData?.factory.prompt ?? ""),
364
+ prompt: graphData?.factory.prompt ?? "",
396
365
  source: "manual",
397
- changeSummary: creating
398
- ? "Created from the Factory visual editor."
399
- : "Updated in the Factory visual editor.",
400
- expectedGraphVersion: creating ? 0 : graph.version,
366
+ changeSummary: "Updated in the Factory visual editor.",
367
+ expectedGraphVersion: graph.version,
401
368
  graph,
402
369
  });
403
- setCreating(false);
404
370
  setDirty(draftRevisionRef.current !== submittedDraftRevision);
405
371
  setSaveConflictRemoteGraph(null);
406
372
  await Promise.all([graphQuery.refetch(), factoryListQuery.refetch()]);
@@ -445,13 +411,11 @@ export default function FactoryRoute() {
445
411
  setSaveConflictRemoteGraph(null);
446
412
  setSaveError(null);
447
413
  setDirty(false);
448
- setCreating(false);
449
414
  setSelectedNodeId(null);
450
415
  setSelectedEdgeId(null);
451
416
  }
452
417
 
453
418
  async function handleFactoryRestored(result?: { graph: FactoryCanvasGraph }) {
454
- setCreating(false);
455
419
  if (result?.graph) setDraftGraph(result.graph);
456
420
  else setDraftGraph(null);
457
421
  setDirty(false);
@@ -468,9 +432,10 @@ export default function FactoryRoute() {
468
432
  }
469
433
  }
470
434
 
471
- if (searchParams.get("tab") === "settings") {
472
- return <Navigate to="/factory-settings" replace />;
435
+ if (searchParams.get("new") === "1") {
436
+ return <Navigate to="/new-factory" replace />;
473
437
  }
438
+
474
439
  if (selectedFactoryId && searchParams.get("tab") === "agents") {
475
440
  return <Navigate to="/factory?tab=agents" replace />;
476
441
  }
@@ -488,7 +453,7 @@ export default function FactoryRoute() {
488
453
  {t("factoryRoute.agentsTitle")}
489
454
  </h1>
490
455
  <div className="ms-auto">
491
- <FactoryWorkspaceActions onNewFactory={startNewFactory} />
456
+ <FactoryWorkspaceActions />
492
457
  </div>
493
458
  </div>
494
459
  <FactoryAgentsView />
@@ -502,7 +467,7 @@ export default function FactoryRoute() {
502
467
  <section className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6 p-4 lg:p-6">
503
468
  <div className="flex flex-wrap items-end justify-between gap-4">
504
469
  <h1 className="text-2xl font-semibold tracking-tight">Factories</h1>
505
- <FactoryWorkspaceActions onNewFactory={startNewFactory} />
470
+ <FactoryWorkspaceActions />
506
471
  </div>
507
472
 
508
473
  {factoryListQuery.isError ? (
@@ -598,7 +563,7 @@ export default function FactoryRoute() {
598
563
  </Button>
599
564
  <div className="h-5 w-48 animate-pulse rounded bg-muted" />
600
565
  </div>
601
- <FactoryWorkspaceActions onNewFactory={startNewFactory} />
566
+ <FactoryWorkspaceActions />
602
567
  </div>
603
568
  </header>
604
569
  <main className="flex flex-1 items-center justify-center p-6">
@@ -645,7 +610,7 @@ export default function FactoryRoute() {
645
610
  </span>
646
611
  </div>
647
612
  <div className="flex items-center gap-2">
648
- <FactoryWorkspaceActions onNewFactory={startNewFactory} />
613
+ <FactoryWorkspaceActions />
649
614
  </div>
650
615
  </div>
651
616
  <div className="flex items-center gap-2 overflow-x-auto px-2 py-2 sm:px-4 lg:px-6">
@@ -695,6 +660,12 @@ export default function FactoryRoute() {
695
660
  >
696
661
  {t("factoryRoute.historyTab")}
697
662
  </TabButton>
663
+ <TabButton
664
+ active={activeTab === "settings"}
665
+ onClick={() => setActiveTab("settings")}
666
+ >
667
+ {t("factoryRoute.factorySettings")}
668
+ </TabButton>
698
669
  </nav>
699
670
  {activeTab === "audit" && (
700
671
  <Button
@@ -723,6 +694,7 @@ export default function FactoryRoute() {
723
694
  onOpenAutomations={() => setActiveTab("automations")}
724
695
  onOpenActivity={() => setActiveTab("audit")}
725
696
  onOpenFlow={() => setActiveTab("map")}
697
+ onOpenSettings={() => setActiveTab("settings")}
726
698
  />
727
699
  ) : activeTab === "map" ? (
728
700
  <div className="grid min-h-full gap-4 p-4 lg:p-6 xl:grid-cols-[minmax(0,1fr)_360px]">
@@ -769,9 +741,11 @@ export default function FactoryRoute() {
769
741
  />
770
742
  </div>
771
743
  ) : activeTab === "inbox" ? (
772
- <InboxView t={t} />
744
+ <InboxView factoryId={factoryId} t={t} />
773
745
  ) : activeTab === "rules" ? (
774
- <RulesView t={t} />
746
+ <RulesView factoryId={factoryId} t={t} />
747
+ ) : activeTab === "settings" ? (
748
+ <FactorySettingsView factoryId={factoryId} />
775
749
  ) : activeTab === "automations" ? (
776
750
  <AutomationsView factoryId={factoryId} t={t} />
777
751
  ) : activeTab === "audit" ? (
@@ -817,6 +791,7 @@ function parseWorkspaceTab(value: string | null): WorkspaceTab {
817
791
  return value === "map" ||
818
792
  value === "inbox" ||
819
793
  value === "rules" ||
794
+ value === "settings" ||
820
795
  value === "automations" ||
821
796
  value === "agents" ||
822
797
  value === "audit" ||
@@ -834,6 +809,7 @@ function OverviewView({
834
809
  onOpenAutomations,
835
810
  onOpenActivity,
836
811
  onOpenFlow,
812
+ onOpenSettings,
837
813
  }: {
838
814
  graph: FactoryCanvasGraph;
839
815
  t: ReturnType<typeof useT>;
@@ -843,6 +819,7 @@ function OverviewView({
843
819
  onOpenAutomations: () => void;
844
820
  onOpenActivity: () => void;
845
821
  onOpenFlow: () => void;
822
+ onOpenSettings: () => void;
846
823
  }) {
847
824
  return (
848
825
  <div className="mx-auto flex w-full max-w-5xl flex-col gap-4 p-4 lg:p-6">
@@ -919,10 +896,12 @@ function OverviewView({
919
896
  <Button type="button" variant="outline" onClick={onOpenActivity}>
920
897
  Activity
921
898
  </Button>
922
- <Button asChild type="button" variant="outline">
923
- <Link to="/factory-settings">
924
- {t("factoryRoute.factorySettings")}
925
- </Link>
899
+ <Button
900
+ type="button"
901
+ variant="outline"
902
+ onClick={() => onOpenSettings?.()}
903
+ >
904
+ {t("factoryRoute.factorySettings")}
926
905
  </Button>
927
906
  <Button type="button" onClick={onOpenFlow}>
928
907
  {t("factoryRoute.editFlow")}
@@ -1015,6 +994,7 @@ function AutomationsView({
1015
994
  current &&
1016
995
  current.id === nextDraft.id &&
1017
996
  current.name === nextDraft.name &&
997
+ current.displayName === nextDraft.displayName &&
1018
998
  current.prompt === nextDraft.prompt &&
1019
999
  current.body === nextDraft.body &&
1020
1000
  current.model === nextDraft.model &&
@@ -1035,6 +1015,7 @@ function AutomationsView({
1035
1015
  factoryId,
1036
1016
  automationId: draft.id,
1037
1017
  name: draft.name,
1018
+ displayName: draft.displayName,
1038
1019
  prompt: draft.prompt ?? draft.body ?? "",
1039
1020
  model: draft.model ?? "",
1040
1021
  schedule: draft.schedule ?? "",
@@ -1101,8 +1082,11 @@ function AutomationsView({
1101
1082
  }`}
1102
1083
  onClick={() => selectAutomation(automation.id)}
1103
1084
  >
1104
- <span className="block truncate text-sm font-medium">
1105
- {automation.name}
1085
+ <span
1086
+ className="block truncate text-sm font-medium"
1087
+ title={automation.name}
1088
+ >
1089
+ {automation.displayName}
1106
1090
  </span>
1107
1091
  <span className="mt-1 block truncate text-xs text-muted-foreground">
1108
1092
  {automation.enabled
@@ -1171,6 +1155,22 @@ function AutomationsView({
1171
1155
  </p>
1172
1156
  ) : (
1173
1157
  <>
1158
+ <div className="grid gap-1.5">
1159
+ <Label htmlFor="factory-automation-display-name">
1160
+ {t("factoryRoute.automationDisplayName")}
1161
+ </Label>
1162
+ <Input
1163
+ id="factory-automation-display-name"
1164
+ value={draft.displayName}
1165
+ onChange={(event) =>
1166
+ setDraft({ ...draft, displayName: event.target.value })
1167
+ }
1168
+ placeholder={t(
1169
+ "factoryRoute.automationDisplayNamePlaceholder",
1170
+ )}
1171
+ disabled={draft.canUpdate === false}
1172
+ />
1173
+ </div>
1174
1174
  <div className="grid gap-4 md:grid-cols-2">
1175
1175
  <div className="grid gap-1.5">
1176
1176
  <Label htmlFor="factory-automation-model">
@@ -1407,7 +1407,13 @@ function formatModelName(model: string | null | undefined) {
1407
1407
  .join(" ");
1408
1408
  }
1409
1409
 
1410
- function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1410
+ function InboxView({
1411
+ factoryId,
1412
+ t,
1413
+ }: {
1414
+ factoryId: string;
1415
+ t: ReturnType<typeof useT>;
1416
+ }) {
1411
1417
  const [searchParams, setSearchParams] = useSearchParams();
1412
1418
  const [status, setStatus] = useState("");
1413
1419
  const [selectedId, setSelectedId] = useState<string | null>(
@@ -1417,6 +1423,7 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1417
1423
  const [verdict, setVerdict] = useState<Verdict | null>(null);
1418
1424
  const selectedRowRef = useRef<HTMLButtonElement | null>(null);
1419
1425
  const listQuery = useActionQuery("list-triage-items", {
1426
+ factoryId,
1420
1427
  limit: 50,
1421
1428
  ...(status.trim()
1422
1429
  ? {
@@ -1434,7 +1441,7 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1434
1441
  });
1435
1442
  const detailQuery = useActionQuery(
1436
1443
  "get-triage-item",
1437
- selectedId ? { itemId: selectedId } : undefined,
1444
+ selectedId ? { factoryId, itemId: selectedId } : undefined,
1438
1445
  { enabled: Boolean(selectedId) },
1439
1446
  );
1440
1447
  const feedbackMutation = useActionMutation("record-triage-feedback");
@@ -1657,6 +1664,7 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1657
1664
  ];
1658
1665
  if (!decision) return;
1659
1666
  approveMutation.mutate({
1667
+ factoryId,
1660
1668
  itemId: selectedItem.itemId ?? selectedItem.id ?? "",
1661
1669
  decisionId: decision.decisionId,
1662
1670
  confirm: true,
@@ -1711,6 +1719,7 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1711
1719
  onClick={() => {
1712
1720
  if (verdict)
1713
1721
  feedbackMutation.mutate({
1722
+ factoryId,
1714
1723
  decisionId: decision.decisionId,
1715
1724
  verdict,
1716
1725
  ...(feedbackNote.trim()
@@ -1738,11 +1747,17 @@ function InboxView({ t }: { t: ReturnType<typeof useT> }) {
1738
1747
  );
1739
1748
  }
1740
1749
 
1741
- function RulesView({ t }: { t: ReturnType<typeof useT> }) {
1750
+ function RulesView({
1751
+ factoryId,
1752
+ t,
1753
+ }: {
1754
+ factoryId: string;
1755
+ t: ReturnType<typeof useT>;
1756
+ }) {
1742
1757
  const [editingId, setEditingId] = useState<string | null>(null);
1743
1758
  const [name, setName] = useState("");
1744
1759
  const [prompt, setPrompt] = useState("");
1745
- const rulesQuery = useActionQuery("list-triage-rules", {});
1760
+ const rulesQuery = useActionQuery("list-triage-rules", { factoryId });
1746
1761
  const saveMutation = useActionMutation("save-triage-rule");
1747
1762
  const rules = (rulesQuery.data ?? []) as TriageRule[];
1748
1763
  function selectRule(rule: TriageRule) {
@@ -1828,6 +1843,7 @@ function RulesView({ t }: { t: ReturnType<typeof useT> }) {
1828
1843
  onClick={() => {
1829
1844
  if (!name.trim() || !prompt.trim()) return;
1830
1845
  saveMutation.mutate({
1846
+ factoryId,
1831
1847
  ...(editingId ? { id: editingId } : {}),
1832
1848
  name,
1833
1849
  description: "",
@@ -1,17 +1,14 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
2
  import { IconArrowLeft } from "@tabler/icons-react";
3
- import { Link } from "react-router";
3
+ import { Link, useNavigate } from "react-router";
4
4
 
5
- import { FactorySettingsView } from "@/components/factory/FactorySettingsView";
6
5
  import { FactoryWorkspaceActions } from "@/components/factory/FactoryWorkspaceActions";
6
+ import { NewFactoryForm } from "@/components/factory/NewFactoryForm";
7
7
  import { Button } from "@/components/ui/button";
8
8
 
9
- export function meta() {
10
- return [{ title: "Factory" }];
11
- }
12
-
13
- export default function FactorySettingsRoute() {
9
+ export default function NewFactoryRoute() {
14
10
  const t = useT();
11
+ const navigate = useNavigate();
15
12
 
16
13
  return (
17
14
  <div className="flex h-full min-h-0 flex-col overflow-y-auto bg-background">
@@ -22,13 +19,19 @@ export default function FactorySettingsRoute() {
22
19
  </Link>
23
20
  </Button>
24
21
  <h1 className="text-sm font-medium sm:text-base">
25
- {t("factoryRoute.factorySettings")}
22
+ {t("factoryRoute.createFactoryTitle")}
26
23
  </h1>
27
24
  <div className="ms-auto">
28
25
  <FactoryWorkspaceActions />
29
26
  </div>
30
27
  </div>
31
- <FactorySettingsView />
28
+ <section className="flex flex-1 flex-col px-4 pb-8 lg:px-6">
29
+ <NewFactoryForm
30
+ onCreated={(factoryId) =>
31
+ navigate(`/factory?factoryId=${encodeURIComponent(factoryId)}`)
32
+ }
33
+ />
34
+ </section>
32
35
  </div>
33
36
  );
34
37
  }
@@ -31,14 +31,20 @@ export const triageItems = table(
31
31
  updatedAt: text("updated_at").notNull().default(now()),
32
32
  ownerEmail: text("owner_email").notNull(),
33
33
  orgId: text("org_id"),
34
+ factoryId: text("factory_id"),
34
35
  },
35
36
  (item) => ({
36
- orgDedupeUnique: uniqueIndex("factory_items_org_dedupe_idx").on(
37
+ orgFactoryDedupeUnique: uniqueIndex(
38
+ "factory_items_org_factory_dedupe_idx",
39
+ ).on(item.orgId, item.factoryId, item.dedupeKey),
40
+ orgStatusIdx: index("factory_items_org_status_idx").on(
37
41
  item.orgId,
38
- item.dedupeKey,
42
+ item.status,
43
+ item.updatedAt,
39
44
  ),
40
- orgStatusIdx: index("factory_items_org_status_idx").on(
45
+ orgFactoryStatusIdx: index("factory_items_org_factory_status_idx").on(
41
46
  item.orgId,
47
+ item.factoryId,
42
48
  item.status,
43
49
  item.updatedAt,
44
50
  ),
@@ -58,6 +64,7 @@ export const triageRules = table("factory_rules", {
58
64
  updatedAt: text("updated_at").notNull().default(now()),
59
65
  ownerEmail: text("owner_email").notNull(),
60
66
  orgId: text("org_id"),
67
+ factoryId: text("factory_id"),
61
68
  });
62
69
 
63
70
  export const triageDecisions = table("factory_decisions", {
@@ -73,6 +80,7 @@ export const triageDecisions = table("factory_decisions", {
73
80
  createdAt: text("created_at").notNull().default(now()),
74
81
  ownerEmail: text("owner_email").notNull(),
75
82
  orgId: text("org_id"),
83
+ factoryId: text("factory_id"),
76
84
  });
77
85
 
78
86
  export const triageRuns = table("factory_runs", {
@@ -93,6 +101,7 @@ export const triageRuns = table("factory_runs", {
93
101
  error: text("error"),
94
102
  ownerEmail: text("owner_email").notNull(),
95
103
  orgId: text("org_id"),
104
+ factoryId: text("factory_id"),
96
105
  });
97
106
 
98
107
  export const triageFeedback = table("factory_feedback", {
@@ -103,6 +112,7 @@ export const triageFeedback = table("factory_feedback", {
103
112
  createdAt: text("created_at").notNull().default(now()),
104
113
  ownerEmail: text("owner_email").notNull(),
105
114
  orgId: text("org_id"),
115
+ factoryId: text("factory_id"),
106
116
  });
107
117
 
108
118
  export const triageConfig = table("factory_config", {
@@ -131,6 +141,7 @@ export const triageConfig = table("factory_config", {
131
141
  updatedAt: text("updated_at").notNull().default(now()),
132
142
  ownerEmail: text("owner_email").notNull(),
133
143
  orgId: text("org_id"),
144
+ factoryId: text("factory_id"),
134
145
  });
135
146
 
136
147
  export const factoryAuditEvents = table(
@@ -151,12 +162,16 @@ export const factoryAuditEvents = table(
151
162
  createdAt: text("created_at").notNull().default(now()),
152
163
  ownerEmail: text("owner_email").notNull(),
153
164
  orgId: text("org_id"),
165
+ factoryId: text("factory_id"),
154
166
  },
155
167
  (event) => ({
156
168
  orgCreatedIdx: index("factory_audit_events_org_created_idx").on(
157
169
  event.orgId,
158
170
  event.createdAt,
159
171
  ),
172
+ orgFactoryCreatedIdx: index(
173
+ "factory_audit_events_org_factory_created_idx",
174
+ ).on(event.orgId, event.factoryId, event.createdAt),
160
175
  runCreatedIdx: index("factory_audit_events_run_created_idx").on(
161
176
  event.orgId,
162
177
  event.automationRunId,
@@ -91,6 +91,30 @@ export function normalizeFactoryGraph(value: unknown): FactoryGraph {
91
91
  };
92
92
  }
93
93
 
94
+ export function minimalFactoryGraph(
95
+ name: string,
96
+ description = "",
97
+ ): FactoryGraph {
98
+ return {
99
+ version: 1,
100
+ name,
101
+ description,
102
+ executionMode: "blueprint",
103
+ nodes: [
104
+ {
105
+ id: "start",
106
+ label: "Start",
107
+ description:
108
+ "Add sources and rules to route work through this factory.",
109
+ kind: "decision",
110
+ provider: "factory",
111
+ position: { x: 240, y: 220 },
112
+ },
113
+ ],
114
+ edges: [],
115
+ };
116
+ }
117
+
94
118
  export function defaultFactoryGraph(): FactoryGraph {
95
119
  return {
96
120
  version: 1,
@@ -62,6 +62,7 @@ export async function readFactoryDefinition(orgId: string, factoryId: string) {
62
62
 
63
63
  export async function readFactoryMetrics(
64
64
  orgId: string,
65
+ factoryId: string,
65
66
  ): Promise<FactoryMetricSummary> {
66
67
  const [
67
68
  totalItems,
@@ -72,13 +73,18 @@ export async function readFactoryMetrics(
72
73
  runs,
73
74
  completedRuns,
74
75
  ] = await Promise.all([
75
- countRows(triageItems, orgId),
76
- countRows(triageItems, orgId, eq(triageItems.source, "slack")),
77
- countRows(triageItems, orgId, eq(triageItems.source, "github")),
78
- countRows(triageDecisions, orgId),
79
- countRows(triageItems, orgId, eq(triageItems.status, "needs_manual")),
80
- countRows(triageRuns, orgId),
81
- countRows(triageRuns, orgId, eq(triageRuns.status, "completed")),
76
+ countRows(triageItems, orgId, factoryId),
77
+ countRows(triageItems, orgId, factoryId, eq(triageItems.source, "slack")),
78
+ countRows(triageItems, orgId, factoryId, eq(triageItems.source, "github")),
79
+ countRows(triageDecisions, orgId, factoryId),
80
+ countRows(
81
+ triageItems,
82
+ orgId,
83
+ factoryId,
84
+ eq(triageItems.status, "needs_manual"),
85
+ ),
86
+ countRows(triageRuns, orgId, factoryId),
87
+ countRows(triageRuns, orgId, factoryId, eq(triageRuns.status, "completed")),
82
88
  ]);
83
89
 
84
90
  return {
@@ -95,11 +101,12 @@ export async function readFactoryMetrics(
95
101
  async function countRows(
96
102
  table: typeof triageItems | typeof triageDecisions | typeof triageRuns,
97
103
  orgId: string,
104
+ factoryId: string,
98
105
  extra?: SQL,
99
106
  ) {
100
107
  const where = extra
101
- ? and(eq(table.orgId, orgId), extra)
102
- : eq(table.orgId, orgId);
108
+ ? and(eq(table.orgId, orgId), eq(table.factoryId, factoryId), extra)
109
+ : and(eq(table.orgId, orgId), eq(table.factoryId, factoryId));
103
110
  const row = (
104
111
  await getDb().select({ value: count() }).from(table).where(where)
105
112
  )[0];