{imagePart && (
diff --git a/drizzle/migrations/0001_aromatic_ultimatum.sql b/drizzle/migrations/0001_aromatic_ultimatum.sql
new file mode 100644
index 00000000..dca6de0b
--- /dev/null
+++ b/drizzle/migrations/0001_aromatic_ultimatum.sql
@@ -0,0 +1,19 @@
+CREATE TABLE "calendar_notes" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "chat_id" uuid,
+ "date" timestamp with time zone NOT NULL,
+ "content" text NOT NULL,
+ "location_tags" jsonb,
+ "user_tags" text[],
+ "map_feature_id" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "messages" ADD COLUMN "attachments" jsonb;--> statement-breakpoint
+ALTER TABLE "messages" ADD COLUMN "tool_name" varchar(100);--> statement-breakpoint
+ALTER TABLE "messages" ADD COLUMN "tool_call_id" varchar(100);--> statement-breakpoint
+ALTER TABLE "messages" ADD COLUMN "type" varchar(50);--> statement-breakpoint
+ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0000_snapshot.json b/drizzle/migrations/meta/0000_snapshot.json
index eb62145d..8f31b8c5 100644
--- a/drizzle/migrations/meta/0000_snapshot.json
+++ b/drizzle/migrations/meta/0000_snapshot.json
@@ -1,10 +1,8 @@
{
- "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9",
- "prevId": "00000000-0000-0000-0000-000000000000",
- "version": "5",
- "dialect": "pg",
+ "version": "7",
+ "dialect": "postgresql",
"tables": {
- "chats": {
+ "public.chats": {
"name": "chats",
"schema": "",
"columns": {
@@ -48,21 +46,24 @@
"chats_user_id_users_id_fk": {
"name": "chats_user_id_users_id_fk",
"tableFrom": "chats",
- "tableTo": "users",
"columnsFrom": [
"user_id"
],
+ "tableTo": "users",
"columnsTo": [
"id"
],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "onUpdate": "no action",
+ "onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
- "uniqueConstraints": {}
+ "uniqueConstraints": {},
+ "policies": {},
+ "isRLSEnabled": false,
+ "checkConstraints": {}
},
- "messages": {
+ "public.messages": {
"name": "messages",
"schema": "",
"columns": {
@@ -110,34 +111,37 @@
"messages_chat_id_chats_id_fk": {
"name": "messages_chat_id_chats_id_fk",
"tableFrom": "messages",
- "tableTo": "chats",
"columnsFrom": [
"chat_id"
],
+ "tableTo": "chats",
"columnsTo": [
"id"
],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "onUpdate": "no action",
+ "onDelete": "cascade"
},
"messages_user_id_users_id_fk": {
"name": "messages_user_id_users_id_fk",
"tableFrom": "messages",
- "tableTo": "users",
"columnsFrom": [
"user_id"
],
+ "tableTo": "users",
"columnsTo": [
"id"
],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "onUpdate": "no action",
+ "onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
- "uniqueConstraints": {}
+ "uniqueConstraints": {},
+ "policies": {},
+ "isRLSEnabled": false,
+ "checkConstraints": {}
},
- "users": {
+ "public.users": {
"name": "users",
"schema": "",
"columns": {
@@ -152,14 +156,23 @@
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
- "uniqueConstraints": {}
+ "uniqueConstraints": {},
+ "policies": {},
+ "isRLSEnabled": false,
+ "checkConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
- "columns": {},
"schemas": {},
- "tables": {}
- }
+ "tables": {},
+ "columns": {}
+ },
+ "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9",
+ "prevId": "00000000-0000-0000-0000-000000000000",
+ "sequences": {},
+ "policies": {},
+ "views": {},
+ "roles": {}
}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0001_snapshot.json b/drizzle/migrations/meta/0001_snapshot.json
new file mode 100644
index 00000000..49d7c82b
--- /dev/null
+++ b/drizzle/migrations/meta/0001_snapshot.json
@@ -0,0 +1,305 @@
+{
+ "id": "63b6e4b5-8ef8-4789-a16c-d109a5fa233a",
+ "prevId": "0d46923a-5423-4b73-91cb-5f46741e7ff9",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.calendar_notes": {
+ "name": "calendar_notes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "chat_id": {
+ "name": "chat_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "location_tags": {
+ "name": "location_tags",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_tags": {
+ "name": "user_tags",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "map_feature_id": {
+ "name": "map_feature_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "calendar_notes_user_id_users_id_fk": {
+ "name": "calendar_notes_user_id_users_id_fk",
+ "tableFrom": "calendar_notes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "calendar_notes_chat_id_chats_id_fk": {
+ "name": "calendar_notes_chat_id_chats_id_fk",
+ "tableFrom": "calendar_notes",
+ "tableTo": "chats",
+ "columnsFrom": [
+ "chat_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chats": {
+ "name": "chats",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Untitled Chat'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'private'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "chats_user_id_users_id_fk": {
+ "name": "chats_user_id_users_id_fk",
+ "tableFrom": "chats",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.messages": {
+ "name": "messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "chat_id": {
+ "name": "chat_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_name": {
+ "name": "tool_name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "messages_chat_id_chats_id_fk": {
+ "name": "messages_chat_id_chats_id_fk",
+ "tableFrom": "messages",
+ "tableTo": "chats",
+ "columnsFrom": [
+ "chat_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "messages_user_id_users_id_fk": {
+ "name": "messages_user_id_users_id_fk",
+ "tableFrom": "messages",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json
index 34cd1203..d224f68c 100644
--- a/drizzle/migrations/meta/_journal.json
+++ b/drizzle/migrations/meta/_journal.json
@@ -8,6 +8,13 @@
"when": 1750358514791,
"tag": "0000_sweet_metal_master",
"breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "7",
+ "when": 1769972170699,
+ "tag": "0001_aromatic_ultimatum",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/actions/chat-db.ts b/lib/actions/chat-db.ts
index 4f0559ec..5169be9e 100644
--- a/lib/actions/chat-db.ts
+++ b/lib/actions/chat-db.ts
@@ -119,7 +119,21 @@ export async function saveChat(chatData: NewChat, messagesData: Omit m.role === 'data');
+
+ if (dataMessage) {
+ // Update existing message content using direct db call since chat-db.ts doesn't have an updateMessage
+ const { db } = await import('@/lib/db');
+ const { messages } = await import('@/lib/db/schema');
+ const { eq } = await import('drizzle-orm');
+
+ await db.update(messages)
+ .set({ content: JSON.stringify(contextData) })
+ .where(eq(messages.id, dataMessage.id));
+
+ console.log('Drawing context updated for chat:', chatId, 'messageId:', dataMessage.id);
+ return { success: true, messageId: dataMessage.id };
+ }
+
+ // Otherwise create a new one
const messageToSave: DbNewMessage = {
...newDrawingMessage,
chatId: chatId,
diff --git a/lib/agents/tools/geospatial.tsx b/lib/agents/tools/geospatial.tsx
index ccff0d02..4f1d9e0f 100644
--- a/lib/agents/tools/geospatial.tsx
+++ b/lib/agents/tools/geospatial.tsx
@@ -25,6 +25,7 @@ interface Location {
interface McpResponse {
location: Location;
mapUrl?: string;
+ geoJson?: any;
}
interface MapboxConfig {
@@ -381,11 +382,35 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g
const parsedData = content as any;
if (parsedData.results?.length > 0) {
const firstResult = parsedData.results[0];
- mcpData = { location: { latitude: firstResult.coordinates?.latitude, longitude: firstResult.coordinates?.longitude, place_name: firstResult.name || firstResult.place_name, address: firstResult.full_address || firstResult.address }, mapUrl: parsedData.mapUrl };
+ mcpData = {
+ location: {
+ latitude: firstResult.coordinates?.latitude,
+ longitude: firstResult.coordinates?.longitude,
+ place_name: firstResult.name || firstResult.place_name,
+ address: firstResult.full_address || firstResult.address
+ },
+ mapUrl: parsedData.mapUrl,
+ geoJson: parsedData.geoJson || parsedData.geojson || firstResult.geoJson || firstResult.geojson
+ };
} else if (parsedData.location) {
- mcpData = { location: { latitude: parsedData.location.latitude, longitude: parsedData.location.longitude, place_name: parsedData.location.place_name || parsedData.location.name, address: parsedData.location.address || parsedData.location.formatted_address }, mapUrl: parsedData.mapUrl || parsedData.map_url };
+ mcpData = {
+ location: {
+ latitude: parsedData.location.latitude,
+ longitude: parsedData.location.longitude,
+ place_name: parsedData.location.place_name || parsedData.location.name,
+ address: parsedData.location.address || parsedData.location.formatted_address
+ },
+ mapUrl: parsedData.mapUrl || parsedData.map_url,
+ geoJson: parsedData.geoJson || parsedData.geojson || parsedData.location.geoJson || parsedData.location.geojson
+ };
+ } else if (parsedData.type === 'FeatureCollection' || parsedData.type === 'Feature') {
+ // Direct GeoJSON response
+ mcpData = {
+ location: {}, // Will be derived from bbox if needed, or left empty
+ geoJson: parsedData
+ };
} else {
- throw new Error("Response missing required 'location' or 'results' field");
+ throw new Error("Response missing required 'location', 'results', or 'geoJson' field");
}
} else throw new Error('Unexpected response format from mapping service');
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index 4b7ef891..3242c083 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -30,10 +30,10 @@ export const messages = pgTable('messages', {
role: varchar('role', { length: 50 }).notNull(), // e.g., 'user', 'assistant', 'system', 'tool'
content: text('content').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
- // attachments: jsonb('attachments'), // As per PR commit: "feat: remove updatedAt and add attachments field to messages"
- // toolName: varchar('tool_name', { length: 100 }), // If messages can be from tools
- // toolCallId: varchar('tool_call_id', {length: 100}), // if tracking specific tool calls
- // type: varchar('type', { length: 50 }) // As per app/actions.tsx AIMessage type
+ attachments: jsonb('attachments'),
+ toolName: varchar('tool_name', { length: 100 }),
+ toolCallId: varchar('tool_call_id', {length: 100}),
+ type: varchar('type', { length: 50 })
});
// Calendar Notes Table
diff --git a/lib/types/index.ts b/lib/types/index.ts
index c4ea616c..d20b9803 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -74,6 +74,7 @@ export type AIMessage = {
| 'end'
| 'drawing_context' // Added custom type for drawing context messages
| 'resolution_search_result'
+ | 'geojson_upload' // Added custom type for GeoJSON upload messages
}
export type CalendarNote = {
diff --git a/package.json b/package.json
index cdf8a7e3..77546007 100644
--- a/package.json
+++ b/package.json
@@ -62,7 +62,7 @@
"csv-parse": "^6.1.0",
"dotenv": "^16.5.0",
"drizzle-kit": "^0.31.1",
- "drizzle-orm": "^0.29.0",
+ "drizzle-orm": "^0.45.1",
"embla-carousel-react": "^8.6.0",
"exa-js": "^1.6.13",
"framer-motion": "^12.23.24",