Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
**/.DS_Store
**/.DS_Store
.env*
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
node_modules/
.next/
/meeting-action-agent/
4 changes: 4 additions & 0 deletions kits/automation/meeting-action-agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MEETING_ACTION_FLOW_ID="your-flow-id-here"
LAMATIC_API_URL="https://your-org.lamatic.dev/graphql"
LAMATIC_PROJECT_ID="your-project-id-here"
LAMATIC_API_KEY="your-api-key-here"
27 changes: 27 additions & 0 deletions kits/automation/meeting-action-agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# next.js
/.next/
/out/

# production
/build

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
147 changes: 147 additions & 0 deletions kits/automation/meeting-action-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# 📋 Meeting Action Items Agent

> **Problem:** After meetings, notes live in one place while tasks, docs, and follow-up emails live in others — so owners, deadlines, and priorities get lost in manual cleanup.
> **This kit:** One Lamatic flow + UI turns **pasted** notes or a transcript into **structured** decisions, **prioritized** action items (owner + deadline), a **markdown** summary, and a **ready-to-send** email draft — so you’re not re-typing the same meeting three times.

Built with [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) · [Lamatic Studio](https://studio.lamatic.ai) · Next.js

**v1 scope:** text in (notes / pasted transcript). Audio file upload is out of scope for this kit.

---

## ✨ What It Does

Paste any raw meeting notes, transcript, or summary and the agent will automatically extract:

- ✅ **Key Decisions** — numbered list of decisions made in the meeting
- 🎯 **Action Items** — each with an owner, deadline, and priority (High / Medium / Low)
- 📄 **Meeting Summary** — a clean markdown report
- 📧 **Follow-up Email Draft** — ready to copy and send to your team

---

## 🏗️ Architecture

```
User (pastes meeting notes)
Next.js Frontend (app/page.tsx)
Server Action (actions/orchestrate.ts)
Lamatic Flow (API Request → LLM → API Response)
Structured JSON output rendered in the UI
```

---

## 🚀 Quick Start

### Prerequisites

| Tool | Version |
|------|---------|
| Node.js | 18+ |
| npm | 9+ |
| Lamatic Account | [lamatic.ai](https://lamatic.ai) |

### 1. Clone the repo

```bash
git clone https://github.com/Lamatic/AgentKit.git
cd AgentKit/kits/automation/meeting-action-agent
```

### 2. Set up environment variables

```bash
cp .env.example .env.local
```

Edit `.env.local` and fill in your values:

```env
MEETING_ACTION_FLOW_ID="your-flow-id"
LAMATIC_API_URL="https://your-org.lamatic.dev/graphql"
LAMATIC_PROJECT_ID="your-project-id"
LAMATIC_API_KEY="your-api-key"
```

> **Where to find these values:**
> - `MEETING_ACTION_FLOW_ID` → Flow URL in Lamatic Studio (the UUID)
> - `LAMATIC_API_URL` → Settings → API Docs → Endpoint
> - `LAMATIC_PROJECT_ID` → Settings → Project → Project ID
> - `LAMATIC_API_KEY` → Settings → API Keys

### 3. Install & run

```bash
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) and paste your meeting notes!

---

## 🧠 Lamatic Flow Setup

### Trigger
- **Type:** API Request
- **Input schema:** `{ "meeting_notes": "string" }`

### LLM Node (Generate Text)
- **Model:** GPT-4o / GPT-4o-mini / Gemini 1.5 Pro
- **System prompt:** Instructs the model to extract decisions, action items, summary, and email as JSON
- **User message:** `{{trigger.meeting_notes}}`

### Response
- **Output schema:** `{ "result": "{{LLMNode.output.generatedResponse}}" }`

> See the exported flow files in the `flows/` folder.

---

## 📂 Project Structure

```
kits/automation/meeting-action-agent/
├── .env.example # Environment variables template
├── .gitignore
├── README.md
├── config.json # Kit metadata
├── package.json
├── actions/
│ └── orchestrate.ts # Server action calling Lamatic
├── app/
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx # Main UI
├── components/ # shadcn/ui components
├── flows/ # Exported Lamatic flow files
├── lib/
│ └── lamatic-client.ts # Lamatic SDK client
└── hooks/
```

---

## 🌐 Deploy to Vercel

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits/automation/meeting-action-agent&env=MEETING_ACTION_FLOW_ID,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY)

1. Click the button above
2. Set the **Root Directory** to `kits/automation/meeting-action-agent`
3. Add all 4 environment variables
4. Deploy!

---

## 🤝 Contributing

This kit is part of [Lamatic AgentKit](https://github.com/Lamatic/AgentKit). See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.

## 📜 License

MIT — see [LICENSE](../../LICENSE)
94 changes: 94 additions & 0 deletions kits/automation/meeting-action-agent/actions/orchestrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use server"

import { lamaticClient } from "@/lib/lamatic-client"

type Priority = "High" | "Medium" | "Low"

function normalizePriority(p: string): Priority {
const cap = ((p ?? "medium").charAt(0).toUpperCase() + (p ?? "medium").slice(1).toLowerCase()) as Priority
return (["High", "Medium", "Low"] as Priority[]).includes(cap) ? cap : "Medium"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

function extractParsed(resData: any): any {
// Try every possible path the Lamatic SDK might use
const candidates = [
resData?.result?.result, // {result: {result: {...}}}
resData?.result, // {result: {...}}
resData?.data?.result, // {data: {result: {...}}}
resData?.data, // {data: {...}}
resData, // top-level
]
for (const c of candidates) {
if (c && typeof c === "object" && (c.decisions || c.action_items || c.summary_report)) {
return c
}
}
// If none matched, try parsing the first string candidate
for (const c of candidates) {
if (typeof c === "string") {
try {
const cleaned = c.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim()
if (cleaned.startsWith("{")) {
return JSON.parse(cleaned)
}
} catch {}
}
}
return null
}

export async function analyzeMeeting(meetingNotes: string): Promise<{
success: boolean
data?: {
decisions: string[]
action_items: Array<{ task: string; owner: string; deadline: string; priority: Priority }>
summary_report: string
followup_email: string
}
rawResult?: string
error?: string
}> {
try {
const flowId = process.env.MEETING_ACTION_FLOW_ID
if (!flowId) throw new Error("MEETING_ACTION_FLOW_ID is not set in environment variables.")

const resData = await lamaticClient.executeFlow(flowId, { meeting_notes: meetingNotes })
console.log("[meeting-agent] Full SDK response:", JSON.stringify(resData))

const parsed = extractParsed(resData)
console.log("[meeting-agent] Extracted parsed:", JSON.stringify(parsed))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (!parsed) {
// Return raw so the UI can show something
return { success: true, rawResult: JSON.stringify(resData, null, 2) }
}

return {
success: true,
data: {
decisions: Array.isArray(parsed.decisions) ? parsed.decisions : [],
action_items: Array.isArray(parsed.action_items)
? parsed.action_items.map((item: any) => ({
task: item.task ?? "",
owner: item.owner ?? "Unassigned",
deadline: item.deadline ?? "TBD",
priority: normalizePriority(item.priority ?? "medium"),
}))
Comment on lines +70 to +76
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mission-critical: harden action_items mapping against null/primitive entries.

LLM output can include null or non-object items; direct property access can throw and fail the whole action.

Suggested diff
-        action_items: Array.isArray(parsed.action_items)
-          ? parsed.action_items.map((item: any) => ({
-              task: item.task ?? "",
-              owner: item.owner ?? "Unassigned",
-              deadline: item.deadline ?? "TBD",
-              priority: normalizePriority(item.priority ?? "medium"),
-            }))
+        action_items: Array.isArray(parsed.action_items)
+          ? parsed.action_items.map((item: any) => {
+              const safeItem = item && typeof item === "object" ? item : {}
+              return {
+                task: typeof safeItem.task === "string" ? safeItem.task : "",
+                owner: typeof safeItem.owner === "string" ? safeItem.owner : "Unassigned",
+                deadline: typeof safeItem.deadline === "string" ? safeItem.deadline : "TBD",
+                priority: normalizePriority(safeItem.priority),
+              }
+            })
           : [],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@kits/automation/meeting-action-agent/actions/orchestrate.ts` around lines 70
- 76, The mapping over parsed.action_items can throw when items are null or
primitives; update the logic around parsed.action_items so you first ensure it's
an array and then filter/type-guard each entry (e.g., keep only items where
typeof item === "object" && item !== null) before mapping; within the map for
action_items use safe property reads with fallbacks and pass the filtered value
to normalizePriority(item.priority ?? "medium") to avoid accessing properties on
non-objects (referencing parsed.action_items, the action_items mapping block,
and normalizePriority).

: [],
summary_report: parsed.summary_report ?? "",
followup_email: parsed.followup_email ?? "",
},
}
} catch (error) {
console.error("[meeting-agent] Error:", error)
let errorMessage = "Unknown error occurred"
if (error instanceof Error) {
errorMessage = error.message
if (error.message.includes("fetch failed"))
errorMessage = "Network error: Cannot connect to the service."
else if (error.message.includes("API key"))
errorMessage = "Authentication error: Check your LAMATIC_API_KEY."
}
return { success: false, error: errorMessage }
}
}
Loading