@cortexmemory/cli 0.26.2 → 0.27.3

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 (90) hide show
  1. package/dist/commands/convex.js +1 -1
  2. package/dist/commands/convex.js.map +1 -1
  3. package/dist/commands/deploy.d.ts +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +771 -144
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/dev.d.ts.map +1 -1
  8. package/dist/commands/dev.js +210 -36
  9. package/dist/commands/dev.js.map +1 -1
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +273 -43
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/commands/setup.d.ts.map +1 -1
  14. package/dist/commands/setup.js +102 -46
  15. package/dist/commands/setup.js.map +1 -1
  16. package/dist/commands/status.d.ts.map +1 -1
  17. package/dist/commands/status.js +94 -7
  18. package/dist/commands/status.js.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/types.d.ts +23 -0
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/utils/app-template-sync.d.ts +95 -0
  23. package/dist/utils/app-template-sync.d.ts.map +1 -0
  24. package/dist/utils/app-template-sync.js +425 -0
  25. package/dist/utils/app-template-sync.js.map +1 -0
  26. package/dist/utils/config.d.ts +11 -0
  27. package/dist/utils/config.d.ts.map +1 -1
  28. package/dist/utils/config.js +20 -0
  29. package/dist/utils/config.js.map +1 -1
  30. package/dist/utils/deployment-selector.d.ts +21 -0
  31. package/dist/utils/deployment-selector.d.ts.map +1 -1
  32. package/dist/utils/deployment-selector.js +32 -0
  33. package/dist/utils/deployment-selector.js.map +1 -1
  34. package/dist/utils/init/graph-setup.d.ts.map +1 -1
  35. package/dist/utils/init/graph-setup.js +25 -2
  36. package/dist/utils/init/graph-setup.js.map +1 -1
  37. package/dist/utils/init/quickstart-setup.d.ts +87 -0
  38. package/dist/utils/init/quickstart-setup.d.ts.map +1 -0
  39. package/dist/utils/init/quickstart-setup.js +462 -0
  40. package/dist/utils/init/quickstart-setup.js.map +1 -0
  41. package/dist/utils/schema-sync.d.ts.map +1 -1
  42. package/dist/utils/schema-sync.js +27 -21
  43. package/dist/utils/schema-sync.js.map +1 -1
  44. package/package.json +3 -2
  45. package/templates/vercel-ai-quickstart/.env.local.example +45 -0
  46. package/templates/vercel-ai-quickstart/README.md +280 -0
  47. package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +30 -0
  48. package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +83 -0
  49. package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +94 -0
  50. package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +59 -0
  51. package/templates/vercel-ai-quickstart/app/api/chat/route.ts +277 -0
  52. package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +179 -0
  53. package/templates/vercel-ai-quickstart/app/api/facts/route.ts +39 -0
  54. package/templates/vercel-ai-quickstart/app/api/health/route.ts +99 -0
  55. package/templates/vercel-ai-quickstart/app/api/memories/route.ts +37 -0
  56. package/templates/vercel-ai-quickstart/app/globals.css +275 -0
  57. package/templates/vercel-ai-quickstart/app/layout.tsx +19 -0
  58. package/templates/vercel-ai-quickstart/app/page.tsx +216 -0
  59. package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +139 -0
  60. package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +283 -0
  61. package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +323 -0
  62. package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +334 -0
  63. package/templates/vercel-ai-quickstart/components/ConvexClientProvider.tsx +21 -0
  64. package/templates/vercel-ai-quickstart/components/DataPreview.tsx +57 -0
  65. package/templates/vercel-ai-quickstart/components/HealthStatus.tsx +214 -0
  66. package/templates/vercel-ai-quickstart/components/LayerCard.tsx +263 -0
  67. package/templates/vercel-ai-quickstart/components/LayerFlowDiagram.tsx +195 -0
  68. package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +202 -0
  69. package/templates/vercel-ai-quickstart/components/MemorySpaceSwitcher.tsx +93 -0
  70. package/templates/vercel-ai-quickstart/convex/conversations.ts +67 -0
  71. package/templates/vercel-ai-quickstart/convex/facts.ts +131 -0
  72. package/templates/vercel-ai-quickstart/convex/health.ts +15 -0
  73. package/templates/vercel-ai-quickstart/convex/memories.ts +104 -0
  74. package/templates/vercel-ai-quickstart/convex/schema.ts +20 -0
  75. package/templates/vercel-ai-quickstart/convex/users.ts +105 -0
  76. package/templates/vercel-ai-quickstart/jest.config.js +45 -0
  77. package/templates/vercel-ai-quickstart/lib/animations.ts +146 -0
  78. package/templates/vercel-ai-quickstart/lib/cortex.ts +27 -0
  79. package/templates/vercel-ai-quickstart/lib/layer-tracking.ts +214 -0
  80. package/templates/vercel-ai-quickstart/lib/password.ts +120 -0
  81. package/templates/vercel-ai-quickstart/next.config.js +27 -0
  82. package/templates/vercel-ai-quickstart/package.json +46 -0
  83. package/templates/vercel-ai-quickstart/postcss.config.js +5 -0
  84. package/templates/vercel-ai-quickstart/tailwind.config.js +37 -0
  85. package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +263 -0
  86. package/templates/vercel-ai-quickstart/tests/helpers/setup.ts +48 -0
  87. package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +455 -0
  88. package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +461 -0
  89. package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +228 -0
  90. package/templates/vercel-ai-quickstart/tsconfig.json +33 -0
@@ -0,0 +1,195 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+ import { LayerCard } from "./LayerCard";
5
+ import type { LayerState } from "@/lib/layer-tracking";
6
+
7
+ interface LayerFlowDiagramProps {
8
+ layers: Record<string, LayerState>;
9
+ isOrchestrating: boolean;
10
+ memorySpaceId: string;
11
+ userId: string;
12
+ }
13
+
14
+ export function LayerFlowDiagram({
15
+ layers,
16
+ isOrchestrating,
17
+ memorySpaceId,
18
+ userId,
19
+ }: LayerFlowDiagramProps) {
20
+ // Define the layer flow order
21
+ const topLayers = [
22
+ { key: "memorySpace", name: "Memory Space", icon: "📦" },
23
+ { key: "user", name: "User", icon: "👤" },
24
+ { key: "agent", name: "Agent", icon: "🤖" },
25
+ ];
26
+
27
+ const flowLayers = [
28
+ { key: "conversation", name: "Conversation", icon: "💬" },
29
+ { key: "vector", name: "Vector Store", icon: "🎯" },
30
+ { key: "facts", name: "Facts", icon: "💡" },
31
+ { key: "graph", name: "Graph", icon: "🕸️", optional: true },
32
+ ];
33
+
34
+ return (
35
+ <div className="space-y-6">
36
+ {/* Title section */}
37
+ <div className="text-center">
38
+ <h3 className="text-lg font-semibold">Memory Orchestration Flow</h3>
39
+ <p className="text-sm text-gray-400 mt-1">
40
+ {isOrchestrating
41
+ ? "Processing your message through Cortex layers..."
42
+ : "Send a message to see data flow through the system"}
43
+ </p>
44
+ </div>
45
+
46
+ {/* Top row: Context layers */}
47
+ <div className="flex justify-center gap-3">
48
+ {topLayers.map((layer, i) => (
49
+ <motion.div
50
+ key={layer.key}
51
+ initial={{ opacity: 0, y: -10 }}
52
+ animate={{ opacity: 1, y: 0 }}
53
+ transition={{ delay: i * 0.1 }}
54
+ >
55
+ <LayerCard
56
+ name={layer.name}
57
+ icon={layer.icon}
58
+ status={layers[layer.key]?.status || "pending"}
59
+ latencyMs={layers[layer.key]?.latencyMs}
60
+ data={layers[layer.key]?.data}
61
+ compact
62
+ />
63
+ </motion.div>
64
+ ))}
65
+ </div>
66
+
67
+ {/* Flow connector */}
68
+ <div className="flex justify-center">
69
+ <motion.div
70
+ className="w-0.5 h-8 bg-gradient-to-b from-white/30 to-white/10"
71
+ animate={{
72
+ opacity: isOrchestrating ? [0.3, 1, 0.3] : 0.3,
73
+ }}
74
+ transition={{
75
+ duration: 1.5,
76
+ repeat: Infinity,
77
+ ease: "easeInOut",
78
+ }}
79
+ />
80
+ </div>
81
+
82
+ {/* Main flow: Processing layers */}
83
+ <div className="space-y-3">
84
+ {flowLayers.map((layer, i) => (
85
+ <motion.div
86
+ key={layer.key}
87
+ initial={{ opacity: 0, x: -20 }}
88
+ animate={{ opacity: 1, x: 0 }}
89
+ transition={{ delay: 0.3 + i * 0.1 }}
90
+ >
91
+ <LayerCard
92
+ name={layer.name}
93
+ icon={layer.icon}
94
+ status={
95
+ layers[layer.key]?.status ||
96
+ (layer.optional ? "skipped" : "pending")
97
+ }
98
+ latencyMs={layers[layer.key]?.latencyMs}
99
+ data={layers[layer.key]?.data}
100
+ optional={layer.optional}
101
+ // Belief revision info (v0.24.0+) - only for facts layer
102
+ revisionAction={
103
+ layer.key === "facts"
104
+ ? layers[layer.key]?.revisionAction
105
+ : undefined
106
+ }
107
+ supersededFacts={
108
+ layer.key === "facts"
109
+ ? layers[layer.key]?.supersededFacts
110
+ : undefined
111
+ }
112
+ />
113
+
114
+ {/* Flow connector between layers */}
115
+ {i < flowLayers.length - 1 && (
116
+ <div className="flex justify-center py-2">
117
+ <motion.div
118
+ className="w-0.5 h-6"
119
+ style={{
120
+ background:
121
+ layers[layer.key]?.status === "complete"
122
+ ? "linear-gradient(to bottom, rgb(34, 197, 94), rgba(255, 255, 255, 0.1))"
123
+ : "linear-gradient(to bottom, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05))",
124
+ }}
125
+ animate={{
126
+ opacity:
127
+ isOrchestrating &&
128
+ layers[layer.key]?.status === "in_progress"
129
+ ? [0.3, 1, 0.3]
130
+ : 1,
131
+ }}
132
+ transition={{
133
+ duration: 0.8,
134
+ repeat: Infinity,
135
+ ease: "easeInOut",
136
+ }}
137
+ />
138
+ </div>
139
+ )}
140
+ </motion.div>
141
+ ))}
142
+ </div>
143
+
144
+ {/* Legend */}
145
+ <div className="pt-4 border-t border-white/10 space-y-2">
146
+ {/* Status legend */}
147
+ <div className="flex justify-center gap-4 text-xs text-gray-500">
148
+ <div className="flex items-center gap-1.5">
149
+ <span className="w-2 h-2 rounded-full bg-gray-500" />
150
+ <span>Pending</span>
151
+ </div>
152
+ <div className="flex items-center gap-1.5">
153
+ <span className="w-2 h-2 rounded-full bg-yellow-500 animate-pulse" />
154
+ <span>Processing</span>
155
+ </div>
156
+ <div className="flex items-center gap-1.5">
157
+ <span className="w-2 h-2 rounded-full bg-green-500" />
158
+ <span>Complete</span>
159
+ </div>
160
+ <div className="flex items-center gap-1.5">
161
+ <span className="w-2 h-2 rounded-full bg-gray-700" />
162
+ <span>Skipped</span>
163
+ </div>
164
+ </div>
165
+ {/* Belief revision legend (v0.24.0+) */}
166
+ <div className="flex justify-center gap-3 text-xs text-gray-600">
167
+ <span className="text-gray-700">Belief Revision:</span>
168
+ <div className="flex items-center gap-1">
169
+ <span className="px-1 py-0.5 bg-green-500/20 text-green-400 rounded text-[9px]">
170
+ NEW
171
+ </span>
172
+ </div>
173
+ <div className="flex items-center gap-1">
174
+ <span className="px-1 py-0.5 bg-blue-500/20 text-blue-400 rounded text-[9px]">
175
+ UPDATED
176
+ </span>
177
+ </div>
178
+ <div className="flex items-center gap-1">
179
+ <span className="px-1 py-0.5 bg-orange-500/20 text-orange-400 rounded text-[9px]">
180
+ SUPERSEDED
181
+ </span>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ {/* Context info */}
187
+ <div className="text-center text-xs text-gray-600">
188
+ <p>
189
+ Space: <code className="text-gray-400">{memorySpaceId}</code> • User:{" "}
190
+ <code className="text-gray-400">{userId}</code>
191
+ </p>
192
+ </div>
193
+ </div>
194
+ );
195
+ }
@@ -0,0 +1,202 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { useAuth } from "./AuthProvider";
5
+
6
+ type Tab = "login" | "register";
7
+
8
+ export function LoginScreen() {
9
+ const { login, register, error, clearError } = useAuth();
10
+ const [activeTab, setActiveTab] = useState<Tab>("login");
11
+ const [username, setUsername] = useState("");
12
+ const [password, setPassword] = useState("");
13
+ const [displayName, setDisplayName] = useState("");
14
+ const [isLoading, setIsLoading] = useState(false);
15
+ const [localError, setLocalError] = useState<string | null>(null);
16
+
17
+ const handleSubmit = async (e: React.FormEvent) => {
18
+ e.preventDefault();
19
+ setLocalError(null);
20
+ clearError();
21
+
22
+ if (!username.trim()) {
23
+ setLocalError("Username is required");
24
+ return;
25
+ }
26
+
27
+ if (!password) {
28
+ setLocalError("Password is required");
29
+ return;
30
+ }
31
+
32
+ setIsLoading(true);
33
+
34
+ if (activeTab === "login") {
35
+ await login(username, password);
36
+ } else {
37
+ await register(username, password, displayName || undefined);
38
+ }
39
+
40
+ setIsLoading(false);
41
+ };
42
+
43
+ const handleTabChange = (tab: Tab) => {
44
+ setActiveTab(tab);
45
+ setLocalError(null);
46
+ clearError();
47
+ };
48
+
49
+ const displayError = localError || error;
50
+
51
+ return (
52
+ <div className="min-h-screen flex items-center justify-center p-4">
53
+ <div className="w-full max-w-md">
54
+ {/* Logo and Title */}
55
+ <div className="text-center mb-8">
56
+ <div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-cortex-500 to-cortex-700 flex items-center justify-center shadow-lg shadow-cortex-500/20">
57
+ <span className="text-4xl">🧠</span>
58
+ </div>
59
+ <h1 className="text-3xl font-bold mb-2">Cortex Memory Demo</h1>
60
+ <p className="text-gray-400">Sign in to explore AI with long-term memory</p>
61
+ </div>
62
+
63
+ {/* Login/Register Card */}
64
+ <div className="glass rounded-2xl overflow-hidden">
65
+ {/* Tabs */}
66
+ <div className="flex border-b border-white/10">
67
+ <button
68
+ onClick={() => handleTabChange("login")}
69
+ className={`flex-1 py-4 text-sm font-medium transition-colors ${
70
+ activeTab === "login"
71
+ ? "text-white border-b-2 border-cortex-500"
72
+ : "text-gray-400 hover:text-white"
73
+ }`}
74
+ >
75
+ Sign In
76
+ </button>
77
+ <button
78
+ onClick={() => handleTabChange("register")}
79
+ className={`flex-1 py-4 text-sm font-medium transition-colors ${
80
+ activeTab === "register"
81
+ ? "text-white border-b-2 border-cortex-500"
82
+ : "text-gray-400 hover:text-white"
83
+ }`}
84
+ >
85
+ Create Account
86
+ </button>
87
+ </div>
88
+
89
+ {/* Form */}
90
+ <form onSubmit={handleSubmit} className="p-8 space-y-5">
91
+ <div>
92
+ <label
93
+ htmlFor="username"
94
+ className="block text-sm font-medium text-gray-300 mb-2"
95
+ >
96
+ Username
97
+ </label>
98
+ <input
99
+ id="username"
100
+ type="text"
101
+ value={username}
102
+ onChange={(e) => setUsername(e.target.value)}
103
+ placeholder="Enter your username"
104
+ className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
105
+ disabled={isLoading}
106
+ autoFocus
107
+ autoComplete="username"
108
+ />
109
+ </div>
110
+
111
+ {activeTab === "register" && (
112
+ <div>
113
+ <label
114
+ htmlFor="displayName"
115
+ className="block text-sm font-medium text-gray-300 mb-2"
116
+ >
117
+ Display Name{" "}
118
+ <span className="text-gray-500">(optional)</span>
119
+ </label>
120
+ <input
121
+ id="displayName"
122
+ type="text"
123
+ value={displayName}
124
+ onChange={(e) => setDisplayName(e.target.value)}
125
+ placeholder="How should we call you?"
126
+ className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
127
+ disabled={isLoading}
128
+ />
129
+ </div>
130
+ )}
131
+
132
+ <div>
133
+ <label
134
+ htmlFor="password"
135
+ className="block text-sm font-medium text-gray-300 mb-2"
136
+ >
137
+ Password
138
+ </label>
139
+ <input
140
+ id="password"
141
+ type="password"
142
+ value={password}
143
+ onChange={(e) => setPassword(e.target.value)}
144
+ placeholder="Enter your password"
145
+ className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
146
+ disabled={isLoading}
147
+ autoComplete={activeTab === "login" ? "current-password" : "new-password"}
148
+ />
149
+ </div>
150
+
151
+ {displayError && (
152
+ <div className="p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-sm">
153
+ {displayError}
154
+ </div>
155
+ )}
156
+
157
+ <button
158
+ type="submit"
159
+ disabled={isLoading || !username || !password}
160
+ className="w-full py-3 bg-cortex-600 hover:bg-cortex-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl font-medium transition-colors flex items-center justify-center gap-2"
161
+ >
162
+ {isLoading ? (
163
+ <>
164
+ <svg
165
+ className="animate-spin h-5 w-5"
166
+ xmlns="http://www.w3.org/2000/svg"
167
+ fill="none"
168
+ viewBox="0 0 24 24"
169
+ >
170
+ <circle
171
+ className="opacity-25"
172
+ cx="12"
173
+ cy="12"
174
+ r="10"
175
+ stroke="currentColor"
176
+ strokeWidth="4"
177
+ />
178
+ <path
179
+ className="opacity-75"
180
+ fill="currentColor"
181
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
182
+ />
183
+ </svg>
184
+ {activeTab === "login" ? "Signing in..." : "Creating account..."}
185
+ </>
186
+ ) : activeTab === "login" ? (
187
+ "Sign In"
188
+ ) : (
189
+ "Create Account"
190
+ )}
191
+ </button>
192
+ </form>
193
+ </div>
194
+
195
+ {/* Demo hint */}
196
+ <p className="mt-6 text-center text-sm text-gray-500">
197
+ Create an account to start chatting with memory-enabled AI
198
+ </p>
199
+ </div>
200
+ </div>
201
+ );
202
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { motion, AnimatePresence } from "framer-motion";
5
+
6
+ interface MemorySpaceSwitcherProps {
7
+ value: string;
8
+ onChange: (value: string) => void;
9
+ }
10
+
11
+ const spaces = [
12
+ { id: "quickstart-demo", name: "Demo", icon: "🧪" },
13
+ { id: "personal", name: "Personal", icon: "👤" },
14
+ { id: "work", name: "Work", icon: "💼" },
15
+ ];
16
+
17
+ export function MemorySpaceSwitcher({
18
+ value,
19
+ onChange,
20
+ }: MemorySpaceSwitcherProps) {
21
+ const [isOpen, setIsOpen] = useState(false);
22
+ const currentSpace = spaces.find((s) => s.id === value) || spaces[0];
23
+
24
+ return (
25
+ <div className="relative">
26
+ <button
27
+ onClick={() => setIsOpen(!isOpen)}
28
+ className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-lg transition-colors"
29
+ >
30
+ <span>{currentSpace.icon}</span>
31
+ <span className="text-sm font-medium">{currentSpace.name}</span>
32
+ <motion.span
33
+ className="text-gray-400 text-xs"
34
+ animate={{ rotate: isOpen ? 180 : 0 }}
35
+ >
36
+
37
+ </motion.span>
38
+ </button>
39
+
40
+ <AnimatePresence>
41
+ {isOpen && (
42
+ <>
43
+ {/* Backdrop */}
44
+ <div
45
+ className="fixed inset-0 z-10"
46
+ onClick={() => setIsOpen(false)}
47
+ />
48
+
49
+ {/* Dropdown */}
50
+ <motion.div
51
+ initial={{ opacity: 0, y: -10 }}
52
+ animate={{ opacity: 1, y: 0 }}
53
+ exit={{ opacity: 0, y: -10 }}
54
+ className="absolute right-0 top-full mt-2 w-56 bg-gray-900 border border-white/10 rounded-lg shadow-xl z-20 overflow-hidden"
55
+ >
56
+ <div className="p-2">
57
+ <div className="text-xs text-gray-500 px-2 py-1 mb-1">
58
+ Memory Space
59
+ </div>
60
+
61
+ {spaces.map((space) => (
62
+ <button
63
+ key={space.id}
64
+ onClick={() => {
65
+ onChange(space.id);
66
+ setIsOpen(false);
67
+ }}
68
+ className={`w-full flex items-center gap-2 px-3 py-2 rounded-lg text-left transition-colors ${
69
+ value === space.id
70
+ ? "bg-cortex-600/20 text-cortex-400"
71
+ : "hover:bg-white/5"
72
+ }`}
73
+ >
74
+ <span>{space.icon}</span>
75
+ <span className="flex-1 font-medium">{space.name}</span>
76
+ <code className="text-xs text-gray-500">{space.id}</code>
77
+ </button>
78
+ ))}
79
+ </div>
80
+
81
+ <div className="border-t border-white/10 p-3 bg-white/5">
82
+ <p className="text-xs text-gray-400">
83
+ Switch memory spaces to demonstrate multi-tenant isolation.
84
+ Memories in one space don't appear in others.
85
+ </p>
86
+ </div>
87
+ </motion.div>
88
+ </>
89
+ )}
90
+ </AnimatePresence>
91
+ </div>
92
+ );
93
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Convex queries for real-time conversation updates
3
+ *
4
+ * These queries enable the LayerFlowDiagram to show live updates
5
+ * as data flows through the Cortex memory system.
6
+ */
7
+
8
+ import { query } from "./_generated/server";
9
+ import { v } from "convex/values";
10
+
11
+ /**
12
+ * Get recent conversations for a memory space
13
+ *
14
+ * Used by the demo to watch for new conversations being created.
15
+ */
16
+ export const getRecent = query({
17
+ args: {
18
+ memorySpaceId: v.string(),
19
+ limit: v.optional(v.number()),
20
+ },
21
+ handler: async (ctx, args) => {
22
+ const limit = args.limit ?? 10;
23
+
24
+ // Query conversations table (from Cortex SDK schema)
25
+ const conversations = await ctx.db
26
+ .query("conversations")
27
+ .filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
28
+ .order("desc")
29
+ .take(limit);
30
+
31
+ return conversations;
32
+ },
33
+ });
34
+
35
+ /**
36
+ * Get a specific conversation by ID
37
+ */
38
+ export const get = query({
39
+ args: {
40
+ conversationId: v.string(),
41
+ },
42
+ handler: async (ctx, args) => {
43
+ const conversation = await ctx.db
44
+ .query("conversations")
45
+ .filter((q) => q.eq(q.field("conversationId"), args.conversationId))
46
+ .first();
47
+
48
+ return conversation;
49
+ },
50
+ });
51
+
52
+ /**
53
+ * Get conversation count for a memory space
54
+ */
55
+ export const count = query({
56
+ args: {
57
+ memorySpaceId: v.string(),
58
+ },
59
+ handler: async (ctx, args) => {
60
+ const conversations = await ctx.db
61
+ .query("conversations")
62
+ .filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
63
+ .collect();
64
+
65
+ return conversations.length;
66
+ },
67
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Convex queries for real-time fact updates
3
+ *
4
+ * These queries enable the LayerFlowDiagram to show live updates
5
+ * as facts are extracted from conversations.
6
+ */
7
+
8
+ import { query } from "./_generated/server";
9
+ import { v } from "convex/values";
10
+
11
+ /**
12
+ * Get recent facts for a memory space
13
+ *
14
+ * Used by the demo to watch for new facts being extracted.
15
+ */
16
+ export const getRecent = query({
17
+ args: {
18
+ memorySpaceId: v.string(),
19
+ limit: v.optional(v.number()),
20
+ },
21
+ handler: async (ctx, args) => {
22
+ const limit = args.limit ?? 20;
23
+
24
+ // Query facts table (from Cortex SDK schema)
25
+ const facts = await ctx.db
26
+ .query("facts")
27
+ .filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
28
+ .order("desc")
29
+ .take(limit);
30
+
31
+ return facts;
32
+ },
33
+ });
34
+
35
+ /**
36
+ * Get facts for a specific user
37
+ */
38
+ export const getByUser = query({
39
+ args: {
40
+ memorySpaceId: v.string(),
41
+ userId: v.string(),
42
+ limit: v.optional(v.number()),
43
+ },
44
+ handler: async (ctx, args) => {
45
+ const limit = args.limit ?? 50;
46
+
47
+ const facts = await ctx.db
48
+ .query("facts")
49
+ .filter((q) =>
50
+ q.and(
51
+ q.eq(q.field("memorySpaceId"), args.memorySpaceId),
52
+ q.eq(q.field("userId"), args.userId),
53
+ ),
54
+ )
55
+ .order("desc")
56
+ .take(limit);
57
+
58
+ return facts;
59
+ },
60
+ });
61
+
62
+ /**
63
+ * Get facts by type
64
+ */
65
+ export const getByType = query({
66
+ args: {
67
+ memorySpaceId: v.string(),
68
+ factType: v.string(),
69
+ limit: v.optional(v.number()),
70
+ },
71
+ handler: async (ctx, args) => {
72
+ const limit = args.limit ?? 20;
73
+
74
+ const facts = await ctx.db
75
+ .query("facts")
76
+ .filter((q) =>
77
+ q.and(
78
+ q.eq(q.field("memorySpaceId"), args.memorySpaceId),
79
+ q.eq(q.field("factType"), args.factType),
80
+ ),
81
+ )
82
+ .order("desc")
83
+ .take(limit);
84
+
85
+ return facts;
86
+ },
87
+ });
88
+
89
+ /**
90
+ * Get fact count for a memory space
91
+ */
92
+ export const count = query({
93
+ args: {
94
+ memorySpaceId: v.string(),
95
+ },
96
+ handler: async (ctx, args) => {
97
+ const facts = await ctx.db
98
+ .query("facts")
99
+ .filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
100
+ .collect();
101
+
102
+ return facts.length;
103
+ },
104
+ });
105
+
106
+ /**
107
+ * Get fact type summary for a user
108
+ */
109
+ export const typeSummary = query({
110
+ args: {
111
+ memorySpaceId: v.string(),
112
+ userId: v.optional(v.string()),
113
+ },
114
+ handler: async (ctx, args) => {
115
+ let factsQuery = ctx.db
116
+ .query("facts")
117
+ .filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId));
118
+
119
+ const facts = await factsQuery.collect();
120
+
121
+ // Group by type
122
+ const summary: Record<string, number> = {};
123
+ for (const fact of facts) {
124
+ if (args.userId && fact.userId !== args.userId) continue;
125
+ const type = fact.factType || "unknown";
126
+ summary[type] = (summary[type] || 0) + 1;
127
+ }
128
+
129
+ return summary;
130
+ },
131
+ });