@theihtisham/ai-agent-starter-kit 1.0.0

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 (130) hide show
  1. package/.env.example +33 -0
  2. package/Dockerfile +35 -0
  3. package/LICENSE +21 -0
  4. package/README.md +73 -0
  5. package/docker-compose.yml +28 -0
  6. package/next-env.d.ts +5 -0
  7. package/next.config.mjs +17 -0
  8. package/package.json +85 -0
  9. package/postcss.config.js +6 -0
  10. package/prisma/schema.prisma +157 -0
  11. package/prisma/seed.ts +46 -0
  12. package/src/app/(auth)/forgot-password/page.tsx +56 -0
  13. package/src/app/(auth)/layout.tsx +7 -0
  14. package/src/app/(auth)/login/page.tsx +83 -0
  15. package/src/app/(auth)/signup/page.tsx +108 -0
  16. package/src/app/(dashboard)/agents/[id]/edit/page.tsx +68 -0
  17. package/src/app/(dashboard)/agents/[id]/page.tsx +114 -0
  18. package/src/app/(dashboard)/agents/new/page.tsx +43 -0
  19. package/src/app/(dashboard)/agents/page.tsx +63 -0
  20. package/src/app/(dashboard)/api-keys/page.tsx +139 -0
  21. package/src/app/(dashboard)/dashboard/page.tsx +79 -0
  22. package/src/app/(dashboard)/layout.tsx +16 -0
  23. package/src/app/(dashboard)/settings/billing/page.tsx +59 -0
  24. package/src/app/(dashboard)/settings/page.tsx +45 -0
  25. package/src/app/(dashboard)/usage/page.tsx +46 -0
  26. package/src/app/api/agents/[id]/chat/route.ts +100 -0
  27. package/src/app/api/agents/[id]/chats/route.ts +36 -0
  28. package/src/app/api/agents/[id]/route.ts +97 -0
  29. package/src/app/api/agents/route.ts +84 -0
  30. package/src/app/api/api-keys/[id]/route.ts +25 -0
  31. package/src/app/api/api-keys/route.ts +72 -0
  32. package/src/app/api/auth/[...nextauth]/route.ts +5 -0
  33. package/src/app/api/auth/register/route.ts +53 -0
  34. package/src/app/api/health/route.ts +26 -0
  35. package/src/app/api/stripe/checkout/route.ts +37 -0
  36. package/src/app/api/stripe/plans/route.ts +16 -0
  37. package/src/app/api/stripe/portal/route.ts +29 -0
  38. package/src/app/api/stripe/webhook/route.ts +45 -0
  39. package/src/app/api/usage/route.ts +43 -0
  40. package/src/app/globals.css +59 -0
  41. package/src/app/layout.tsx +22 -0
  42. package/src/app/page.tsx +32 -0
  43. package/src/app/pricing/page.tsx +25 -0
  44. package/src/components/agents/agent-form.tsx +137 -0
  45. package/src/components/agents/model-selector.tsx +35 -0
  46. package/src/components/agents/tool-selector.tsx +48 -0
  47. package/src/components/auth-provider.tsx +17 -0
  48. package/src/components/billing/plan-badge.tsx +23 -0
  49. package/src/components/billing/pricing-table.tsx +95 -0
  50. package/src/components/billing/usage-meter.tsx +39 -0
  51. package/src/components/chat/chat-input.tsx +68 -0
  52. package/src/components/chat/chat-interface.tsx +152 -0
  53. package/src/components/chat/chat-message.tsx +50 -0
  54. package/src/components/chat/chat-sidebar.tsx +49 -0
  55. package/src/components/chat/code-block.tsx +38 -0
  56. package/src/components/chat/markdown-renderer.tsx +56 -0
  57. package/src/components/chat/streaming-text.tsx +46 -0
  58. package/src/components/dashboard/agent-card.tsx +52 -0
  59. package/src/components/dashboard/header.tsx +75 -0
  60. package/src/components/dashboard/sidebar.tsx +52 -0
  61. package/src/components/dashboard/stat-card.tsx +42 -0
  62. package/src/components/dashboard/usage-chart.tsx +42 -0
  63. package/src/components/landing/cta.tsx +30 -0
  64. package/src/components/landing/features.tsx +75 -0
  65. package/src/components/landing/hero.tsx +42 -0
  66. package/src/components/landing/pricing.tsx +28 -0
  67. package/src/components/ui/avatar.tsx +24 -0
  68. package/src/components/ui/badge.tsx +24 -0
  69. package/src/components/ui/button.tsx +39 -0
  70. package/src/components/ui/card.tsx +50 -0
  71. package/src/components/ui/dialog.tsx +73 -0
  72. package/src/components/ui/dropdown.tsx +77 -0
  73. package/src/components/ui/input.tsx +23 -0
  74. package/src/components/ui/skeleton.tsx +7 -0
  75. package/src/components/ui/switch.tsx +31 -0
  76. package/src/components/ui/table.tsx +48 -0
  77. package/src/components/ui/tabs.tsx +66 -0
  78. package/src/components/ui/textarea.tsx +20 -0
  79. package/src/hooks/use-agent.ts +44 -0
  80. package/src/hooks/use-streaming.ts +82 -0
  81. package/src/hooks/use-subscription.ts +40 -0
  82. package/src/hooks/use-usage.ts +43 -0
  83. package/src/hooks/use-user.ts +13 -0
  84. package/src/lib/agents/index.ts +60 -0
  85. package/src/lib/agents/memory/long-term.ts +241 -0
  86. package/src/lib/agents/memory/manager.ts +154 -0
  87. package/src/lib/agents/memory/short-term.ts +155 -0
  88. package/src/lib/agents/memory/types.ts +68 -0
  89. package/src/lib/agents/orchestration/debate.ts +170 -0
  90. package/src/lib/agents/orchestration/index.ts +103 -0
  91. package/src/lib/agents/orchestration/parallel.ts +143 -0
  92. package/src/lib/agents/orchestration/router.ts +199 -0
  93. package/src/lib/agents/orchestration/sequential.ts +127 -0
  94. package/src/lib/agents/orchestration/types.ts +68 -0
  95. package/src/lib/agents/tools/calculator.ts +131 -0
  96. package/src/lib/agents/tools/code-executor.ts +191 -0
  97. package/src/lib/agents/tools/file-reader.ts +129 -0
  98. package/src/lib/agents/tools/index.ts +48 -0
  99. package/src/lib/agents/tools/registry.ts +182 -0
  100. package/src/lib/agents/tools/web-search.ts +83 -0
  101. package/src/lib/ai/agent.ts +275 -0
  102. package/src/lib/ai/context.ts +68 -0
  103. package/src/lib/ai/memory.ts +98 -0
  104. package/src/lib/ai/models.ts +80 -0
  105. package/src/lib/ai/streaming.ts +80 -0
  106. package/src/lib/ai/tools.ts +149 -0
  107. package/src/lib/auth/middleware.ts +41 -0
  108. package/src/lib/auth/nextauth.ts +69 -0
  109. package/src/lib/db/client.ts +15 -0
  110. package/src/lib/rate-limit/limiter.ts +93 -0
  111. package/src/lib/rate-limit/rules.ts +38 -0
  112. package/src/lib/stripe/client.ts +25 -0
  113. package/src/lib/stripe/plans.ts +75 -0
  114. package/src/lib/stripe/usage.ts +123 -0
  115. package/src/lib/stripe/webhooks.ts +96 -0
  116. package/src/lib/utils/api-response.ts +85 -0
  117. package/src/lib/utils/errors.ts +73 -0
  118. package/src/lib/utils/helpers.ts +50 -0
  119. package/src/lib/utils/id.ts +21 -0
  120. package/src/lib/utils/logger.ts +38 -0
  121. package/src/lib/utils/validation.ts +44 -0
  122. package/src/middleware.ts +13 -0
  123. package/src/types/agent.ts +31 -0
  124. package/src/types/api.ts +38 -0
  125. package/src/types/billing.ts +35 -0
  126. package/src/types/chat.ts +30 -0
  127. package/src/types/next-auth.d.ts +19 -0
  128. package/tailwind.config.ts +72 -0
  129. package/tsconfig.json +28 -0
  130. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+
3
+ import { useState, useRef, useEffect, type ReactNode } from 'react';
4
+ import { cn } from '@/lib/utils/helpers';
5
+
6
+ export function Dropdown({ children }: { children: ReactNode }) {
7
+ return <div className="relative inline-block">{children}</div>;
8
+ }
9
+
10
+ export function DropdownTrigger({ children, onClick, className }: {
11
+ children: ReactNode;
12
+ onClick: () => void;
13
+ className?: string;
14
+ }) {
15
+ return (
16
+ <button className={cn('outline-none', className)} onClick={onClick}>
17
+ {children}
18
+ </button>
19
+ );
20
+ }
21
+
22
+ export function DropdownContent({ children, open, onClose, className, align = 'end' }: {
23
+ children: ReactNode;
24
+ open: boolean;
25
+ onClose: () => void;
26
+ className?: string;
27
+ align?: 'start' | 'end';
28
+ }) {
29
+ const ref = useRef<HTMLDivElement>(null);
30
+
31
+ useEffect(() => {
32
+ function handleClick(e: MouseEvent) {
33
+ if (ref.current && !ref.current.contains(e.target as Node)) {
34
+ onClose();
35
+ }
36
+ }
37
+ if (open) document.addEventListener('mousedown', handleClick);
38
+ return () => document.removeEventListener('mousedown', handleClick);
39
+ }, [open, onClose]);
40
+
41
+ if (!open) return null;
42
+
43
+ return (
44
+ <div
45
+ ref={ref}
46
+ className={cn(
47
+ 'absolute z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-fade-in',
48
+ align === 'end' ? 'right-0' : 'left-0',
49
+ className,
50
+ )}
51
+ >
52
+ {children}
53
+ </div>
54
+ );
55
+ }
56
+
57
+ export function DropdownItem({ children, onClick, className }: {
58
+ children: ReactNode;
59
+ onClick?: () => void;
60
+ className?: string;
61
+ }) {
62
+ return (
63
+ <button
64
+ className={cn(
65
+ 'relative flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground',
66
+ className,
67
+ )}
68
+ onClick={onClick}
69
+ >
70
+ {children}
71
+ </button>
72
+ );
73
+ }
74
+
75
+ export function DropdownSeparator({ className }: { className?: string }) {
76
+ return <div className={cn('-mx-1 my-1 h-px bg-muted', className)} />;
77
+ }
@@ -0,0 +1,23 @@
1
+ import { forwardRef, type InputHTMLAttributes } from 'react';
2
+ import { cn } from '@/lib/utils/helpers';
3
+
4
+ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {}
5
+
6
+ const Input = forwardRef<HTMLInputElement, InputProps>(
7
+ ({ className, type, ...props }, ref) => {
8
+ return (
9
+ <input
10
+ type={type}
11
+ className={cn(
12
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
13
+ className,
14
+ )}
15
+ ref={ref}
16
+ {...props}
17
+ />
18
+ );
19
+ },
20
+ );
21
+ Input.displayName = 'Input';
22
+
23
+ export { Input };
@@ -0,0 +1,7 @@
1
+ import { cn } from '@/lib/utils/helpers';
2
+
3
+ export function Skeleton({ className }: { className?: string }) {
4
+ return (
5
+ <div className={cn('animate-pulse rounded-md bg-muted', className)} />
6
+ );
7
+ }
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+
3
+ import { cn } from '@/lib/utils/helpers';
4
+
5
+ export function Switch({ checked, onCheckedChange, className, disabled }: {
6
+ checked: boolean;
7
+ onCheckedChange: (checked: boolean) => void;
8
+ className?: string;
9
+ disabled?: boolean;
10
+ }) {
11
+ return (
12
+ <button
13
+ role="switch"
14
+ aria-checked={checked}
15
+ disabled={disabled}
16
+ className={cn(
17
+ 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50',
18
+ checked ? 'bg-primary' : 'bg-input',
19
+ className,
20
+ )}
21
+ onClick={() => onCheckedChange(!checked)}
22
+ >
23
+ <span
24
+ className={cn(
25
+ 'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform',
26
+ checked ? 'translate-x-5' : 'translate-x-0',
27
+ )}
28
+ />
29
+ </button>
30
+ );
31
+ }
@@ -0,0 +1,48 @@
1
+ import { forwardRef, type HTMLAttributes, type TdHTMLAttributes, type ThHTMLAttributes } from 'react';
2
+ import { cn } from '@/lib/utils/helpers';
3
+
4
+ const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
5
+ ({ className, ...props }, ref) => (
6
+ <div className="relative w-full overflow-auto">
7
+ <table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />
8
+ </div>
9
+ ),
10
+ );
11
+ Table.displayName = 'Table';
12
+
13
+ const TableHeader = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
14
+ ({ className, ...props }, ref) => (
15
+ <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
16
+ ),
17
+ );
18
+ TableHeader.displayName = 'TableHeader';
19
+
20
+ const TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
21
+ ({ className, ...props }, ref) => (
22
+ <tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />
23
+ ),
24
+ );
25
+ TableBody.displayName = 'TableBody';
26
+
27
+ const TableRow = forwardRef<HTMLTableRowElement, HTMLAttributes<HTMLTableRowElement>>(
28
+ ({ className, ...props }, ref) => (
29
+ <tr ref={ref} className={cn('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', className)} {...props} />
30
+ ),
31
+ );
32
+ TableRow.displayName = 'TableRow';
33
+
34
+ const TableHead = forwardRef<HTMLTableCellElement, ThHTMLAttributes<HTMLTableCellElement>>(
35
+ ({ className, ...props }, ref) => (
36
+ <th ref={ref} className={cn('h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0', className)} {...props} />
37
+ ),
38
+ );
39
+ TableHead.displayName = 'TableHead';
40
+
41
+ const TableCell = forwardRef<HTMLTableCellElement, TdHTMLAttributes<HTMLTableCellElement>>(
42
+ ({ className, ...props }, ref) => (
43
+ <td ref={ref} className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)} {...props} />
44
+ ),
45
+ );
46
+ TableCell.displayName = 'TableCell';
47
+
48
+ export { Table, TableHeader, TableBody, TableRow, TableHead, TableCell };
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { useState, createContext, useContext, type ReactNode } from 'react';
4
+ import { cn } from '@/lib/utils/helpers';
5
+
6
+ interface TabsContextValue {
7
+ value: string;
8
+ onValueChange: (value: string) => void;
9
+ }
10
+
11
+ const TabsContext = createContext<TabsContextValue>({ value: '', onValueChange: () => {} });
12
+
13
+ export function Tabs({ defaultValue, value: controlledValue, onValueChange, children, className }: {
14
+ defaultValue?: string;
15
+ value?: string;
16
+ onValueChange?: (value: string) => void;
17
+ children: ReactNode;
18
+ className?: string;
19
+ }) {
20
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '');
21
+ const value = controlledValue ?? internalValue;
22
+ const onValueChangeHandler = onValueChange ?? setInternalValue;
23
+
24
+ return (
25
+ <TabsContext.Provider value={{ value, onValueChange: onValueChangeHandler }}>
26
+ <div className={className}>{children}</div>
27
+ </TabsContext.Provider>
28
+ );
29
+ }
30
+
31
+ export function TabsList({ children, className }: { children: ReactNode; className?: string }) {
32
+ return (
33
+ <div className={cn('inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground', className)}>
34
+ {children}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ export function TabsTrigger({ value, children, className }: { value: string; children: ReactNode; className?: string }) {
40
+ const { value: currentValue, onValueChange } = useContext(TabsContext);
41
+ const isActive = currentValue === value;
42
+
43
+ return (
44
+ <button
45
+ className={cn(
46
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
47
+ isActive && 'bg-background text-foreground shadow-sm',
48
+ className,
49
+ )}
50
+ onClick={() => onValueChange(value)}
51
+ >
52
+ {children}
53
+ </button>
54
+ );
55
+ }
56
+
57
+ export function TabsContent({ value, children, className }: { value: string; children: ReactNode; className?: string }) {
58
+ const { value: currentValue } = useContext(TabsContext);
59
+ if (currentValue !== value) return null;
60
+
61
+ return (
62
+ <div className={cn('mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', className)}>
63
+ {children}
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,20 @@
1
+ import { forwardRef, type TextareaHTMLAttributes } from 'react';
2
+ import { cn } from '@/lib/utils/helpers';
3
+
4
+ const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(
5
+ ({ className, ...props }, ref) => {
6
+ return (
7
+ <textarea
8
+ className={cn(
9
+ 'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
10
+ className,
11
+ )}
12
+ ref={ref}
13
+ {...props}
14
+ />
15
+ );
16
+ },
17
+ );
18
+ Textarea.displayName = 'Textarea';
19
+
20
+ export { Textarea };
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+
5
+ interface Agent {
6
+ id: string;
7
+ name: string;
8
+ description?: string;
9
+ model: string;
10
+ systemPrompt: string;
11
+ temperature: number;
12
+ maxTokens: number;
13
+ tools: string[];
14
+ isPublic: boolean;
15
+ }
16
+
17
+ export function useAgent(agentId: string) {
18
+ const [agent, setAgent] = useState<Agent | null>(null);
19
+ const [loading, setLoading] = useState(true);
20
+ const [error, setError] = useState<string | null>(null);
21
+
22
+ const fetchAgent = useCallback(async () => {
23
+ try {
24
+ setLoading(true);
25
+ const res = await fetch(`/api/agents/${agentId}`);
26
+ const data = await res.json();
27
+ if (data.success) {
28
+ setAgent(data.data);
29
+ } else {
30
+ setError(data.error?.message ?? 'Failed to load agent');
31
+ }
32
+ } catch {
33
+ setError('Network error');
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }, [agentId]);
38
+
39
+ useEffect(() => {
40
+ fetchAgent();
41
+ }, [fetchAgent]);
42
+
43
+ return { agent, loading, error, refetch: fetchAgent };
44
+ }
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback, useRef } from 'react';
4
+
5
+ interface UseStreamingOptions {
6
+ onError?: (error: Error) => void;
7
+ onComplete?: (content: string) => void;
8
+ }
9
+
10
+ export function useStreaming(options?: UseStreamingOptions) {
11
+ const [content, setContent] = useState('');
12
+ const [isLoading, setIsLoading] = useState(false);
13
+ const abortRef = useRef<AbortController | null>(null);
14
+
15
+ const stream = useCallback(async (url: string, body: Record<string, unknown>) => {
16
+ setContent('');
17
+ setIsLoading(true);
18
+
19
+ const abortController = new AbortController();
20
+ abortRef.current = abortController;
21
+
22
+ try {
23
+ const response = await fetch(url, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify(body),
27
+ signal: abortController.signal,
28
+ });
29
+
30
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
31
+ if (!response.body) throw new Error('No response body');
32
+
33
+ const reader = response.body.getReader();
34
+ const decoder = new TextDecoder();
35
+ let accumulated = '';
36
+
37
+ while (true) {
38
+ const { done, value } = await reader.read();
39
+ if (done) break;
40
+
41
+ const chunk = decoder.decode(value, { stream: true });
42
+ const lines = chunk.split('\n');
43
+
44
+ for (const line of lines) {
45
+ if (!line.startsWith('data: ')) continue;
46
+ const data = line.slice(6).trim();
47
+ if (data === '[DONE]') continue;
48
+
49
+ try {
50
+ const parsed = JSON.parse(data);
51
+ if (parsed.type === 'text') {
52
+ accumulated += parsed.content;
53
+ setContent(accumulated);
54
+ } else if (parsed.type === 'error') {
55
+ throw new Error(parsed.content);
56
+ }
57
+ } catch (parseErr) {
58
+ if ((parseErr as Error).message !== 'Unexpected end of JSON input') {
59
+ // Skip malformed chunks
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ options?.onComplete?.(accumulated);
66
+ } catch (err) {
67
+ if ((err as Error).name !== 'AbortError') {
68
+ options?.onError?.(err as Error);
69
+ }
70
+ } finally {
71
+ setIsLoading(false);
72
+ abortRef.current = null;
73
+ }
74
+ }, [options]);
75
+
76
+ const stop = useCallback(() => {
77
+ abortRef.current?.abort();
78
+ setIsLoading(false);
79
+ }, []);
80
+
81
+ return { content, isLoading, stream, stop };
82
+ }
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+
5
+ interface SubscriptionData {
6
+ planId: string;
7
+ status: string;
8
+ currentPeriodEnd: string;
9
+ cancelAtPeriodEnd: boolean;
10
+ }
11
+
12
+ export function useSubscription() {
13
+ const [subscription, setSubscription] = useState<SubscriptionData | null>(null);
14
+ const [loading, setLoading] = useState(true);
15
+
16
+ const fetchSubscription = useCallback(async () => {
17
+ try {
18
+ const res = await fetch('/api/usage');
19
+ const data = await res.json();
20
+ if (data.success) {
21
+ setSubscription({
22
+ planId: data.data?.planId ?? 'free',
23
+ status: 'active',
24
+ currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
25
+ cancelAtPeriodEnd: false,
26
+ });
27
+ }
28
+ } catch {
29
+ // Defaults to free
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ }, []);
34
+
35
+ useEffect(() => {
36
+ fetchSubscription();
37
+ }, [fetchSubscription]);
38
+
39
+ return { subscription, loading, refetch: fetchSubscription };
40
+ }
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+
5
+ interface UsageData {
6
+ messages: number;
7
+ toolCalls: number;
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ }
11
+
12
+ interface UsageLimits {
13
+ maxMessages: number;
14
+ maxTokens: number;
15
+ maxAgents: number;
16
+ }
17
+
18
+ export function useUsage() {
19
+ const [usage, setUsage] = useState<UsageData>({ messages: 0, toolCalls: 0, inputTokens: 0, outputTokens: 0 });
20
+ const [limits, setLimits] = useState<UsageLimits>({ maxMessages: 50, maxTokens: 100000, maxAgents: 2 });
21
+ const [loading, setLoading] = useState(true);
22
+
23
+ const fetchUsage = useCallback(async () => {
24
+ try {
25
+ const res = await fetch('/api/usage');
26
+ const data = await res.json();
27
+ if (data.success) {
28
+ setUsage(data.data?.usage ?? usage);
29
+ setLimits(data.data?.limits ?? limits);
30
+ }
31
+ } catch {
32
+ // Keep defaults
33
+ } finally {
34
+ setLoading(false);
35
+ }
36
+ }, []);
37
+
38
+ useEffect(() => {
39
+ fetchUsage();
40
+ }, [fetchUsage]);
41
+
42
+ return { usage, limits, loading, refetch: fetchUsage };
43
+ }
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import { useSession } from 'next-auth/react';
4
+
5
+ export function useUser() {
6
+ const { data: session, status } = useSession();
7
+
8
+ return {
9
+ user: session?.user ?? null,
10
+ isLoading: status === 'loading',
11
+ isAuthenticated: status === 'authenticated',
12
+ };
13
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Agent Framework — Memory, Orchestration, and Tools.
3
+ *
4
+ * This is the main entry point for the agent framework.
5
+ * Import everything you need from here.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { MemoryManager } from '@/lib/agents';
10
+ * import { createOrchestrator } from '@/lib/agents';
11
+ * import { createDefaultRegistry } from '@/lib/agents';
12
+ * ```
13
+ */
14
+
15
+ // Memory System
16
+ export { ShortTermMemory } from './memory/short-term';
17
+ export { LongTermMemory, cosineSimilarity, simpleEmbedding } from './memory/long-term';
18
+ export { MemoryManager } from './memory/manager';
19
+ export type {
20
+ MemoryEntry,
21
+ MemoryConfig,
22
+ RecallOptions,
23
+ MemoryStats,
24
+ MemorySummary,
25
+ } from './memory/types';
26
+ export { DEFAULT_MEMORY_CONFIG } from './memory/types';
27
+
28
+ // Multi-Agent Orchestration
29
+ export {
30
+ SequentialOrchestrator,
31
+ ParallelOrchestrator,
32
+ RouterOrchestrator,
33
+ DebateOrchestrator,
34
+ keywordClassifier,
35
+ createOrchestrator,
36
+ orchestrate,
37
+ } from './orchestration';
38
+ export type {
39
+ OrchestrationAgentConfig,
40
+ AgentMessage,
41
+ Handoff,
42
+ OrchestrationPattern,
43
+ OrchestrationResult,
44
+ OrchestratorOptions,
45
+ RouterClassifier,
46
+ CreateOrchestratorOptions,
47
+ DebateOptions,
48
+ } from './orchestration';
49
+
50
+ // Tool Registry
51
+ export {
52
+ ToolRegistry,
53
+ getGlobalRegistry,
54
+ createDefaultRegistry,
55
+ webSearchTool,
56
+ calculatorTool,
57
+ codeExecutorTool,
58
+ fileReaderTool,
59
+ } from './tools';
60
+ export type { ToolDefinition, ToolExecutionResult } from './tools';