Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/.DS_Store

.env
.env
node_modules/
.next/
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"
import { normalizePriority } from "@/lib/priority"

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

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 })
if (process.env.NODE_ENV !== "production") {
console.debug("[meeting-agent] SDK response received")
}

const parsed = extractParsed(resData)
if (process.env.NODE_ENV !== "production") {
console.debug("[meeting-agent] Parsed payload extracted:", Boolean(parsed))
}

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 during flow execution")
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