@agent-native/core 0.157.0 → 0.157.2
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/server/jobs/analytics-bigquery-backfill.ts +19 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
- package/corpus/templates/assets/app/global.css +4 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -2
- package/corpus/templates/brain/app/routes/sources.tsx +1 -2
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
- package/corpus/templates/calendar/server/handlers/bookings.ts +224 -93
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
- package/corpus/templates/clips/actions/create-folder.ts +7 -1
- package/corpus/templates/clips/actions/create-meeting.ts +6 -2
- package/corpus/templates/clips/actions/create-recording.ts +5 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/request-recording-access.ts +305 -0
- package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +75 -75
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
- package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -16
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +135 -18
- package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
- package/corpus/templates/clips/desktop/src/app.tsx +24 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +24 -9
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
- package/corpus/templates/clips/server/db/index.ts +1 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
- package/corpus/templates/clips/server/lib/emails.ts +23 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
- package/corpus/templates/clips/server/lib/recordings.ts +2 -1
- package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
- package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
- package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +133 -4
- package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
- package/corpus/templates/clips/shared/recording-link.ts +4 -0
- package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +6 -2
- package/corpus/templates/factory/actions/view-screen.ts +15 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
- package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
- package/corpus/templates/factory/app/routes/agents.tsx +9 -0
- package/corpus/templates/mail/server/plugins/auth.ts +0 -2
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +71 -22
- package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -0
- package/dist/client/AgentPanel.d.ts +13 -1
- package/dist/client/AgentPanel.js +12 -10
- package/dist/client/AssistantChat.js +25 -5
- package/dist/client/HighlightedCodeBlock.js +12 -2
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/chat/markdown-renderer.js +16 -3
- package/dist/client/chat-first/apps-rail.js +1 -1
- package/dist/client/components/ui/message-scroller.js +1 -1
- package/dist/client/settings/AccountSettingsCard.js +48 -19
- package/dist/client/sharing/ShareButton.d.ts +1 -2
- package/dist/client/sharing/ShareButton.js +5 -41
- package/dist/client/sharing/ShareDialog.js +5 -4
- package/dist/client/use-chat-threads.js +10 -14
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +1 -0
- package/dist/localization/default-messages.js +1 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +0 -2
- package/dist/server/auth.d.ts +1 -0
- package/dist/server/auth.js +18 -0
- package/dist/server/onboarding-html.d.ts +1 -0
- package/dist/server/onboarding-html.js +2 -88
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/shared/markdown-block-split.d.ts +16 -0
- package/dist/shared/markdown-block-split.js +81 -5
- package/dist/styles/agent-native.css +13 -0
- package/dist/voice/voice-cleanup-prompt.js +1 -0
- package/docs/content/cloneable-saas.mdx +24 -26
- package/package.json +3 -3
- package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
|
@@ -59,10 +59,17 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
59
59
|
const result = await reapExpiredUploads({
|
|
60
60
|
dryRun: query.dryRun === "1" || query.dryRun === "true",
|
|
61
61
|
});
|
|
62
|
-
if (
|
|
62
|
+
if (
|
|
63
|
+
result.failed > 0 ||
|
|
64
|
+
result.scratchKeysDeleted > 0 ||
|
|
65
|
+
result.resumableSessionsAborted > 0 ||
|
|
66
|
+
result.resumableCleanupFailed > 0
|
|
67
|
+
) {
|
|
63
68
|
console.log("[uploads] reaped expired uploads", {
|
|
64
69
|
failed: result.failed,
|
|
65
70
|
scratchKeysDeleted: result.scratchKeysDeleted,
|
|
71
|
+
resumableSessionsAborted: result.resumableSessionsAborted,
|
|
72
|
+
resumableCleanupFailed: result.resumableCleanupFailed,
|
|
66
73
|
});
|
|
67
74
|
}
|
|
68
75
|
return { ok: true, ...result };
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { withShareAttribution } from "./share-attribution";
|
|
14
14
|
|
|
15
|
+
/** Short-lived capability used by a signed-in private share viewer to request access. */
|
|
16
|
+
export const CLIPS_ACCESS_REQUEST_TOKEN_PREFIX = "clips-access-request";
|
|
17
|
+
export const CLIPS_ACCESS_REQUEST_TOKEN_TTL_SECONDS = 10 * 60;
|
|
18
|
+
|
|
15
19
|
/** Public share path for a recording, relative to the app base path. */
|
|
16
20
|
export function recordingSharePath(recordingId: string): string {
|
|
17
21
|
return `/share/${encodeURIComponent(recordingId)}`;
|
|
@@ -5,8 +5,9 @@ export const PRIVATE_SHARE_RESPONSE_HEADERS = {
|
|
|
5
5
|
"Referrer-Policy": "no-referrer",
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export function privateShareLoaderData<T>(payload: T) {
|
|
8
|
+
export function privateShareLoaderData<T>(payload: T, status = 200) {
|
|
9
9
|
return data(payload, {
|
|
10
|
+
status,
|
|
10
11
|
headers: PRIVATE_SHARE_RESPONSE_HEADERS,
|
|
11
12
|
});
|
|
12
13
|
}
|
|
@@ -6,6 +6,7 @@ import { useT } from "@agent-native/core/client/i18n";
|
|
|
6
6
|
import { ShareButton } from "@agent-native/core/client/sharing";
|
|
7
7
|
import { CreativeContextShareTab } from "@agent-native/creative-context/client";
|
|
8
8
|
import { PresenceBar } from "@agent-native/toolkit/collab-ui";
|
|
9
|
+
import { ShareTrigger } from "@agent-native/toolkit/sharing";
|
|
9
10
|
import type { DocumentSourceInfo } from "@shared/api";
|
|
10
11
|
import {
|
|
11
12
|
IconArrowBarDown,
|
|
@@ -31,7 +32,6 @@ import {
|
|
|
31
32
|
IconLink,
|
|
32
33
|
IconMessageCircle,
|
|
33
34
|
IconRefresh,
|
|
34
|
-
IconShare3,
|
|
35
35
|
IconTrash,
|
|
36
36
|
} from "@tabler/icons-react";
|
|
37
37
|
import { useQueryClient } from "@tanstack/react-query";
|
|
@@ -891,22 +891,13 @@ export function DocumentToolbar({
|
|
|
891
891
|
className="mr-1"
|
|
892
892
|
/>
|
|
893
893
|
{isLocalFileDocument ? (
|
|
894
|
-
<
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
className="h-9 gap-1.5 rounded-lg px-3"
|
|
894
|
+
<ShareTrigger
|
|
895
|
+
className="h-9 rounded-lg px-3"
|
|
896
|
+
pending={shareLocalFile.isPending}
|
|
898
897
|
disabled={shareLocalFile.isPending}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
<IconLoader2 className="h-4 w-4 animate-spin" />
|
|
903
|
-
) : (
|
|
904
|
-
<IconShare3 className="h-4 w-4" />
|
|
905
|
-
)}
|
|
906
|
-
<span className="hidden sm:inline">
|
|
907
|
-
{t("editor.toolbar.share")}
|
|
908
|
-
</span>
|
|
909
|
-
</Button>
|
|
898
|
+
label={t("editor.toolbar.share")}
|
|
899
|
+
onPress={() => void handleShareLocalFile()}
|
|
900
|
+
/>
|
|
910
901
|
) : (
|
|
911
902
|
<>
|
|
912
903
|
<ShareButton
|
|
@@ -35,7 +35,8 @@ decisions, feedback, agent runs, and provider audit records.
|
|
|
35
35
|
|
|
36
36
|
## Application state
|
|
37
37
|
|
|
38
|
-
- `navigation.view`: `factory`
|
|
38
|
+
- `navigation.view`: `factory` for the Factory workspace or `agents` for the
|
|
39
|
+
shared Agents sidebar surface.
|
|
39
40
|
- `navigation.factoryId`: selected Factory id when present.
|
|
40
41
|
- `navigation.factoryTab`: `map` | `inbox` | `rules` | `automations` | `agents` | `audit` | `settings`.
|
|
41
42
|
- `navigation.factoryAuditRunId`: selected automation run in the audit view when present.
|
|
@@ -43,6 +44,9 @@ decisions, feedback, agent runs, and provider audit records.
|
|
|
43
44
|
- A selected graph node or edge is part of `navigation` context. Read
|
|
44
45
|
`view-screen` before answering why a route exists or changing the selected
|
|
45
46
|
Factory.
|
|
47
|
+
- The `/agents` surface shows mounted agentic apps and reusable agent packs from
|
|
48
|
+
the same Dispatch SQL/action registry. Use `view-screen` there before
|
|
49
|
+
answering questions about the visible agent inventory.
|
|
46
50
|
|
|
47
51
|
## Action contract
|
|
48
52
|
|
|
@@ -76,8 +76,8 @@ approves a Factory item.
|
|
|
76
76
|
|
|
77
77
|
## Agents and agentic apps
|
|
78
78
|
|
|
79
|
-
Factory's top-level Agents tab is a shared workspace surface, not a
|
|
80
|
-
registry. It reads mounted agentic apps and their editable Dispatch metadata,
|
|
79
|
+
Factory's top-level **Agents** sidebar tab is a shared workspace surface, not a
|
|
80
|
+
second registry. It reads mounted agentic apps and their editable Dispatch metadata,
|
|
81
81
|
then embeds Dispatch's reusable-agent manager for create, chat, import, and
|
|
82
82
|
folder-backed pack editing. A simple agent's profile lives at
|
|
83
83
|
`agents/<slug>.md`; its optional context, references, and private skills live
|
|
@@ -86,6 +86,10 @@ Cowork-style folder, or another text-based agent setup. The importer strips
|
|
|
86
86
|
credentials, hooks, shell commands, and local environment settings, and All-app
|
|
87
87
|
imports remain subject to Dispatch approval policy.
|
|
88
88
|
|
|
89
|
+
The page uses the same two-column library treatment for mounted apps and
|
|
90
|
+
reusable agents. Empty states keep creation actions in the content area, and
|
|
91
|
+
agent-specific secondary actions live behind each row's overflow menu.
|
|
92
|
+
|
|
89
93
|
When editing a factory flow, an agent step can bind to either a shared reusable
|
|
90
94
|
agent or a ready mounted agentic app from the same workspace database. The map
|
|
91
95
|
stores the selected target type and id, while the graph remains a reviewable
|
|
@@ -82,6 +82,21 @@ export default defineAction({
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
if (
|
|
86
|
+
navigation &&
|
|
87
|
+
typeof navigation === "object" &&
|
|
88
|
+
"view" in navigation &&
|
|
89
|
+
navigation.view === "agents"
|
|
90
|
+
) {
|
|
91
|
+
const [apps, agents] = await Promise.all([
|
|
92
|
+
runDispatchAction("list-workspace-apps", {
|
|
93
|
+
includeAgentCards: false,
|
|
94
|
+
}),
|
|
95
|
+
runDispatchAction("list-workspace-resources", { kind: "agent" }),
|
|
96
|
+
]);
|
|
97
|
+
screen.agents = { apps, agents };
|
|
98
|
+
}
|
|
99
|
+
|
|
85
100
|
if (Object.keys(screen).length === 0) {
|
|
86
101
|
return "No application state found. Is the app running?";
|
|
87
102
|
}
|
|
@@ -1,33 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useActionMutation,
|
|
3
|
-
useActionQuery,
|
|
4
|
-
} from "@agent-native/core/client/hooks";
|
|
1
|
+
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
5
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
6
|
-
import { SimpleAgentsPanel } from "@agent-native/dispatch/components";
|
|
7
|
-
import { ActionQueryError } from "@agent-native/dispatch/components";
|
|
8
|
-
import { Badge } from "@agent-native/dispatch/components/ui/badge";
|
|
9
|
-
import {
|
|
10
|
-
Dialog,
|
|
11
|
-
DialogContent,
|
|
12
|
-
DialogDescription,
|
|
13
|
-
DialogFooter,
|
|
14
|
-
DialogHeader,
|
|
15
|
-
DialogTitle,
|
|
16
|
-
DialogTrigger,
|
|
17
|
-
} from "@agent-native/dispatch/components/ui/dialog";
|
|
18
3
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import {
|
|
4
|
+
ActionQueryError,
|
|
5
|
+
CreateAppPopover,
|
|
6
|
+
SimpleAgentsPanel,
|
|
7
|
+
WorkspaceAppCard,
|
|
8
|
+
} from "@agent-native/dispatch/components";
|
|
9
|
+
import { IconPlus } from "@tabler/icons-react";
|
|
25
10
|
|
|
26
11
|
import { Button } from "@/components/ui/button";
|
|
27
|
-
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
28
|
-
import { Input } from "@/components/ui/input";
|
|
29
|
-
import { Label } from "@/components/ui/label";
|
|
30
|
-
import { Textarea } from "@/components/ui/textarea";
|
|
31
12
|
|
|
32
13
|
interface WorkspaceAppSummary {
|
|
33
14
|
id: string;
|
|
@@ -40,88 +21,12 @@ interface WorkspaceAppSummary {
|
|
|
40
21
|
statusLabel?: string;
|
|
41
22
|
}
|
|
42
23
|
|
|
43
|
-
function
|
|
44
|
-
app,
|
|
45
|
-
onSaved,
|
|
46
|
-
t,
|
|
47
|
-
}: {
|
|
48
|
-
app: WorkspaceAppSummary;
|
|
49
|
-
onSaved: () => void;
|
|
50
|
-
t: ReturnType<typeof useT>;
|
|
51
|
-
}) {
|
|
52
|
-
const [open, setOpen] = useState(false);
|
|
53
|
-
const [name, setName] = useState(app.name);
|
|
54
|
-
const [description, setDescription] = useState(app.description ?? "");
|
|
55
|
-
const update = useActionMutation("update-workspace-app-metadata", {
|
|
56
|
-
onSuccess: () => {
|
|
57
|
-
toast.success(t("factoryRoute.appDetailsUpdated"));
|
|
58
|
-
setOpen(false);
|
|
59
|
-
onSaved();
|
|
60
|
-
},
|
|
61
|
-
onError: (error) => toast.error(error.message),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (!open) return;
|
|
66
|
-
setName(app.name);
|
|
67
|
-
setDescription(app.description ?? "");
|
|
68
|
-
}, [app, open]);
|
|
69
|
-
|
|
24
|
+
function CreateAppButton({ label }: { label: string }) {
|
|
70
25
|
return (
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</Button>
|
|
76
|
-
</DialogTrigger>
|
|
77
|
-
<DialogContent>
|
|
78
|
-
<DialogHeader>
|
|
79
|
-
<DialogTitle>{t("factoryRoute.editAppDetails")}</DialogTitle>
|
|
80
|
-
<DialogDescription className="sr-only">
|
|
81
|
-
{t("factoryRoute.editAppDetailsDescription")}
|
|
82
|
-
</DialogDescription>
|
|
83
|
-
</DialogHeader>
|
|
84
|
-
<div className="space-y-4 py-2">
|
|
85
|
-
<div className="space-y-2">
|
|
86
|
-
<Label htmlFor={`factory-app-name-${app.id}`}>
|
|
87
|
-
{t("factoryRoute.appName")}
|
|
88
|
-
</Label>
|
|
89
|
-
<Input
|
|
90
|
-
id={`factory-app-name-${app.id}`}
|
|
91
|
-
value={name}
|
|
92
|
-
onChange={(event) => setName(event.target.value)}
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
95
|
-
<div className="space-y-2">
|
|
96
|
-
<Label htmlFor={`factory-app-description-${app.id}`}>
|
|
97
|
-
{t("factoryRoute.appDescription")}
|
|
98
|
-
</Label>
|
|
99
|
-
<Textarea
|
|
100
|
-
id={`factory-app-description-${app.id}`}
|
|
101
|
-
value={description}
|
|
102
|
-
onChange={(event) => setDescription(event.target.value)}
|
|
103
|
-
rows={4}
|
|
104
|
-
/>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
<DialogFooter>
|
|
108
|
-
<Button
|
|
109
|
-
onClick={() =>
|
|
110
|
-
update.mutate({
|
|
111
|
-
appId: app.id,
|
|
112
|
-
name: name.trim(),
|
|
113
|
-
description: description.trim(),
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
disabled={!name.trim() || update.isPending}
|
|
117
|
-
>
|
|
118
|
-
{update.isPending
|
|
119
|
-
? t("factoryRoute.savingChanges")
|
|
120
|
-
: t("factoryRoute.saveChanges")}
|
|
121
|
-
</Button>
|
|
122
|
-
</DialogFooter>
|
|
123
|
-
</DialogContent>
|
|
124
|
-
</Dialog>
|
|
26
|
+
<Button size="sm">
|
|
27
|
+
<IconPlus size={15} />
|
|
28
|
+
{label}
|
|
29
|
+
</Button>
|
|
125
30
|
);
|
|
126
31
|
}
|
|
127
32
|
|
|
@@ -130,6 +35,7 @@ function AgenticAppsSection({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
130
35
|
includeAgentCards: false,
|
|
131
36
|
});
|
|
132
37
|
const apps = (query.data ?? []).filter((app) => !app.isDispatch);
|
|
38
|
+
const createAppLabel = t("factoryRoute.createApp");
|
|
133
39
|
|
|
134
40
|
if (query.isError) {
|
|
135
41
|
return (
|
|
@@ -141,80 +47,52 @@ function AgenticAppsSection({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
141
47
|
}
|
|
142
48
|
|
|
143
49
|
return (
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
<IconLayoutGrid className="size-4 text-muted-foreground" />
|
|
50
|
+
<section className="flex flex-col gap-4">
|
|
51
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
52
|
+
<h2 className="text-base font-medium text-foreground">
|
|
148
53
|
{t("factoryRoute.agenticAppsTitle")}
|
|
149
|
-
</
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
{app.description ? (
|
|
181
|
-
<div className="mt-1 text-sm text-muted-foreground">
|
|
182
|
-
{app.description}
|
|
183
|
-
</div>
|
|
184
|
-
) : null}
|
|
185
|
-
<div className="mt-1 font-mono text-[11px] text-muted-foreground/70">
|
|
186
|
-
{app.path}
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
<div className="flex shrink-0 items-center gap-1">
|
|
190
|
-
{app.url ? (
|
|
191
|
-
<Button variant="ghost" size="icon" asChild>
|
|
192
|
-
<a
|
|
193
|
-
href={app.url}
|
|
194
|
-
target="_blank"
|
|
195
|
-
rel="noreferrer"
|
|
196
|
-
aria-label={t("factoryRoute.openApp")}
|
|
197
|
-
>
|
|
198
|
-
<IconExternalLink size={16} />
|
|
199
|
-
</a>
|
|
200
|
-
</Button>
|
|
201
|
-
) : null}
|
|
202
|
-
<EditAppDialog
|
|
203
|
-
app={app}
|
|
204
|
-
t={t}
|
|
205
|
-
onSaved={() => void query.refetch()}
|
|
206
|
-
/>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
))}
|
|
210
|
-
</div>
|
|
211
|
-
) : (
|
|
212
|
-
<div className="rounded-xl border border-dashed px-6 py-10 text-center text-sm text-muted-foreground">
|
|
54
|
+
</h2>
|
|
55
|
+
{apps.length > 0 ? (
|
|
56
|
+
<CreateAppPopover
|
|
57
|
+
align="end"
|
|
58
|
+
trigger={<CreateAppButton label={createAppLabel} />}
|
|
59
|
+
onCreated={() => void query.refetch()}
|
|
60
|
+
/>
|
|
61
|
+
) : null}
|
|
62
|
+
</div>
|
|
63
|
+
{query.isLoading && apps.length === 0 ? (
|
|
64
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
65
|
+
{[0, 1, 2, 3].map((item) => (
|
|
66
|
+
<div
|
|
67
|
+
key={item}
|
|
68
|
+
className="h-24 animate-pulse rounded-2xl border bg-muted"
|
|
69
|
+
/>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
) : apps.length > 0 ? (
|
|
73
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
74
|
+
{apps.map((app) => (
|
|
75
|
+
<WorkspaceAppCard
|
|
76
|
+
key={app.id}
|
|
77
|
+
app={app}
|
|
78
|
+
className="rounded-2xl border bg-card px-4 py-4"
|
|
79
|
+
/>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
) : (
|
|
83
|
+
<div className="rounded-2xl border border-dashed bg-card px-4 py-12 text-center">
|
|
84
|
+
<div className="text-sm font-medium text-foreground">
|
|
213
85
|
{t("factoryRoute.agenticAppsEmpty")}
|
|
214
86
|
</div>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
87
|
+
<div className="mt-4 flex justify-center">
|
|
88
|
+
<CreateAppPopover
|
|
89
|
+
trigger={<CreateAppButton label={createAppLabel} />}
|
|
90
|
+
onCreated={() => void query.refetch()}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
</section>
|
|
218
96
|
);
|
|
219
97
|
}
|
|
220
98
|
|
|
@@ -222,18 +100,9 @@ export function FactoryAgentsView() {
|
|
|
222
100
|
const t = useT();
|
|
223
101
|
|
|
224
102
|
return (
|
|
225
|
-
<div className="
|
|
103
|
+
<div className="flex flex-col gap-8 p-6 lg:p-8">
|
|
226
104
|
<AgenticAppsSection t={t} />
|
|
227
|
-
<
|
|
228
|
-
<CardHeader>
|
|
229
|
-
<CardTitle className="text-base">
|
|
230
|
-
{t("factoryRoute.agentsTitle")}
|
|
231
|
-
</CardTitle>
|
|
232
|
-
</CardHeader>
|
|
233
|
-
<CardContent>
|
|
234
|
-
<SimpleAgentsPanel />
|
|
235
|
-
</CardContent>
|
|
236
|
-
</Card>
|
|
105
|
+
<SimpleAgentsPanel title={t("factoryRoute.agentsTitle")} />
|
|
237
106
|
</div>
|
|
238
107
|
);
|
|
239
108
|
}
|
|
@@ -12,6 +12,7 @@ import { APP_TITLE } from "@/lib/app-config";
|
|
|
12
12
|
const pageTitleKeys: Record<string, string> = {
|
|
13
13
|
"/": "navigation.chat",
|
|
14
14
|
"/chat": "navigation.chat",
|
|
15
|
+
"/agents": "navigation.agents",
|
|
15
16
|
"/observability": "navigation.observability",
|
|
16
17
|
"/agent": "settings.agentTitle",
|
|
17
18
|
"/settings": "navigation.settings",
|
|
@@ -52,7 +52,7 @@ export function Layout({ children }: LayoutProps) {
|
|
|
52
52
|
const navigate = useNavigate();
|
|
53
53
|
const t = useT();
|
|
54
54
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
55
|
-
const [sidebarCollapsed, setSidebarCollapsed] = useState(
|
|
55
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
56
56
|
const isChatRoute =
|
|
57
57
|
location.pathname === "/" ||
|
|
58
58
|
location.pathname === "/chat" ||
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
IconLayoutSidebarLeftCollapse,
|
|
18
18
|
IconLayoutSidebarLeftExpand,
|
|
19
19
|
IconGitPullRequest,
|
|
20
|
+
IconHierarchy2,
|
|
20
21
|
IconMessageCircle,
|
|
21
22
|
IconSearch,
|
|
22
23
|
IconSettings,
|
|
@@ -46,6 +47,12 @@ const navItems = [
|
|
|
46
47
|
href: "/factory",
|
|
47
48
|
view: "factory",
|
|
48
49
|
},
|
|
50
|
+
{
|
|
51
|
+
icon: IconHierarchy2,
|
|
52
|
+
labelKey: "navigation.agents",
|
|
53
|
+
href: "/agents",
|
|
54
|
+
view: "agents",
|
|
55
|
+
},
|
|
49
56
|
];
|
|
50
57
|
|
|
51
58
|
const bottomNavItems = [
|
|
@@ -72,6 +72,7 @@ function viewForPath(pathname: string): string {
|
|
|
72
72
|
if (pathname.startsWith("/database")) return "database";
|
|
73
73
|
if (pathname.startsWith("/extensions")) return "extensions";
|
|
74
74
|
if (pathname.startsWith("/observability")) return "observability";
|
|
75
|
+
if (pathname.startsWith("/agents")) return "agents";
|
|
75
76
|
if (pathname.startsWith("/factory")) return "factory";
|
|
76
77
|
if (pathname.startsWith("/settings/agent") || pathname.startsWith("/agent")) {
|
|
77
78
|
return "agent";
|
|
@@ -95,6 +96,8 @@ function pathForView(view?: string): string {
|
|
|
95
96
|
return "/observability";
|
|
96
97
|
case "factory":
|
|
97
98
|
return "/factory";
|
|
99
|
+
case "agents":
|
|
100
|
+
return "/agents";
|
|
98
101
|
case "agent":
|
|
99
102
|
return "/settings/agent";
|
|
100
103
|
case "settings":
|
|
@@ -40,6 +40,7 @@ const messages = {
|
|
|
40
40
|
unpinChat: "Unpin Chat",
|
|
41
41
|
},
|
|
42
42
|
navigation: {
|
|
43
|
+
agents: "Agents",
|
|
43
44
|
chat: "Chat",
|
|
44
45
|
collapseSidebar: "Collapse Sidebar",
|
|
45
46
|
database: "Database",
|
|
@@ -51,7 +52,7 @@ const messages = {
|
|
|
51
52
|
openNavigation: "Open navigation",
|
|
52
53
|
settings: "Settings",
|
|
53
54
|
team: "Team",
|
|
54
|
-
triage: "
|
|
55
|
+
triage: "Factories",
|
|
55
56
|
},
|
|
56
57
|
triage: {
|
|
57
58
|
title: "Factory",
|
|
@@ -165,6 +166,7 @@ const messages = {
|
|
|
165
166
|
agentsTab: "Agents",
|
|
166
167
|
agenticAppsTitle: "Agentic apps",
|
|
167
168
|
agenticAppsEmpty: "No agentic apps in this workspace yet.",
|
|
169
|
+
createApp: "Create app",
|
|
168
170
|
editAppDetails: "Edit app details",
|
|
169
171
|
editAppDetailsDescription:
|
|
170
172
|
"Update the shared display name and description for this workspace app.",
|
|
@@ -33,8 +33,6 @@ export default createAuthPlugin({
|
|
|
33
33
|
"Multi-account Gmail in a single unified inbox",
|
|
34
34
|
"Autonomous triage, archiving, and follow-ups",
|
|
35
35
|
],
|
|
36
|
-
runLocalCommand:
|
|
37
|
-
"npx @agent-native/core@latest create my-mail-app --template mail",
|
|
38
36
|
},
|
|
39
37
|
// Gmail Pub/Sub push notifications POST here from Google's servers — no
|
|
40
38
|
// user session. The handler itself verifies the OIDC token when
|
|
@@ -108,8 +108,17 @@ export function LocalCodebasePicker() {
|
|
|
108
108
|
if (removedCount > 0) {
|
|
109
109
|
await queryClient.invalidateQueries({ queryKey: ["resources"] });
|
|
110
110
|
}
|
|
111
|
+
|
|
112
|
+
const failedResult = results.find(
|
|
113
|
+
(result) => result.status === "rejected",
|
|
114
|
+
);
|
|
115
|
+
if (failedResult?.status === "rejected") {
|
|
116
|
+
throw failedResult.reason instanceof Error
|
|
117
|
+
? failedResult.reason
|
|
118
|
+
: new Error(t("raw.localCodebase.codebaseSyncFailed"));
|
|
119
|
+
}
|
|
111
120
|
},
|
|
112
|
-
[queryClient],
|
|
121
|
+
[queryClient, t],
|
|
113
122
|
);
|
|
114
123
|
|
|
115
124
|
useEffect(() => {
|
|
@@ -187,11 +196,12 @@ export function LocalCodebasePicker() {
|
|
|
187
196
|
handle: chosen.handle,
|
|
188
197
|
latest: null,
|
|
189
198
|
};
|
|
190
|
-
|
|
191
|
-
await cleanupLocalResources(active);
|
|
192
|
-
}
|
|
193
|
-
setActive(selection);
|
|
199
|
+
setSyncState({ kind: "syncing" });
|
|
194
200
|
try {
|
|
201
|
+
if (active && active.id !== selection.id) {
|
|
202
|
+
await cleanupLocalResources(active);
|
|
203
|
+
}
|
|
204
|
+
setActive(selection);
|
|
195
205
|
await syncSelection(selection);
|
|
196
206
|
} catch (err) {
|
|
197
207
|
const message =
|
|
@@ -207,16 +217,28 @@ export function LocalCodebasePicker() {
|
|
|
207
217
|
|
|
208
218
|
const clearSelection = useCallback(async () => {
|
|
209
219
|
const previous = active;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
setSyncState({ kind: "syncing" });
|
|
221
|
+
try {
|
|
222
|
+
if (previous) {
|
|
223
|
+
await cleanupLocalResources(previous);
|
|
224
|
+
}
|
|
225
|
+
await clearLocalCodebaseSelection();
|
|
226
|
+
setActive(null);
|
|
227
|
+
setSummary(null);
|
|
228
|
+
await syncAppState(null);
|
|
229
|
+
setSyncState({ kind: "idle" });
|
|
230
|
+
toast(t("raw.localCodebase.codebaseUnlinked"));
|
|
231
|
+
} catch (err) {
|
|
232
|
+
const message =
|
|
233
|
+
err instanceof Error
|
|
234
|
+
? err.message
|
|
235
|
+
: t("raw.localCodebase.codebaseSyncFailed");
|
|
236
|
+
setSyncState({ kind: "error", message });
|
|
237
|
+
toast.error(t("raw.localCodebase.codebaseSyncFailed"), {
|
|
238
|
+
description: message,
|
|
239
|
+
});
|
|
217
240
|
}
|
|
218
|
-
|
|
219
|
-
}, [active, cleanupLocalResources, syncAppState]);
|
|
241
|
+
}, [active, cleanupLocalResources, syncAppState, t]);
|
|
220
242
|
|
|
221
243
|
const resync = useCallback(async () => {
|
|
222
244
|
if (!active) return;
|
|
@@ -338,6 +360,20 @@ export function LocalCodebasePicker() {
|
|
|
338
360
|
</>
|
|
339
361
|
)}
|
|
340
362
|
|
|
363
|
+
{syncState.kind === "syncing" && (
|
|
364
|
+
<span
|
|
365
|
+
className={cn(
|
|
366
|
+
"inline-flex min-h-8 max-w-[280px] items-center gap-1.5 truncate text-xs",
|
|
367
|
+
statusClasses(syncState.kind),
|
|
368
|
+
)}
|
|
369
|
+
>
|
|
370
|
+
<IconRefresh className="size-3.5 shrink-0 animate-spin" />
|
|
371
|
+
<span className="truncate">
|
|
372
|
+
{t("raw.localCodebase.clearCodebase")}
|
|
373
|
+
</span>
|
|
374
|
+
</span>
|
|
375
|
+
)}
|
|
376
|
+
|
|
341
377
|
{syncState.kind === "error" && (
|
|
342
378
|
<span
|
|
343
379
|
className={cn(
|