feat: journal dashboard widgets, agent polish, and edit-mode live previews
Journal dashboard widgets and quick-add; rich-text quick-add dialogs. Dashboard draft sync for live edit previews; assistant bubble + API tools. Journal UX: stress slider, mood grid, query cap fix.
This commit is contained in:
+640
-2
@@ -1,4 +1,7 @@
|
||||
import type { AgentToolDefinition } from "@/lib/llm";
|
||||
import { MOOD_CATALOG } from "@/modules/journal/mood-catalog";
|
||||
|
||||
const JOURNAL_MOOD_IDS = MOOD_CATALOG.map((mood) => mood.id).join(", ");
|
||||
|
||||
export const AGENT_TOOLS: AgentToolDefinition[] = [
|
||||
{
|
||||
@@ -46,6 +49,85 @@ export const AGENT_TOOLS: AgentToolDefinition[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_list_item",
|
||||
description:
|
||||
"Update a list item — mark done/undone, rename, or change quantity. Use list_list_items first to resolve itemId.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
listId: { type: "string", description: "UUID of the list" },
|
||||
itemId: { type: "string", description: "UUID of the item" },
|
||||
done: { type: "boolean", description: "Mark completed (true) or open (false)" },
|
||||
text: { type: "string", description: "New item text" },
|
||||
qty: { type: "string", description: "New quantity" },
|
||||
},
|
||||
required: ["listId", "itemId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_list_item",
|
||||
description: "Remove an item from a list permanently.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
listId: { type: "string", description: "UUID of the list" },
|
||||
itemId: { type: "string", description: "UUID of the item" },
|
||||
},
|
||||
required: ["listId", "itemId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_list",
|
||||
description: "Create a new list (e.g. type shopping or tasks).",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
type: { type: "string", description: "List type, e.g. shopping or tasks" },
|
||||
name: { type: "string", description: "Display name" },
|
||||
},
|
||||
required: ["type", "name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_list",
|
||||
description: "Rename a list or archive/unarchive it.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
listId: { type: "string" },
|
||||
name: { type: "string" },
|
||||
archived: { type: "boolean" },
|
||||
},
|
||||
required: ["listId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_list",
|
||||
description: "Permanently delete a list and all its items.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
listId: { type: "string" },
|
||||
},
|
||||
required: ["listId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
@@ -88,11 +170,66 @@ export const AGENT_TOOLS: AgentToolDefinition[] = [
|
||||
allDay: { type: "boolean" },
|
||||
location: { type: "string" },
|
||||
notes: { type: "string" },
|
||||
remindMinutesBefore: {
|
||||
type: "number",
|
||||
description: "Optional reminder N minutes before start",
|
||||
},
|
||||
},
|
||||
required: ["calendarId", "title", "startAt", "endAt"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_event",
|
||||
description: "Update or reschedule a calendar event.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
eventId: { type: "string" },
|
||||
title: { type: "string" },
|
||||
startAt: { type: "string" },
|
||||
endAt: { type: "string" },
|
||||
allDay: { type: "boolean" },
|
||||
location: { type: "string" },
|
||||
notes: { type: "string" },
|
||||
remindMinutesBefore: { type: "number" },
|
||||
},
|
||||
required: ["eventId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_event",
|
||||
description: "Delete a calendar event.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
eventId: { type: "string" },
|
||||
},
|
||||
required: ["eventId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_calendar",
|
||||
description: "Create a new calendar.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
color: { type: "string" },
|
||||
visibility: { type: "string", description: "private or household" },
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
@@ -112,11 +249,44 @@ export const AGENT_TOOLS: AgentToolDefinition[] = [
|
||||
title: { type: "string" },
|
||||
body: { type: "string" },
|
||||
pinned: { type: "boolean" },
|
||||
remindAt: { type: "string", description: "ISO 8601 reminder datetime, or null to clear" },
|
||||
},
|
||||
required: ["title"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_note",
|
||||
description: "Update a note's title, body, pin state, or reminder.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
noteId: { type: "string" },
|
||||
title: { type: "string" },
|
||||
body: { type: "string" },
|
||||
pinned: { type: "boolean" },
|
||||
remindAt: { type: "string", description: "ISO 8601 datetime or null" },
|
||||
},
|
||||
required: ["noteId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_note",
|
||||
description: "Delete a note.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
noteId: { type: "string" },
|
||||
},
|
||||
required: ["noteId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
@@ -130,10 +300,478 @@ export const AGENT_TOOLS: AgentToolDefinition[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_journal_entry",
|
||||
description:
|
||||
"Create a journal entry for the current user. Supports moods, stress (1-10), and pillsTaken.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
recordedAt: { type: "string", description: "ISO 8601 date/time for the entry" },
|
||||
title: { type: "string" },
|
||||
body: { type: "string" },
|
||||
moods: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: `Mood ids: ${JOURNAL_MOOD_IDS}`,
|
||||
},
|
||||
stress: { type: "number", description: "Stress level 1-10" },
|
||||
pillsTaken: { type: "boolean", description: "Whether pills were taken" },
|
||||
},
|
||||
required: ["recordedAt"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_journal_entry",
|
||||
description: "Update a journal entry including moods, stress, and pillsTaken.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entryId: { type: "string" },
|
||||
recordedAt: { type: "string" },
|
||||
title: { type: "string" },
|
||||
body: { type: "string" },
|
||||
moods: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
description: `Mood ids: ${JOURNAL_MOOD_IDS}`,
|
||||
},
|
||||
stress: { type: "number" },
|
||||
pillsTaken: { type: "boolean" },
|
||||
},
|
||||
required: ["entryId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_journal_entry",
|
||||
description: "Delete a journal entry.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entryId: { type: "string" },
|
||||
},
|
||||
required: ["entryId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_bangs",
|
||||
description: "Get bang counter stats and recent entries for the household.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "number", description: "Recent entries to include (default 10)" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "add_bang",
|
||||
description: "Record a new bang. Date defaults to today if omitted.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
occurredOn: { type: "string", description: "YYYY-MM-DD" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_bang",
|
||||
description: "Change the date of an existing bang entry.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
bangId: { type: "string" },
|
||||
occurredOn: { type: "string", description: "YYYY-MM-DD" },
|
||||
},
|
||||
required: ["bangId", "occurredOn"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_bang",
|
||||
description: "Delete a bang entry.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
bangId: { type: "string" },
|
||||
},
|
||||
required: ["bangId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_garden_containers",
|
||||
description: "List garden containers (pots, beds, etc.).",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_garden_plants",
|
||||
description: "List plants. Optionally filter by containerId.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
containerId: { type: "string", description: "UUID of container" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_garden_plant",
|
||||
description: "Get full detail for a plant by id.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { plantId: { type: "string" } },
|
||||
required: ["plantId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_garden_container",
|
||||
description: "Create a garden container.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
type: { type: "string", description: "e.g. pot, bed, greenhouse" },
|
||||
locationNotes: { type: "string" },
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_garden_container",
|
||||
description: "Update a garden container.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
containerId: { type: "string" },
|
||||
name: { type: "string" },
|
||||
type: { type: "string" },
|
||||
locationNotes: { type: "string" },
|
||||
},
|
||||
required: ["containerId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_garden_container",
|
||||
description: "Delete a garden container.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { containerId: { type: "string" } },
|
||||
required: ["containerId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_garden_plant",
|
||||
description: "Add a new plant to the garden.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
category: { type: "string" },
|
||||
containerId: { type: "string" },
|
||||
healthStatus: { type: "string" },
|
||||
notes: { type: "string" },
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_garden_plant",
|
||||
description: "Update plant details (name, health, container, notes, etc.).",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
plantId: { type: "string" },
|
||||
name: { type: "string" },
|
||||
category: { type: "string" },
|
||||
containerId: { type: "string" },
|
||||
healthStatus: { type: "string" },
|
||||
notes: { type: "string" },
|
||||
},
|
||||
required: ["plantId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_garden_plant",
|
||||
description: "Delete a plant.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { plantId: { type: "string" } },
|
||||
required: ["plantId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "log_garden_care",
|
||||
description: "Record care performed on a plant (water, fertilize, prune, etc.).",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
plantId: { type: "string" },
|
||||
careType: { type: "string", description: "e.g. water, fertilize, prune" },
|
||||
notes: { type: "string" },
|
||||
performedAt: { type: "string", description: "ISO 8601; defaults to now" },
|
||||
},
|
||||
required: ["plantId", "careType"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_garden_care_logs",
|
||||
description: "List recent care logs for a plant.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
plantId: { type: "string" },
|
||||
limit: { type: "number" },
|
||||
},
|
||||
required: ["plantId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_garden_care_schedules",
|
||||
description: "List care schedules (recurring reminders) for a plant.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { plantId: { type: "string" } },
|
||||
required: ["plantId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "upsert_garden_care_schedule",
|
||||
description: "Create or update a recurring care schedule for a plant.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
plantId: { type: "string" },
|
||||
careType: { type: "string" },
|
||||
intervalDays: { type: "number", description: "Days between care" },
|
||||
enabled: { type: "boolean" },
|
||||
},
|
||||
required: ["plantId", "careType", "intervalDays"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_garden_care_schedule",
|
||||
description: "Delete a care schedule by schedule id.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { scheduleId: { type: "string" } },
|
||||
required: ["scheduleId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "toggle_garden_care_schedule",
|
||||
description: "Enable or disable a care schedule.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
scheduleId: { type: "string" },
|
||||
enabled: { type: "boolean" },
|
||||
},
|
||||
required: ["scheduleId", "enabled"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "push_overdue_garden_care",
|
||||
description: "Push overdue garden care items to the household task list.",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "schedule_garden_care_on_calendar",
|
||||
description: "Add a care schedule's next due date as a calendar event.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
scheduleId: { type: "string" },
|
||||
calendarId: { type: "string" },
|
||||
reminderMinutesBefore: { type: "number" },
|
||||
},
|
||||
required: ["scheduleId", "calendarId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_shareable_entity_types",
|
||||
description: "List entity types that can be shared via public links.",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_share_links",
|
||||
description: "List active share links. Optionally filter by entityType and entityId.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityType: { type: "string" },
|
||||
entityId: { type: "string" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_share_link",
|
||||
description:
|
||||
"Create a temporary public share link for an entity. Entity types: calendar, calendar.event, list, note, garden.plant, garden.container.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entityType: { type: "string" },
|
||||
entityId: { type: "string" },
|
||||
expiresAt: { type: "string", description: "ISO 8601 expiry, or omit for no expiry" },
|
||||
write: { type: "boolean", description: "Allow write via link (default false)" },
|
||||
},
|
||||
required: ["entityType", "entityId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "revoke_share_link",
|
||||
description: "Revoke an active share link by its id.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: { linkId: { type: "string" } },
|
||||
required: ["linkId"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "get_api_docs",
|
||||
description:
|
||||
"Read famapp REST API documentation (OpenAPI). Use when unsure which endpoint to call or no dedicated tool exists. Pass search to filter relevant paths.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
search: {
|
||||
type: "string",
|
||||
description: "Optional keyword to filter paths (e.g. garden, share, journal)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "call_api",
|
||||
description:
|
||||
"Fallback: call any /api/v1/* endpoint directly. Use get_api_docs first when unsure. Only household-scoped v1 routes.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
method: {
|
||||
type: "string",
|
||||
enum: ["GET", "POST", "PATCH", "DELETE"],
|
||||
description: "HTTP method",
|
||||
},
|
||||
path: {
|
||||
type: "string",
|
||||
description: "API path starting with /api/v1/ (e.g. /api/v1/notes)",
|
||||
},
|
||||
query: {
|
||||
type: "object",
|
||||
description: "Optional query string parameters as key-value pairs",
|
||||
},
|
||||
body: {
|
||||
type: "object",
|
||||
description: "Optional JSON body for POST/PATCH",
|
||||
},
|
||||
},
|
||||
required: ["method", "path"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const AGENT_SYSTEM_PROMPT = `You are the famapp household assistant. Help Matt and his wife manage calendar events, shopping and task lists, notes, and personal journal entries.
|
||||
export const AGENT_SYSTEM_PROMPT = `You are the famapp household assistant. Help Matt and his wife manage calendar events, shopping and task lists, notes, journal entries, garden plants, share links, and the bang counter.
|
||||
|
||||
Use the provided tools to read and update data. Prefer calling tools instead of guessing. Be concise and friendly.
|
||||
|
||||
When adding shopping items, resolve the shopping list id via list_lists if needed. Use ISO 8601 datetimes for calendar tools.`;
|
||||
When no dedicated tool fits, or you are unsure how to do something:
|
||||
1. Call get_api_docs with a relevant search term to find the right /api/v1/* endpoint.
|
||||
2. Call call_api with the documented method, path, query, and body.
|
||||
|
||||
Lists: resolve list ids via list_lists. To complete items, list_list_items then update_list_item with done: true.
|
||||
|
||||
Journal: per-user private entries. Valid mood ids: ${JOURNAL_MOOD_IDS}. stress is 1-10. pillsTaken is boolean.
|
||||
|
||||
Garden: care types are free text (water, fertilize, prune, etc.). Use list_garden_plants to find plant ids.
|
||||
|
||||
Sharing: journal entries are not shareable. Shareable types: calendar, calendar.event, list, note, garden.plant, garden.container.
|
||||
|
||||
Calendar: use ISO 8601 datetimes. Bang dates use YYYY-MM-DD.`;
|
||||
|
||||
Reference in New Issue
Block a user