@agent-native/dispatch 0.2.11 → 0.2.13
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/dist/actions/create-workspace-resource.js +4 -4
- package/dist/actions/create-workspace-resource.js.map +1 -1
- package/dist/actions/grant-workspace-resources-to-app.d.ts +3 -0
- package/dist/actions/grant-workspace-resources-to-app.d.ts.map +1 -0
- package/dist/actions/grant-workspace-resources-to-app.js +15 -0
- package/dist/actions/grant-workspace-resources-to-app.js.map +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +4 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/list-workspace-resource-options.d.ts +3 -0
- package/dist/actions/list-workspace-resource-options.d.ts.map +1 -0
- package/dist/actions/list-workspace-resource-options.js +15 -0
- package/dist/actions/list-workspace-resource-options.js.map +1 -0
- package/dist/actions/list-workspace-resources.js +2 -2
- package/dist/actions/list-workspace-resources.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +6 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.d.ts.map +1 -1
- package/dist/actions/view-screen.js +4 -0
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-keys-popover.js +3 -3
- package/dist/components/app-keys-popover.js.map +1 -1
- package/dist/components/create-app-popover.d.ts +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +66 -21
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/workspace-app-card.d.ts +6 -0
- package/dist/components/workspace-app-card.d.ts.map +1 -0
- package/dist/components/workspace-app-card.js +12 -0
- package/dist/components/workspace-app-card.js.map +1 -0
- package/dist/db/schema.js +2 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +15 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -0
- package/dist/lib/workspace-apps.js +9 -0
- package/dist/lib/workspace-apps.js.map +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +1 -5
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +3 -20
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/overview.d.ts.map +1 -1
- package/dist/routes/pages/overview.js +2 -20
- package/dist/routes/pages/overview.js.map +1 -1
- package/dist/routes/pages/workspace.d.ts.map +1 -1
- package/dist/routes/pages/workspace.js +17 -6
- package/dist/routes/pages/workspace.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts +1 -0
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +36 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/workspace-resources-store.d.ts +29 -1
- package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
- package/dist/server/lib/workspace-resources-store.js +46 -0
- package/dist/server/lib/workspace-resources-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.d.ts.map +1 -1
- package/dist/server/plugins/agent-chat.js +1 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +2 -0
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/create-workspace-resource.ts +4 -4
- package/src/actions/grant-workspace-resources-to-app.ts +16 -0
- package/src/actions/index.ts +4 -0
- package/src/actions/list-workspace-resource-options.ts +16 -0
- package/src/actions/list-workspace-resources.ts +2 -2
- package/src/actions/start-workspace-app-creation.ts +8 -1
- package/src/actions/view-screen.ts +4 -0
- package/src/components/app-keys-popover.tsx +3 -3
- package/src/components/create-app-popover.tsx +155 -22
- package/src/components/workspace-app-card.tsx +85 -0
- package/src/db/schema.ts +2 -2
- package/src/lib/workspace-apps.ts +21 -0
- package/src/routes/pages/apps.$appId.tsx +4 -17
- package/src/routes/pages/apps.tsx +6 -89
- package/src/routes/pages/overview.tsx +5 -84
- package/src/routes/pages/workspace.tsx +31 -5
- package/src/server/lib/app-creation-store.ts +52 -0
- package/src/server/lib/workspace-resources-store.ts +75 -1
- package/src/server/plugins/agent-chat.ts +1 -0
- package/src/server/plugins/integrations.ts +2 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { IconArrowUpRight, IconClockHour4 } from "@tabler/icons-react";
|
|
2
|
+
import { AppKeysPopover } from "@/components/app-keys-popover";
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import {
|
|
6
|
+
isPendingBuilderHref,
|
|
7
|
+
workspaceAppHref,
|
|
8
|
+
type WorkspaceAppSummary,
|
|
9
|
+
} from "@/lib/workspace-apps";
|
|
10
|
+
|
|
11
|
+
export function WorkspaceAppCard({
|
|
12
|
+
app,
|
|
13
|
+
className,
|
|
14
|
+
}: {
|
|
15
|
+
app: WorkspaceAppSummary;
|
|
16
|
+
className?: string;
|
|
17
|
+
}) {
|
|
18
|
+
const href = workspaceAppHref(app);
|
|
19
|
+
const openInNewTab = isPendingBuilderHref(app);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
aria-disabled={!href}
|
|
24
|
+
className={cn(
|
|
25
|
+
"group relative rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:opacity-60",
|
|
26
|
+
className,
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
{href ? (
|
|
30
|
+
<a
|
|
31
|
+
href={href}
|
|
32
|
+
target={openInNewTab ? "_blank" : undefined}
|
|
33
|
+
rel={openInNewTab ? "noreferrer" : undefined}
|
|
34
|
+
aria-label={`Open ${app.name}`}
|
|
35
|
+
className="absolute inset-0 z-0 rounded-lg"
|
|
36
|
+
/>
|
|
37
|
+
) : null}
|
|
38
|
+
|
|
39
|
+
<div className="pointer-events-none relative z-10 flex h-full items-start justify-between gap-3">
|
|
40
|
+
<div className="min-w-0">
|
|
41
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
42
|
+
<h3 className="truncate text-sm font-semibold text-foreground">
|
|
43
|
+
{app.name}
|
|
44
|
+
</h3>
|
|
45
|
+
{app.status === "pending" ? (
|
|
46
|
+
<Badge
|
|
47
|
+
variant="outline"
|
|
48
|
+
className="shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
49
|
+
>
|
|
50
|
+
<IconClockHour4 size={12} />
|
|
51
|
+
Building
|
|
52
|
+
</Badge>
|
|
53
|
+
) : null}
|
|
54
|
+
</div>
|
|
55
|
+
<p className="mt-1 truncate font-mono text-xs text-muted-foreground">
|
|
56
|
+
{app.path}
|
|
57
|
+
</p>
|
|
58
|
+
{app.status === "pending" && app.branchName ? (
|
|
59
|
+
<p className="mt-1 truncate text-xs text-muted-foreground">
|
|
60
|
+
Branch: {app.branchName}
|
|
61
|
+
</p>
|
|
62
|
+
) : null}
|
|
63
|
+
{app.description ? (
|
|
64
|
+
<p className="mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
|
|
65
|
+
{app.description}
|
|
66
|
+
</p>
|
|
67
|
+
) : null}
|
|
68
|
+
</div>
|
|
69
|
+
<div className="flex shrink-0 items-center gap-1">
|
|
70
|
+
{app.status === "ready" ? (
|
|
71
|
+
<div className="pointer-events-auto">
|
|
72
|
+
<AppKeysPopover appId={app.id} appName={app.name} />
|
|
73
|
+
</div>
|
|
74
|
+
) : null}
|
|
75
|
+
{href ? (
|
|
76
|
+
<IconArrowUpRight
|
|
77
|
+
size={16}
|
|
78
|
+
className="text-muted-foreground transition group-hover:text-foreground"
|
|
79
|
+
/>
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
package/src/db/schema.ts
CHANGED
|
@@ -130,13 +130,13 @@ export const vaultAuditLog = table("vault_audit_log", {
|
|
|
130
130
|
createdAt: integer("created_at").notNull(),
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
// ─── Workspace Resources: shared skills, instructions, agents ──────
|
|
133
|
+
// ─── Workspace Resources: shared skills, instructions, agents, knowledge ──────
|
|
134
134
|
|
|
135
135
|
export const workspaceResources = table("workspace_resources", {
|
|
136
136
|
id: text("id").primaryKey(),
|
|
137
137
|
ownerEmail: text("owner_email").notNull(),
|
|
138
138
|
orgId: text("org_id"),
|
|
139
|
-
kind: text("kind").notNull(), // "skill" | "instruction" | "agent"
|
|
139
|
+
kind: text("kind").notNull(), // "skill" | "instruction" | "agent" | "knowledge"
|
|
140
140
|
name: text("name").notNull(),
|
|
141
141
|
description: text("description"),
|
|
142
142
|
path: text("path").notNull(), // resource path, e.g. "skills/designer.md"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface WorkspaceAppSummary {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
path: string;
|
|
6
|
+
url?: string | null;
|
|
7
|
+
isDispatch?: boolean;
|
|
8
|
+
status?: "ready" | "pending";
|
|
9
|
+
statusLabel?: string;
|
|
10
|
+
builderUrl?: string | null;
|
|
11
|
+
branchName?: string | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
15
|
+
if (app.status === "pending") return app.builderUrl || null;
|
|
16
|
+
return app.path || app.url || null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {
|
|
20
|
+
return app.status === "pending" && !!app.builderUrl;
|
|
21
|
+
}
|
|
@@ -9,23 +9,10 @@ import {
|
|
|
9
9
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
10
10
|
import { Badge } from "@/components/ui/badge";
|
|
11
11
|
import { Button } from "@/components/ui/button";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
description?: string;
|
|
17
|
-
path: string;
|
|
18
|
-
url?: string | null;
|
|
19
|
-
status?: "ready" | "pending";
|
|
20
|
-
statusLabel?: string;
|
|
21
|
-
builderUrl?: string | null;
|
|
22
|
-
branchName?: string | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
26
|
-
if (app.status === "pending") return app.builderUrl || null;
|
|
27
|
-
return app.path || app.url || null;
|
|
28
|
-
}
|
|
12
|
+
import {
|
|
13
|
+
workspaceAppHref,
|
|
14
|
+
type WorkspaceAppSummary,
|
|
15
|
+
} from "@/lib/workspace-apps";
|
|
29
16
|
|
|
30
17
|
export function meta() {
|
|
31
18
|
return [{ title: "Workspace app - Dispatch" }];
|
|
@@ -1,37 +1,10 @@
|
|
|
1
1
|
import { useActionQuery } from "@agent-native/core/client";
|
|
2
|
-
import {
|
|
3
|
-
IconArrowUpRight,
|
|
4
|
-
IconApps,
|
|
5
|
-
IconClockHour4,
|
|
6
|
-
IconPlus,
|
|
7
|
-
} from "@tabler/icons-react";
|
|
8
|
-
import { AppKeysPopover } from "@/components/app-keys-popover";
|
|
2
|
+
import { IconApps, IconPlus } from "@tabler/icons-react";
|
|
9
3
|
import { CreateAppPopover } from "@/components/create-app-popover";
|
|
10
4
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
11
|
-
import {
|
|
5
|
+
import { WorkspaceAppCard } from "@/components/workspace-app-card";
|
|
12
6
|
import { Button } from "@/components/ui/button";
|
|
13
|
-
|
|
14
|
-
interface WorkspaceAppSummary {
|
|
15
|
-
id: string;
|
|
16
|
-
name: string;
|
|
17
|
-
description?: string;
|
|
18
|
-
path: string;
|
|
19
|
-
url?: string | null;
|
|
20
|
-
isDispatch: boolean;
|
|
21
|
-
status?: "ready" | "pending";
|
|
22
|
-
statusLabel?: string;
|
|
23
|
-
builderUrl?: string | null;
|
|
24
|
-
branchName?: string | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
28
|
-
if (app.status === "pending") return app.builderUrl || null;
|
|
29
|
-
return app.path || app.url || null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {
|
|
33
|
-
return app.status === "pending" && !!app.builderUrl;
|
|
34
|
-
}
|
|
7
|
+
import type { WorkspaceAppSummary } from "@/lib/workspace-apps";
|
|
35
8
|
|
|
36
9
|
export function meta() {
|
|
37
10
|
return [{ title: "Apps — Dispatch" }];
|
|
@@ -94,65 +67,9 @@ export default function AppsRoute() {
|
|
|
94
67
|
</div>
|
|
95
68
|
|
|
96
69
|
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
97
|
-
{typedApps.map((app) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// those in a new tab. Ready workspace apps stay in-window
|
|
101
|
-
// so the cards work inside the Builder webview, where new
|
|
102
|
-
// tabs would escape to the host browser.
|
|
103
|
-
const openInNewTab = isPendingBuilderHref(app);
|
|
104
|
-
return (
|
|
105
|
-
<a
|
|
106
|
-
key={app.id}
|
|
107
|
-
href={href ?? undefined}
|
|
108
|
-
target={openInNewTab ? "_blank" : undefined}
|
|
109
|
-
rel={openInNewTab ? "noreferrer" : undefined}
|
|
110
|
-
aria-disabled={!href}
|
|
111
|
-
className="group rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:pointer-events-none aria-disabled:opacity-60"
|
|
112
|
-
>
|
|
113
|
-
<div className="flex items-start justify-between gap-3">
|
|
114
|
-
<div className="min-w-0">
|
|
115
|
-
<div className="flex min-w-0 items-center gap-2">
|
|
116
|
-
<h3 className="truncate text-sm font-semibold text-foreground">
|
|
117
|
-
{app.name}
|
|
118
|
-
</h3>
|
|
119
|
-
{app.status === "pending" ? (
|
|
120
|
-
<Badge
|
|
121
|
-
variant="outline"
|
|
122
|
-
className="shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
123
|
-
>
|
|
124
|
-
<IconClockHour4 size={12} />
|
|
125
|
-
Building
|
|
126
|
-
</Badge>
|
|
127
|
-
) : null}
|
|
128
|
-
</div>
|
|
129
|
-
<p className="mt-1 truncate font-mono text-xs text-muted-foreground">
|
|
130
|
-
{app.path}
|
|
131
|
-
</p>
|
|
132
|
-
{app.status === "pending" && app.branchName ? (
|
|
133
|
-
<p className="mt-1 truncate text-xs text-muted-foreground">
|
|
134
|
-
Branch: {app.branchName}
|
|
135
|
-
</p>
|
|
136
|
-
) : null}
|
|
137
|
-
{app.description ? (
|
|
138
|
-
<p className="mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
|
|
139
|
-
{app.description}
|
|
140
|
-
</p>
|
|
141
|
-
) : null}
|
|
142
|
-
</div>
|
|
143
|
-
<div className="flex shrink-0 items-center gap-1">
|
|
144
|
-
{app.status === "ready" ? (
|
|
145
|
-
<AppKeysPopover appId={app.id} appName={app.name} />
|
|
146
|
-
) : null}
|
|
147
|
-
<IconArrowUpRight
|
|
148
|
-
size={16}
|
|
149
|
-
className="text-muted-foreground transition group-hover:text-foreground"
|
|
150
|
-
/>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</a>
|
|
154
|
-
);
|
|
155
|
-
})}
|
|
70
|
+
{typedApps.map((app) => (
|
|
71
|
+
<WorkspaceAppCard key={app.id} app={app} />
|
|
72
|
+
))}
|
|
156
73
|
|
|
157
74
|
<CreateAppPopover />
|
|
158
75
|
</div>
|
|
@@ -21,11 +21,10 @@ import {
|
|
|
21
21
|
IconShieldCheck,
|
|
22
22
|
type IconProps,
|
|
23
23
|
} from "@tabler/icons-react";
|
|
24
|
-
import { AppKeysPopover } from "@/components/app-keys-popover";
|
|
25
24
|
import { CreateAppPopover } from "@/components/create-app-popover";
|
|
26
25
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
26
|
+
import { WorkspaceAppCard } from "@/components/workspace-app-card";
|
|
27
27
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
28
|
-
import { Badge } from "@/components/ui/badge";
|
|
29
28
|
import { Button } from "@/components/ui/button";
|
|
30
29
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
31
30
|
import {
|
|
@@ -34,6 +33,7 @@ import {
|
|
|
34
33
|
TooltipTrigger,
|
|
35
34
|
} from "@/components/ui/tooltip";
|
|
36
35
|
import { submitOverviewPrompt } from "@/lib/overview-chat";
|
|
36
|
+
import type { WorkspaceAppSummary } from "@/lib/workspace-apps";
|
|
37
37
|
|
|
38
38
|
interface IntegrationStatus {
|
|
39
39
|
platform: string;
|
|
@@ -67,28 +67,6 @@ const ZERO_TASK_QUEUE_STATS: TaskQueueStats = {
|
|
|
67
67
|
recent_failures: [],
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
interface WorkspaceAppSummary {
|
|
71
|
-
id: string;
|
|
72
|
-
name: string;
|
|
73
|
-
description?: string;
|
|
74
|
-
path: string;
|
|
75
|
-
url?: string | null;
|
|
76
|
-
isDispatch: boolean;
|
|
77
|
-
status?: "ready" | "pending";
|
|
78
|
-
statusLabel?: string;
|
|
79
|
-
builderUrl?: string | null;
|
|
80
|
-
branchName?: string | null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
84
|
-
if (app.status === "pending") return app.builderUrl || null;
|
|
85
|
-
return app.path || app.url || null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {
|
|
89
|
-
return app.status === "pending" && !!app.builderUrl;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
70
|
const HOME_CHAT_SUGGESTIONS = [
|
|
93
71
|
"Create a lightweight customer onboarding app",
|
|
94
72
|
"Ask Slides to draft a board update from our latest metrics",
|
|
@@ -186,66 +164,9 @@ function WorkspaceAppsSection({
|
|
|
186
164
|
? Array.from({ length: 6 }).map((_, index) => (
|
|
187
165
|
<AppCardSkeleton key={index} />
|
|
188
166
|
))
|
|
189
|
-
: visibleApps.map((app) =>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// editor URL); open those in a new tab. Ready workspace apps
|
|
193
|
-
// navigate the current window so this works inside the
|
|
194
|
-
// Builder webview, where new tabs would try to open in the
|
|
195
|
-
// host browser and break the embedded session.
|
|
196
|
-
const openInNewTab = isPendingBuilderHref(app);
|
|
197
|
-
return (
|
|
198
|
-
<a
|
|
199
|
-
key={app.id}
|
|
200
|
-
href={href ?? undefined}
|
|
201
|
-
target={openInNewTab ? "_blank" : undefined}
|
|
202
|
-
rel={openInNewTab ? "noreferrer" : undefined}
|
|
203
|
-
aria-disabled={!href}
|
|
204
|
-
className="group min-h-32 rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:pointer-events-none aria-disabled:opacity-60"
|
|
205
|
-
>
|
|
206
|
-
<div className="flex h-full items-start justify-between gap-3">
|
|
207
|
-
<div className="min-w-0">
|
|
208
|
-
<div className="flex min-w-0 items-center gap-2">
|
|
209
|
-
<h3 className="truncate text-sm font-semibold text-foreground">
|
|
210
|
-
{app.name}
|
|
211
|
-
</h3>
|
|
212
|
-
{app.status === "pending" ? (
|
|
213
|
-
<Badge
|
|
214
|
-
variant="outline"
|
|
215
|
-
className="shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
216
|
-
>
|
|
217
|
-
<IconClockHour4 size={12} />
|
|
218
|
-
Building
|
|
219
|
-
</Badge>
|
|
220
|
-
) : null}
|
|
221
|
-
</div>
|
|
222
|
-
<p className="mt-1 truncate font-mono text-xs text-muted-foreground">
|
|
223
|
-
{app.path}
|
|
224
|
-
</p>
|
|
225
|
-
{app.status === "pending" && app.branchName ? (
|
|
226
|
-
<p className="mt-1 truncate text-xs text-muted-foreground">
|
|
227
|
-
Branch: {app.branchName}
|
|
228
|
-
</p>
|
|
229
|
-
) : null}
|
|
230
|
-
{app.description ? (
|
|
231
|
-
<p className="mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
|
|
232
|
-
{app.description}
|
|
233
|
-
</p>
|
|
234
|
-
) : null}
|
|
235
|
-
</div>
|
|
236
|
-
<div className="flex shrink-0 items-center gap-1">
|
|
237
|
-
{app.status === "ready" ? (
|
|
238
|
-
<AppKeysPopover appId={app.id} appName={app.name} />
|
|
239
|
-
) : null}
|
|
240
|
-
<IconArrowUpRight
|
|
241
|
-
size={16}
|
|
242
|
-
className="text-muted-foreground transition group-hover:text-foreground"
|
|
243
|
-
/>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
</a>
|
|
247
|
-
);
|
|
248
|
-
})}
|
|
167
|
+
: visibleApps.map((app) => (
|
|
168
|
+
<WorkspaceAppCard key={app.id} app={app} className="min-h-32" />
|
|
169
|
+
))}
|
|
249
170
|
|
|
250
171
|
{!showSkeletons ? <CreateAppPopover /> : null}
|
|
251
172
|
</div>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconChevronDown,
|
|
7
7
|
IconChevronRight,
|
|
8
8
|
IconCode,
|
|
9
|
+
IconFileText,
|
|
9
10
|
IconPlus,
|
|
10
11
|
IconRefresh,
|
|
11
12
|
IconTrash,
|
|
@@ -72,6 +73,13 @@ const KIND_CONFIG = {
|
|
|
72
73
|
description:
|
|
73
74
|
"Reusable agent profiles — specialist agents shared across apps",
|
|
74
75
|
},
|
|
76
|
+
knowledge: {
|
|
77
|
+
label: "Knowledge",
|
|
78
|
+
icon: IconFileText,
|
|
79
|
+
pathPrefix: "context/",
|
|
80
|
+
description:
|
|
81
|
+
"Knowledge packs — reusable GTM, product, and domain context for apps",
|
|
82
|
+
},
|
|
75
83
|
} as const;
|
|
76
84
|
|
|
77
85
|
function AddResourceDialog() {
|
|
@@ -127,6 +135,7 @@ function AddResourceDialog() {
|
|
|
127
135
|
<SelectItem value="skill">Skill</SelectItem>
|
|
128
136
|
<SelectItem value="instruction">Instruction</SelectItem>
|
|
129
137
|
<SelectItem value="agent">Agent</SelectItem>
|
|
138
|
+
<SelectItem value="knowledge">Knowledge pack</SelectItem>
|
|
130
139
|
</SelectContent>
|
|
131
140
|
</Select>
|
|
132
141
|
</div>
|
|
@@ -151,7 +160,9 @@ function AddResourceDialog() {
|
|
|
151
160
|
? "Frontend Designer"
|
|
152
161
|
: kind === "agent"
|
|
153
162
|
? "Research Specialist"
|
|
154
|
-
:
|
|
163
|
+
: kind === "knowledge"
|
|
164
|
+
? "Core GTM Messaging"
|
|
165
|
+
: "Code Style Guide"
|
|
155
166
|
}
|
|
156
167
|
value={name}
|
|
157
168
|
onChange={(e) => setName(e.target.value)}
|
|
@@ -167,7 +178,7 @@ function AddResourceDialog() {
|
|
|
167
178
|
/>
|
|
168
179
|
<p className="text-xs text-muted-foreground">
|
|
169
180
|
Resource path in target apps. Skills go in skills/, agents in
|
|
170
|
-
agents/.
|
|
181
|
+
agents/, knowledge packs in context/.
|
|
171
182
|
</p>
|
|
172
183
|
</div>
|
|
173
184
|
<div className="space-y-2">
|
|
@@ -186,7 +197,9 @@ function AddResourceDialog() {
|
|
|
186
197
|
? "---\nname: my-skill\ndescription: What this skill teaches\n---\n\n# My Skill\n\n..."
|
|
187
198
|
: kind === "agent"
|
|
188
199
|
? "---\nname: Research Specialist\ndescription: Handles research tasks\n---\n\n# Instructions\n\n..."
|
|
189
|
-
:
|
|
200
|
+
: kind === "knowledge"
|
|
201
|
+
? "# Core GTM Messaging\n\n## Positioning\n\n## ICP\n\n## Proof points\n\n## Source\n\n"
|
|
202
|
+
: "# Instructions\n\nBehavioral rules and guidance for agents across apps..."
|
|
190
203
|
}
|
|
191
204
|
value={content}
|
|
192
205
|
onChange={(e) => setContent(e.target.value)}
|
|
@@ -199,7 +212,7 @@ function AddResourceDialog() {
|
|
|
199
212
|
<Button
|
|
200
213
|
onClick={() =>
|
|
201
214
|
create.mutate({
|
|
202
|
-
kind: kind as "skill" | "instruction" | "agent",
|
|
215
|
+
kind: kind as "skill" | "instruction" | "agent" | "knowledge",
|
|
203
216
|
name,
|
|
204
217
|
description: description || undefined,
|
|
205
218
|
path:
|
|
@@ -504,6 +517,9 @@ export default function WorkspaceRoute() {
|
|
|
504
517
|
(r: any) => r.kind === "instruction",
|
|
505
518
|
);
|
|
506
519
|
const agents = (resources || []).filter((r: any) => r.kind === "agent");
|
|
520
|
+
const knowledge = (resources || []).filter(
|
|
521
|
+
(r: any) => r.kind === "knowledge",
|
|
522
|
+
);
|
|
507
523
|
|
|
508
524
|
function ResourceList({
|
|
509
525
|
items,
|
|
@@ -535,7 +551,7 @@ export default function WorkspaceRoute() {
|
|
|
535
551
|
return (
|
|
536
552
|
<DispatchShell
|
|
537
553
|
title="Workspace Resources"
|
|
538
|
-
description="Share skills, instructions,
|
|
554
|
+
description="Share skills, instructions, agent profiles, and knowledge packs across workspace apps. Scope to all apps or grant per-app."
|
|
539
555
|
>
|
|
540
556
|
<div className="flex items-center justify-between">
|
|
541
557
|
<div className="text-sm text-muted-foreground">
|
|
@@ -570,6 +586,9 @@ export default function WorkspaceRoute() {
|
|
|
570
586
|
<TabsTrigger value="agents">
|
|
571
587
|
Agents {agents.length > 0 && `(${agents.length})`}
|
|
572
588
|
</TabsTrigger>
|
|
589
|
+
<TabsTrigger value="knowledge">
|
|
590
|
+
Knowledge {knowledge.length > 0 && `(${knowledge.length})`}
|
|
591
|
+
</TabsTrigger>
|
|
573
592
|
</TabsList>
|
|
574
593
|
|
|
575
594
|
<TabsContent value="skills" className="mt-4">
|
|
@@ -592,6 +611,13 @@ export default function WorkspaceRoute() {
|
|
|
592
611
|
emptyText="No workspace agents yet. Add a reusable agent profile to share specialist agents across apps."
|
|
593
612
|
/>
|
|
594
613
|
</TabsContent>
|
|
614
|
+
|
|
615
|
+
<TabsContent value="knowledge" className="mt-4">
|
|
616
|
+
<ResourceList
|
|
617
|
+
items={knowledge}
|
|
618
|
+
emptyText="No knowledge packs yet. Add GTM, product, or domain context that apps can reuse."
|
|
619
|
+
/>
|
|
620
|
+
</TabsContent>
|
|
595
621
|
</Tabs>
|
|
596
622
|
</DispatchShell>
|
|
597
623
|
);
|
|
@@ -20,6 +20,11 @@ import {
|
|
|
20
20
|
} from "./dispatch-store.js";
|
|
21
21
|
import { identityKeyForIncoming } from "./dispatch-integrations.js";
|
|
22
22
|
import { createRequest, listSecrets } from "./vault-store.js";
|
|
23
|
+
import {
|
|
24
|
+
grantWorkspaceResourcesToApp,
|
|
25
|
+
listWorkspaceResourceOptions,
|
|
26
|
+
type WorkspaceResourceOption,
|
|
27
|
+
} from "./workspace-resources-store.js";
|
|
23
28
|
|
|
24
29
|
const SETTINGS_KEY = "dispatch-app-creation-settings";
|
|
25
30
|
const WORKSPACE_APPS_ENV_KEY = "AGENT_NATIVE_WORKSPACE_APPS_JSON";
|
|
@@ -827,6 +832,7 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
827
832
|
appId?: string | null;
|
|
828
833
|
template?: string | null;
|
|
829
834
|
selectedKeys?: string[];
|
|
835
|
+
selectedResources?: WorkspaceResourceOption[];
|
|
830
836
|
}): { appId: string; prompt: string } {
|
|
831
837
|
const appId =
|
|
832
838
|
slugify(input.appId || "") ||
|
|
@@ -835,6 +841,15 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
835
841
|
) ||
|
|
836
842
|
"new-app";
|
|
837
843
|
const selectedKeys = input.selectedKeys || [];
|
|
844
|
+
const selectedResources = input.selectedResources || [];
|
|
845
|
+
const resourceList = selectedResources.length
|
|
846
|
+
? selectedResources
|
|
847
|
+
.map(
|
|
848
|
+
(resource) =>
|
|
849
|
+
`- ${resource.name} (${resource.kind}, ${resource.path})`,
|
|
850
|
+
)
|
|
851
|
+
.join("\n")
|
|
852
|
+
: "none";
|
|
838
853
|
return {
|
|
839
854
|
appId,
|
|
840
855
|
prompt: [
|
|
@@ -846,11 +861,18 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
846
861
|
selectedKeys.length
|
|
847
862
|
? `Dispatch vault keys selected for this app: ${selectedKeys.join(", ")}`
|
|
848
863
|
: "Dispatch vault keys selected for this app: none",
|
|
864
|
+
`Dispatch workspace resources selected for this app:\n${resourceList}`,
|
|
849
865
|
"",
|
|
850
866
|
`Use the workspace app layout: create it under apps/${appId}, mount it at /${appId}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,
|
|
867
|
+
"Existing first-party apps are neighbors, not implementation details for this app. If the user prompt mentions Mail, Calendar, Analytics, Dispatch, or other templates, treat them as existing hosted/connected apps that this app can link to or call through A2A/default connected agents. For example, Mail, Calendar, and Analytics already exist at https://mail.agent-native.com, https://calendar.agent-native.com, and https://analytics.agent-native.com.",
|
|
868
|
+
`Do not clone first-party templates, create wrapper apps, or scaffold child apps/routes for Mail, Calendar, Analytics, etc. inside apps/${appId} just so this app can access them. If the request is a cross-app dashboard or overview, build only the new dashboard/overview app and delegate to the existing apps for domain work.`,
|
|
869
|
+
"Only create another first-party app copy when the user explicitly asks for a customized fork/copy of that app; otherwise keep using the hosted/shared app so improvements to the base template keep flowing to users.",
|
|
851
870
|
selectedKeys.length
|
|
852
871
|
? `Dispatch will create pending vault requests for the selected keys for appId "${appId}" after this app creation request is accepted. Do not grant or sync vault keys directly from the app-creation branch.`
|
|
853
872
|
: "Do not grant or request any Dispatch vault keys unless the user asks later.",
|
|
873
|
+
selectedResources.length
|
|
874
|
+
? `Dispatch will create workspace resource grants for the selected resources for appId "${appId}". After the app exists, sync workspace resources so the app receives those shared resources. Add a short note to apps/${appId}/AGENTS.md telling the app agent to read relevant shared resources under context/ or the selected resource paths before doing GTM/domain work.`
|
|
875
|
+
: "Do not grant any Dispatch workspace resources unless the user asks later.",
|
|
854
876
|
"",
|
|
855
877
|
"Agent-native rules (these are the framework's contract — not optional):",
|
|
856
878
|
`- Persist ALL data in SQL via Drizzle. Add tables to apps/${appId}/server/db/schema.ts and migrations to apps/${appId}/server/plugins/db.ts. NEVER use localStorage, sessionStorage, IndexedDB, or in-memory state for anything the user expects to persist — agent and UI must read the same source of truth.`,
|
|
@@ -890,11 +912,29 @@ async function requestSelectedVaultKeys(input: {
|
|
|
890
912
|
);
|
|
891
913
|
}
|
|
892
914
|
|
|
915
|
+
async function selectedWorkspaceResourcesForIds(
|
|
916
|
+
resourceIds: string[] | undefined,
|
|
917
|
+
): Promise<WorkspaceResourceOption[]> {
|
|
918
|
+
if (!resourceIds?.length) return [];
|
|
919
|
+
const requested = new Set(resourceIds);
|
|
920
|
+
const resources = await listWorkspaceResourceOptions();
|
|
921
|
+
return resources.filter((resource) => requested.has(resource.id));
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
async function grantSelectedWorkspaceResources(input: {
|
|
925
|
+
appId: string;
|
|
926
|
+
resourceIds: string[];
|
|
927
|
+
}) {
|
|
928
|
+
if (input.resourceIds.length === 0) return;
|
|
929
|
+
await grantWorkspaceResourcesToApp(input);
|
|
930
|
+
}
|
|
931
|
+
|
|
893
932
|
export async function startWorkspaceAppCreation(input: {
|
|
894
933
|
prompt: string;
|
|
895
934
|
appId?: string | null;
|
|
896
935
|
template?: string | null;
|
|
897
936
|
secretIds?: string[];
|
|
937
|
+
resourceIds?: string[];
|
|
898
938
|
}) {
|
|
899
939
|
const initial = buildWorkspaceAppPrompt({
|
|
900
940
|
prompt: input.prompt,
|
|
@@ -920,11 +960,15 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
920
960
|
.filter((secret) => input.secretIds?.includes(secret.id))
|
|
921
961
|
.map((secret) => secret.credentialKey)
|
|
922
962
|
: [];
|
|
963
|
+
const selectedResources = await selectedWorkspaceResourcesForIds(
|
|
964
|
+
input.resourceIds,
|
|
965
|
+
);
|
|
923
966
|
const built = buildWorkspaceAppPrompt({
|
|
924
967
|
prompt: input.prompt,
|
|
925
968
|
appId: input.appId,
|
|
926
969
|
template: input.template,
|
|
927
970
|
selectedKeys,
|
|
971
|
+
selectedResources,
|
|
928
972
|
});
|
|
929
973
|
const prompt = built.prompt;
|
|
930
974
|
|
|
@@ -933,6 +977,10 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
933
977
|
appId: built.appId,
|
|
934
978
|
selectedKeys,
|
|
935
979
|
});
|
|
980
|
+
await grantSelectedWorkspaceResources({
|
|
981
|
+
appId: built.appId,
|
|
982
|
+
resourceIds: selectedResources.map((resource) => resource.id),
|
|
983
|
+
});
|
|
936
984
|
return {
|
|
937
985
|
mode: "local-agent",
|
|
938
986
|
appId: built.appId,
|
|
@@ -997,6 +1045,10 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
997
1045
|
appId: built.appId,
|
|
998
1046
|
selectedKeys,
|
|
999
1047
|
});
|
|
1048
|
+
await grantSelectedWorkspaceResources({
|
|
1049
|
+
appId: built.appId,
|
|
1050
|
+
resourceIds: selectedResources.map((resource) => resource.id),
|
|
1051
|
+
});
|
|
1000
1052
|
|
|
1001
1053
|
return {
|
|
1002
1054
|
mode: "builder",
|