Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
c8b19e7
chore: scaffold know-thy-person app from content-generation reference
ayushgupta0610 Jul 26, 2026
c64089f
feat: rewire know-thy-person app config, env, lazy client, and action
ayushgupta0610 Jul 26, 2026
a61728e
feat: tolerant dossier normalizer (TDD) + form and dossier UI
ayushgupta0610 Jul 26, 2026
8ba61b1
feat: add know-thy-person lamatic.config.ts
ayushgupta0610 Jul 26, 2026
a250b32
docs: add know-thy-person agent.md and README
ayushgupta0610 Jul 26, 2026
1cfd17e
docs: replace copied app README with know-thy-person app README
ayushgupta0610 Jul 26, 2026
aaf4757
feat: add know-thy-person exported flow assets (flow, prompts, model-…
ayushgupta0610 Jul 26, 2026
a049453
fix: read flat dossier from flow result (flow maps fields at top leve…
ayushgupta0610 Jul 26, 2026
9cb23d1
chore: gitignore local lockfiles (pnpm/yarn)
ayushgupta0610 Jul 26, 2026
171d03f
fix: add postcss config so Tailwind compiles; rebrand header/title to…
ayushgupta0610 Jul 26, 2026
cb33753
chore: add kit-root .env.example to satisfy structural validation
ayushgupta0610 Jul 26, 2026
f073f7f
fix: relabel person_context field to company/personal site (LinkedIn/…
ayushgupta0610 Jul 26, 2026
3a8d04a
refactor: remove 58 unused shadcn/ui components, hooks, and theme-pro…
ayushgupta0610 Jul 26, 2026
5e1810b
feat: swap Resolve LLM for deterministic code node; fix Serper query …
ayushgupta0610 Jul 26, 2026
7c4f866
feat: research_url crawl target (company site or person_context link)…
ayushgupta0610 Jul 26, 2026
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: 6 additions & 0 deletions kits/know-thy-person/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Deployed Lamatic flow ID for the know-thy-person flow
KNOW_THY_PERSON="KNOW_THY_PERSON Flow ID"
# Lamatic API access (Studio → Settings → API)
LAMATIC_API_URL="LAMATIC_API_URL"
LAMATIC_PROJECT_ID="LAMATIC_PROJECT_ID"
LAMATIC_API_KEY="LAMATIC_API_KEY"
7 changes: 7 additions & 0 deletions kits/know-thy-person/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.lamatic/
node_modules/
.next/
.env
.env.local
next-env.d.ts
package-lock.json

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mission directive: retain npm’s resolution lock.

The documented npm install path will resolve a potentially different dependency graph on each fresh install because package-lock.json cannot be committed. Remove this ignore entry and commit the lockfile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/know-thy-person/.gitignore` at line 7, Remove the package-lock.json
entry from .gitignore and add the generated package-lock.json to version control
so npm installs use the committed dependency resolution.

87 changes: 87 additions & 0 deletions kits/know-thy-person/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Know Thy Person

Paste the **email** and **name** of whoever you're about to meet — optionally their
**company or personal website** — and get a one-page, **fully-sourced** dossier: who they
are, what they're into *outside work*, and warm talking points. Every claim links to a real
source; anything it can't verify is shown as "couldn't confirm" instead of invented.

> `person_context` accepts any link. A **company or personal site gets crawled** for real
> detail; a **LinkedIn/X profile isn't readable** (anti-bot walls) but still helps the agent
> find and confirm the right person among same-name results.

Built as a [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) **kit** (one Lamatic
flow + a Next.js app).

## Why it's different
Every existing research kit here is company-centric. This one researches the **individual
human** and leads with the **non-work / rapport** angle — and it's built to **never
fabricate** facts about a real person (the worst failure mode of person research). Only
sourced claims are ever rendered.

## How it works
```
email + name + person_context?
│ (API Request)
[1 Resolve] code (no LLM) → parses company/domain from the email; picks a research_url
(the company site, or the person_context link)
[2 Serper] → live public-presence web search (name + company + person_context)
[3 Firecrawl] → crawls the research_url for supplementary source content
[4 Synthesize] gemini-2.5-flash → dossier JSON from search results (primary) + crawl (supplementary),
each item carrying a source_url
│ (API Response)
answer = dossier JSON
```

Sourcing is enforced end-to-end: the Synthesize node only emits claims with a `source_url`,
and the app's normalizer **drops any talking-point or outside-work item without a source**
before rendering — so the UI never shows an unsourced claim.

### Output schema (`answer`)
```json
{
"identity": { "name": "", "role": null, "company": null, "location": null, "sources": [] },
"summary": "",
"outside_work": [{ "note": "", "source_url": "" }],
"talking_points": [{ "point": "", "why_it_works": "", "source_url": "" }],
"couldnt_confirm": [],
"sources": [],
"confidence": "high | medium | low"
}
```
> The app accepts either `snake_case` or `camelCase` field spellings from the flow and
> canonicalizes to the shape above.

## Run locally
```bash
cd kits/know-thy-person/apps
cp .env.example .env.local # fill in real values from Lamatic Studio
npm install --legacy-peer-deps
npm run dev # http://localhost:3000
```
Run the tests with `npm run test` and a production build with `npm run build`.

> `--legacy-peer-deps` is needed because one transitive UI dependency (`vaul`) still
> declares a React 18 peer range while this app is on React 19.

### Environment
| Var | Where |
|---|---|
| `KNOW_THY_PERSON` | deployed flow ID (Studio → Flow → Details) |
| `LAMATIC_API_URL` / `LAMATIC_PROJECT_ID` / `LAMATIC_API_KEY` | Studio → Settings → API Keys |

The **OpenRouter key** (for `gemini-2.5-flash`) and the **Serper** / **Firecrawl** keys are
configured in **Lamatic Studio**, not in this app.

## Honest limitations
- **LinkedIn and X are not scraped directly** (anti-bot walls). We rely on live web search
+ crawling of open pages, which often surfaces those facts indirectly.
- **Needs a crawlable anchor: a company email _or_ a website link.** A company email resolves
the domain to crawl; for a generic provider (gmail, outlook, …) supply the person's
**company or personal site** as `person_context` and that gets crawled instead. A generic
email with **no link** has nothing to crawl and is not supported.
- **Data-broker / people-search sites are blacklisted** as sources.
- Results are only as good as a person's public footprint. For **low-footprint people** the
agent correctly returns little and says so — that's the point, not a bug.
- Public information only. This is meeting prep, not surveillance.
62 changes: 62 additions & 0 deletions kits/know-thy-person/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Know Thy Person — Agent

## Overview
A meeting-prep research agent. Given a person's email and name (and, optionally, a
link to their LinkedIn / X / company or personal site), it produces a fully-sourced
dossier to help you build genuine rapport before a scheduled meeting.

## Purpose
Before a meeting you want to know the *person*, not just their title — enough to open
warmly and human. Doing this by hand doesn't scale, and the worst outcome is a *wrong*
fact stated with confidence. This agent leads with the non-work / rapport angle and is
built to **never fabricate**: every rendered claim carries a source URL, and anything it
can't verify is surfaced as "couldn't confirm" instead of invented.

## Flow: `know-thy-person`
- **Trigger (API Request):** `email` (required), `name` (required), `person_context`
(optional — their company or personal website URL; a LinkedIn/X link isn't crawlable but
still helps pin the right person).
- **Processing:**
1. **Resolve** (code, no LLM) — deterministically parses company/domain from the email
(no model call). Generic providers (gmail, outlook, …) resolve to no company, by design.
2. **Serper** — live web search over the person's public presence.
3. **Firecrawl** — crawls the most relevant public pages (personal/company sites, press,
talks, profiles) for source-attributable content.
4. **Synthesize** (`gemini-2.5-flash`) — converts the sourced material into a strict
dossier JSON where every identity / outside-work / talking-point item carries a
`source_url`.
- **Response (API Response):** `answer` — the dossier object (see README for the schema).
- **When to use:** ahead of a 1:1, sales call, interview, or intro where rapport matters.
- **Output:** `{ result: { answer: <dossier> } }`.

## Guardrails
- Cites or stays silent — never fabricates facts about a real person.
- Public information only. This is meeting prep, not surveillance.
- LinkedIn and X are **not** scraped directly (anti-bot walls); facts about them are used
only when they surface in public search/crawl results.
- Data-broker / people-search aggregator sites are blacklisted as sources.
- Low-footprint people correctly return sparse, honest results.

## Integration Reference
- **Serper** — web search (configured in Lamatic Studio).
- **Firecrawl** — page crawling (configured in Lamatic Studio).
- **OpenRouter** — serves `gemini-2.5-flash` for the Synthesize node (the only LLM in the
flow; Resolve is deterministic code). Configured in Lamatic Studio, not in the app.

## Environment Setup
| Var | Source / purpose |
|---|---|
| `KNOW_THY_PERSON` | Deployed flow ID (Studio → Flow → Details). |
| `LAMATIC_API_URL` / `LAMATIC_PROJECT_ID` / `LAMATIC_API_KEY` | Studio → Settings → API Keys. |

## Quickstart
1. `cd kits/know-thy-person/apps`
2. `cp .env.example .env.local` and fill in the four values above.
3. `npm install --legacy-peer-deps`
4. `npm run dev` → open http://localhost:3000

## Inputs
- `email` (required), `name` (required), `person_context` (optional URL).

## Output
- `answer`: a dossier object (see README for the schema).
4 changes: 4 additions & 0 deletions kits/know-thy-person/apps/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
KNOW_THY_PERSON="KNOW_THY_PERSON Flow ID"
LAMATIC_API_URL="LAMATIC_API_URL"
LAMATIC_PROJECT_ID="LAMATIC_PROJECT_ID"
LAMATIC_API_KEY="LAMATIC_API_KEY"
30 changes: 30 additions & 0 deletions kits/know-thy-person/apps/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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
Comment on lines +19 to +20

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.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Mission: keep runtime credentials out of Git.

.env does not ignore .env.local, where this app instructs users to place Lamatic credentials. Ignore .env.local and .env.*.local while retaining .env.example.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/know-thy-person/apps/.gitignore` around lines 19 - 20, Update the
env-file rules in the .gitignore section to ignore .env.local and .env.*.local,
while explicitly retaining .env.example as a tracked example configuration.


# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
# lockfiles (kept local; not committed)
pnpm-lock.yaml
yarn.lock
32 changes: 32 additions & 0 deletions kits/know-thy-person/apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Know Thy Person — app

The runnable Next.js app for the **Know Thy Person** kit. Enter an email + name (and,
optionally, a LinkedIn / X / company or personal site link) and get a fully-sourced
meeting-prep dossier.

See the kit README — [`../README.md`](../README.md) — for what it does, the flow, the output
schema, and honest limitations.

## Run locally

```bash
cp .env.example .env.local # fill in real values from Lamatic Studio
npm install --legacy-peer-deps
npm run dev # http://localhost:3000
```

- `npm run test` — run the dossier-normalizer unit tests (Vitest).
- `npm run build` — production build.

### Environment

| Var | Where |
| --- | --- |
| `KNOW_THY_PERSON` | deployed flow ID (Studio → Flow → Details) |
| `LAMATIC_API_URL` / `LAMATIC_PROJECT_ID` / `LAMATIC_API_KEY` | Studio → Settings → API Keys |

The OpenRouter, Serper, and Firecrawl keys are configured in **Lamatic Studio**, not here.

## License

MIT License — see [LICENSE](../../../LICENSE).
47 changes: 47 additions & 0 deletions kits/know-thy-person/apps/actions/orchestrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use server";

import { getLamaticClient } from "@/lib/lamatic-client";
import { normalizeDossier, type Dossier } from "@/lib/dossier";

export interface ResearchInput {
email: string;
name: string;
personContext?: string;
}

export async function researchPerson(
input: ResearchInput
): Promise<{ success: boolean; data?: Dossier; error?: string }> {
try {
const workflowId = process.env.KNOW_THY_PERSON;
if (!workflowId) throw new Error("Workflow ID not found in config.");
Comment on lines +16 to +17

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mission: route workflow selection through the kit config.

Import and use ../../lamatic.config to resolve the know-thy-person step’s envKey instead of duplicating KNOW_THY_PERSON here. This keeps the server action aligned with the kit contract.

Proposed fix
+import kitConfig from "../../lamatic.config";
+
-    const workflowId = process.env.KNOW_THY_PERSON;
+    const step = kitConfig.steps.find((item) => item.id === "know-thy-person");
+    const workflowId = step ? process.env[step.envKey] : undefined;

As per coding guidelines, Kit Next.js apps must import and use ../../lamatic.config to read step definitions from the parent kit.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const workflowId = process.env.KNOW_THY_PERSON;
if (!workflowId) throw new Error("Workflow ID not found in config.");
import kitConfig from "../../lamatic.config";
const step = kitConfig.steps.find((item) => item.id === "know-thy-person");
const workflowId = step ? process.env[step.envKey] : undefined;
if (!workflowId) throw new Error("Workflow ID not found in config.");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/know-thy-person/apps/actions/orchestrate.ts` around lines 16 - 17,
Update the workflow selection in the action’s initialization flow to import the
parent kit configuration from ../../lamatic.config and resolve the
know-thy-person step’s envKey from its step definition instead of hardcoding
KNOW_THY_PERSON. Preserve the existing missing-workflow validation after
retrieving the configured value.

Source: Coding guidelines


const inputs = {
email: input.email,
name: input.name,
person_context: input.personContext ?? "",
};

const lamaticClient = getLamaticClient();
const resData = await lamaticClient.executeFlow(workflowId, inputs);
// The flow maps the dossier fields flat onto `result` (identity, summary,
// talkingPoints, ...). Some flows instead wrap it as `result.answer`; support both.
const result = resData?.result as Record<string, unknown> | undefined;
const dossier = result && "answer" in result && result.answer ? result.answer : result;
if (!dossier || typeof dossier !== "object") {
throw new Error("No dossier returned from the flow.");
}

return { success: true, data: normalizeDossier(dossier) };
} catch (error) {
let message = "Unknown error occurred";
if (error instanceof Error) {
message = error.message;
if (message.includes("fetch failed"))
message = "Network error: could not reach the research service.";
else if (message.toLowerCase().includes("api key"))
message = "Authentication error: check your API configuration.";
}
return { success: false, error: message };
}
}
Loading
Loading