fix note creation agent

This commit is contained in:
Ramnique Singh 2026-01-21 21:58:35 +05:30
parent e1b524fadd
commit cab82dceec
5 changed files with 326 additions and 321 deletions

View file

@ -24,6 +24,9 @@ import { IRunsRepo } from "../runs/repo.js";
import { IRunsLock } from "../runs/lock.js";
import { PrefixLogger } from "@x/shared";
import { parse } from "yaml";
import { raw as noteCreationMediumRaw } from "../knowledge/note_creation_medium.js";
import { raw as noteCreationLowRaw } from "../knowledge/note_creation_low.js";
import { raw as noteCreationHighRaw } from "../knowledge/note_creation_high.js";
export interface IAgentRuntime {
trigger(runId: string): Promise<void>;
@ -246,26 +249,20 @@ export async function loadAgent(id: string): Promise<z.infer<typeof Agent>> {
return CopilotAgent;
}
// Built-in agents loaded from checked-in files
const builtinAgents: Record<string, string> = {
'meeting-prep': '../pre_built/meeting-prep.md',
'email-draft': '../pre_built/email-draft.md',
};
// Resolve agent file path (note_creation is dynamic based on strictness config)
let agentFilePath: string | null = null;
const currentDir = path.dirname(new URL(import.meta.url).pathname);
if (id === 'note_creation') {
const strictness = getNoteCreationStrictness();
agentFilePath = path.join(currentDir, `../knowledge/note_creation_${strictness}.md`);
} else if (id in builtinAgents) {
agentFilePath = path.join(currentDir, builtinAgents[id]);
}
if (agentFilePath) {
const raw = fs.readFileSync(agentFilePath, "utf8");
let raw = '';
switch (strictness) {
case 'medium':
raw = noteCreationMediumRaw;
break;
case 'low':
raw = noteCreationLowRaw;
break;
case 'high':
raw = noteCreationHighRaw;
break;
}
let agent: z.infer<typeof Agent> = {
name: id,
instructions: raw,

View file

@ -1,3 +1,4 @@
export const raw = `
---
model: gpt-5.2
tools:
@ -66,22 +67,22 @@ When you need to:
- Find an organization Look up by name/domain in the index
- Resolve "David" to a full name Check index for people with that name/alias + organization context
**Only use `cat` to read full note content** when you need details not in the index (e.g., existing activity logs, open items).
**Only use \`cat\` to read full note content** when you need details not in the index (e.g., existing activity logs, open items).
# Tools Available
You have access to `executeCommand` to run shell commands:
```
You have access to \`executeCommand\` to run shell commands:
\`\`\`
executeCommand("ls {path}") # List directory contents
executeCommand("cat {path}") # Read file contents
executeCommand("head -50 {path}") # Read first 50 lines
executeCommand("write {path} {content}") # Create or overwrite file
```
\`\`\`
**Important:** Use shell escaping for paths with spaces:
```
\`\`\`
executeCommand("cat 'knowledge_folder/People/Sarah Chen.md'")
```
\`\`\`
**NOTE:** Do NOT use grep to search for entities. Use the provided knowledge_index instead.
@ -119,33 +120,33 @@ This mode prioritizes comprehensive capture over selectivity. The goal is to nev
# Step 0: Determine Source Type
Read the source file and determine if it's a meeting or email.
```
\`\`\`
executeCommand("cat '{source_file}'")
```
\`\`\`
**Meeting indicators:**
- Has `Attendees:` field
- Has `Meeting:` title
- Has \`Attendees:\` field
- Has \`Meeting:\` title
- Transcript format with speaker labels
**Email indicators:**
- Has `From:` and `To:` fields
- Has `Subject:` field
- Has \`From:\` and \`To:\` fields
- Has \`Subject:\` field
- Email signature
**Set processing mode:**
- `source_type = "meeting"` Create notes for all external attendees
- `source_type = "email"` Create notes for sender if identifiable human
- \`source_type = "meeting"\` → Create notes for all external attendees
- \`source_type = "email"\` → Create notes for sender if identifiable human
---
## Calendar Invite Emails
Emails containing calendar invites (`.ics` attachments) are **high signal** - a scheduled meeting means this person matters.
Emails containing calendar invites (\`.ics\` attachments) are **high signal** - a scheduled meeting means this person matters.
**How to identify:**
- Subject contains "Invitation:", "Accepted:", "Declined:", or "Updated:"
- Has `.ics` attachment reference
- Has \`.ics\` attachment reference
**Rules:**
1. **CREATE a note for the primary contact** - the person you're meeting with
@ -193,19 +194,19 @@ Once a note exists, subsequent emails will enrich it. When the meeting happens,
**Important:** When in doubt, PROCESS. In low strictness mode, we err on the side of capturing more.
If skipping:
```
\`\`\`
SKIP
Reason: {reason}
```
\`\`\`
If processing, continue to Step 2.
---
# Step 2: Read and Parse Source File
```
\`\`\`
executeCommand("cat '{source_file}'")
```
\`\`\`
Extract metadata:
@ -216,8 +217,8 @@ Extract metadata:
- **Duration:** If available
**For emails:**
- **Date:** From `Date:` header
- **Subject:** From `Subject:` header
- **Date:** From \`Date:\` header
- **Subject:** From \`Subject:\` header
- **From:** Sender email/name
- **To/Cc:** Recipients
@ -263,37 +264,37 @@ Create a list of all variants found.
For each person variant (name, email, alias), check the index:
```
\`\`\`
From index, find matches for:
- "Sarah Chen" Check People table for matching name
- "Sarah" Check People table for matching name or alias
- "sarah@acme.com" Check People table for matching email
- "@acme.com" Check People table for matching organization or check Organizations for domain
```
\`\`\`
## 3b: Look Up Organizations
```
\`\`\`
From index, find matches for:
- "Acme Corp" Check Organizations table for matching name
- "Acme" Check Organizations table for matching name or alias
- "acme.com" Check Organizations table for matching domain
```
\`\`\`
## 3c: Look Up Projects and Topics
```
\`\`\`
From index, find matches for:
- "the pilot" Check Projects table for related names
- "SOC 2" Check Topics table for matching keywords
```
\`\`\`
## 3d: Read Full Notes When Needed
Only read the full note content when you need details not in the index (e.g., activity logs, open items):
```bash
\`\`\`bash
executeCommand("cat '{knowledge_folder}/People/Sarah Chen.md'")
```
\`\`\`
**Why read these notes:**
- Find canonical names (David David Kim)
@ -368,7 +369,7 @@ When multiple candidates match a variant, disambiguate by:
## 4d: Resolution Map Output
Final resolution map before proceeding:
```
\`\`\`
RESOLVED (use canonical name with absolute path):
- "Sarah", "Sarah Chen", "sarah@acme.com" [[People/Sarah Chen]]
@ -377,7 +378,7 @@ NEW ENTITIES (create notes):
AMBIGUOUS (create with disambiguation note):
- "Mike" (no context) Create [[People/Mike]] with note about ambiguity
```
\`\`\`
---
@ -508,9 +509,9 @@ Write 2-3 sentences covering their role/function, context of the relationship, a
## Activity Summary
One line summarizing this source's relevance to the entity:
```
\`\`\`
**{YYYY-MM-DD}** ({meeting|email}): {Summary with [[links]]}
```
\`\`\`
---
@ -554,9 +555,9 @@ Before writing:
## 9a: Create and Update Notes
**For new entities:**
```bash
\`\`\`bash
executeCommand("write '{knowledge_folder}/People/Jennifer.md' '{content}'")
```
\`\`\`
**For existing entities:**
- Read current content first
@ -575,7 +576,7 @@ Add newly discovered name variants to Aliases field.
## 9d: Writing Rules
- **Always use absolute paths** with format `[[Folder/Name]]` for all links
- **Always use absolute paths** with format \`[[Folder/Name]]\` for all links
- Use YYYY-MM-DD format for dates
- Be concise: one line per activity entry
- Escape quotes properly in shell commands
@ -589,12 +590,12 @@ After writing, verify links go both ways.
## Absolute Link Format
**IMPORTANT:** Always use absolute links:
```markdown
\`\`\`markdown
[[People/Sarah Chen]]
[[Organizations/Acme Corp]]
[[Projects/Acme Integration]]
[[Topics/Security Compliance]]
```
\`\`\`
## Bidirectional Link Rules
@ -611,7 +612,7 @@ After writing, verify links go both ways.
# Note Templates
## People
```markdown
\`\`\`markdown
# {Full Name}
## Info
@ -638,10 +639,10 @@ After writing, verify links go both ways.
## Open items
{Commitments and next steps only. Leave empty if none.}
```
\`\`\`
## Organizations
```markdown
\`\`\`markdown
# {Organization Name}
## Info
@ -673,10 +674,10 @@ After writing, verify links go both ways.
## Open items
{Commitments and next steps only. Leave empty if none.}
```
\`\`\`
## Projects
```markdown
\`\`\`markdown
# {Project Name}
## Info
@ -709,10 +710,10 @@ After writing, verify links go both ways.
## Key facts
{Substantive facts only.}
```
\`\`\`
## Topics
```markdown
\`\`\`markdown
# {Topic Name}
## About
@ -740,7 +741,7 @@ After writing, verify links go both ways.
## Key facts
{Substantive facts only.}
```
\`\`\`
---
@ -780,7 +781,7 @@ Before completing, verify:
- [ ] Extracted all name variants
- [ ] Searched existing notes
- [ ] Built resolution map
- [ ] Used absolute paths `[[Folder/Name]]`
- [ ] Used absolute paths \`[[Folder/Name]]\`
**Filtering:**
- [ ] Excluded only self and @user.domain
@ -798,7 +799,8 @@ Before completing, verify:
- [ ] Logged changes in activity
**Structure:**
- [ ] All links use `[[Folder/Name]]` format
- [ ] All links use \`[[Folder/Name]]\` format
- [ ] Activity entries reverse chronological
- [ ] Dates are YYYY-MM-DD
- [ ] Bidirectional links consistent
`;

View file

@ -1,4 +1,4 @@
---
export const raw = `---
model: gpt-5.2
tools:
workspace-writeFile:
@ -66,22 +66,22 @@ When you need to:
- Find an organization Look up by name/domain in the index
- Resolve "David" to a full name Check index for people with that name/alias + organization context
**Only use `cat` to read full note content** when you need details not in the index (e.g., existing activity logs, open items).
**Only use \`cat\` to read full note content** when you need details not in the index (e.g., existing activity logs, open items).
# Tools Available
You have access to `executeCommand` to run shell commands:
```
You have access to \`executeCommand\` to run shell commands:
\`\`\`
executeCommand("ls {path}") # List directory contents
executeCommand("cat {path}") # Read file contents
executeCommand("head -50 {path}") # Read first 50 lines
executeCommand("write {path} {content}") # Create or overwrite file
```
\`\`\`
**Important:** Use shell escaping for paths with spaces:
```
\`\`\`
executeCommand("cat 'knowledge_folder/People/Sarah Chen.md'")
```
\`\`\`
**NOTE:** Do NOT use grep to search for entities. Use the provided knowledge_index instead.
@ -118,33 +118,33 @@ Either:
# Step 0: Determine Source Type
Read the source file and determine if it's a meeting or email.
```
\`\`\`
executeCommand("cat '{source_file}'")
```
\`\`\`
**Meeting indicators:**
- Has `Attendees:` field
- Has `Meeting:` title
- Has \`Attendees:\` field
- Has \`Meeting:\` title
- Transcript format with speaker labels
**Email indicators:**
- Has `From:` and `To:` fields
- Has `Subject:` field
- Has \`From:\` and \`To:\` fields
- Has \`Subject:\` field
- Email signature
**Set processing mode:**
- `source_type = "meeting"` Can create new notes
- `source_type = "email"` Can create notes if personalized and relevant
- \`source_type = "meeting"\` → Can create new notes
- \`source_type = "email"\` → Can create notes if personalized and relevant
---
## Calendar Invite Emails
Emails containing calendar invites (`.ics` attachments or inline calendar data) are **high signal** - a scheduled meeting means this person matters.
Emails containing calendar invites (\`.ics\` attachments or inline calendar data) are **high signal** - a scheduled meeting means this person matters.
**How to identify:**
- Subject contains "Invitation:", "Accepted:", "Declined:", or "Updated:"
- Has `.ics` attachment reference
- Has \`.ics\` attachment reference
- Contains calendar metadata (VCALENDAR, VEVENT)
**Rules for calendar invite emails:**
@ -333,19 +333,19 @@ For emails, evaluate if the content is personalized and business-relevant:
## Filter Decision Output
If skipping:
```
\`\`\`
SKIP
Reason: {reason}
```
\`\`\`
If processing, continue to Step 2.
---
# Step 2: Read and Parse Source File
```
\`\`\`
executeCommand("cat '{source_file}'")
```
\`\`\`
Extract metadata:
@ -356,8 +356,8 @@ Extract metadata:
- **Duration:** If available
**For emails:**
- **Date:** From `Date:` header
- **Subject:** From `Subject:` header
- **Date:** From \`Date:\` header
- **Subject:** From \`Subject:\` header
- **From:** Sender email/name
- **To/Cc:** Recipients
@ -395,12 +395,12 @@ From the source, collect every way entities are referenced:
- Combined references: "Acme integration", "the Series A"
Create a list of all variants found:
```
\`\`\`
Variants found:
- People: "Sarah Chen", "Sarah", "sarah@acme.com", "David", "their CTO"
- Organizations: "Acme Corp", "Acme", "@acme.com"
- Projects: "the pilot", "Q2 integration"
```
\`\`\`
---
@ -412,37 +412,37 @@ Variants found:
For each person variant (name, email, alias), check the index:
```
\`\`\`
From index, find matches for:
- "Sarah Chen" Check People table for matching name
- "Sarah" Check People table for matching name or alias
- "sarah@acme.com" Check People table for matching email
- "@acme.com" Check People table for matching organization or check Organizations for domain
```
\`\`\`
## 3b: Look Up Organizations
```
\`\`\`
From index, find matches for:
- "Acme Corp" Check Organizations table for matching name
- "Acme" Check Organizations table for matching name or alias
- "acme.com" Check Organizations table for matching domain
```
\`\`\`
## 3c: Look Up Projects and Topics
```
\`\`\`
From index, find matches for:
- "the pilot" Check Projects table for related names
- "SOC 2" Check Topics table for matching keywords
```
\`\`\`
## 3d: Read Full Notes When Needed
Only read the full note content when you need details not in the index (e.g., activity logs, open items):
```bash
\`\`\`bash
executeCommand("cat '{knowledge_folder}/People/Sarah Chen.md'")
```
\`\`\`
**Why read these notes:**
- Find canonical names (David David Kim)
@ -495,7 +495,7 @@ Using the search results from Step 3, resolve each variant to a canonical name.
## 4a: Build Resolution Map
Create a mapping from every source reference to its canonical form:
```
\`\`\`
Resolution Map:
- "Sarah Chen" "Sarah Chen" (exact match found)
- "Sarah" "Sarah Chen" (matched via Acme context)
@ -507,7 +507,7 @@ Resolution Map:
- "@acme.com" "Acme Corp" (domain match)
- "the pilot" "Acme Integration" (project with Acme)
- "the integration" "Acme Integration" (same project)
```
\`\`\`
## 4b: Apply Source Type Rules (Medium Strictness)
@ -525,7 +525,7 @@ Resolution Map:
When multiple candidates match a variant, disambiguate:
**By organization (strongest signal):**
```bash
\`\`\`bash
# "David" could be David Kim or David Chen
executeCommand("grep -i 'Acme' '{knowledge_folder}/People/David Kim.md'")
# Output: **Organization:** [[Acme Corp]]
@ -534,20 +534,20 @@ executeCommand("grep -i 'Acme' '{knowledge_folder}/People/David Chen.md'")
# Output: **Organization:** [[Other Corp]]
# Source is from Acme context "David" = "David Kim"
```
\`\`\`
**By email (definitive):**
```bash
\`\`\`bash
executeCommand("grep -i 'david@acme.com' '{knowledge_folder}/People/David Kim.md'")
# Exact email match is definitive
```
\`\`\`
**By role:**
```bash
\`\`\`bash
# Source mentions "their CTO"
executeCommand("grep -r -i 'Role.*CTO' '{knowledge_folder}/People/'")
# Filter results by organization context
```
\`\`\`
**By recency (weakest signal):**
If still ambiguous, prefer the person with more recent activity in notes.
@ -559,7 +559,7 @@ If still ambiguous, prefer the person with more recent activity in notes.
## 4d: Resolution Map Output
Final resolution map before proceeding:
```
\`\`\`
RESOLVED (use canonical name with absolute path):
- "Sarah", "Sarah Chen", "sarah@acme.com" [[People/Sarah Chen]]
- "David" [[People/David Kim]]
@ -575,7 +575,7 @@ AMBIGUOUS (flag or skip):
SKIP (doesn't warrant note):
- "their assistant" Transactional contact
```
\`\`\`
---
@ -642,11 +642,11 @@ If role is not explicitly stated, infer from context:
- "I can make that call" decision maker
**Format in note:**
```markdown
\`\`\`markdown
**Role:** Product Lead (inferred from evaluation discussions)
**Role:** Senior (inferred organized cross-company meeting)
**Role:** Engineering (inferred asked technical integration questions)
```
\`\`\`
**Never write just "Unknown" if you can make a reasonable inference.**
@ -670,11 +670,11 @@ If role is not explicitly stated, infer from context:
### Handling Non-Note-Worthy People
For people who don't warrant their own note, add to Organization note's Contacts section:
```markdown
\`\`\`markdown
## Contacts
- James Wong Relationship Manager, helped with account setup
- Sarah Lee Support, handled wire transfer issue
```
\`\`\`
## Organizations
@ -758,9 +758,9 @@ Open items are **commitments and next steps from the conversation** — not task
- Follow-ups agreed: "Will loop in their CTO"
**Format:**
```markdown
\`\`\`markdown
- [ ] {Action} {owner if not you}, {due date if known}
```
\`\`\`
**Never include:**
- Data gaps: "Find their full name", "Get their email", "Add role"
@ -783,18 +783,18 @@ The summary should answer: **"Who is this person and why do I know them?"**
## Activity Summary
One line summarizing this source's relevance to the entity:
```
\`\`\`
**{YYYY-MM-DD}** ({meeting|email}): {Summary with [[links]]}
```
\`\`\`
**Important:** Use canonical names with absolute paths from resolution map in all summaries:
```
\`\`\`
# Correct (uses absolute paths):
**2025-01-15** (meeting): [[People/Sarah Chen]] confirmed timeline with [[People/David Kim]]. Blocked on [[Topics/Security Compliance]].
# Incorrect (uses variants or relative links):
**2025-01-15** (meeting): Sarah confirmed timeline with David. Blocked on SOC 2.
```
\`\`\`
---
@ -814,7 +814,7 @@ Review the extracted content for signals that existing note fields should be upd
| "Launched" / "completed" / "done" / "shipped" | completed |
| "Exploring" / "considering" / "evaluating" / "might" | planning |
**Action:** If a related project note exists and the signal is clear, update the `**Status:**` field.
**Action:** If a related project note exists and the signal is clear, update the \`**Status:**\` field.
**Be conservative:** Only update status when the signal is unambiguous. If unclear, add to activity log but don't change status.
@ -833,7 +833,7 @@ Review the extracted content for signals that existing note fields should be upd
**How to match:**
1. Read existing open items from the note
2. Look for items that match what was delivered/completed
3. Change `- [ ]` to `- [x]` with completion date
3. Change \`- [ ]\` to \`- [x]\` with completion date
**Be conservative:** Only mark complete if there's a clear match. If unsure, add to activity log but don't mark complete.
@ -846,7 +846,7 @@ Review the extracted content for signals that existing note fields should be upd
- "I've moved to the [X] team"
- Different role mentioned than what's in the note
**Action:** Update the `**Role:**` field in person note.
**Action:** Update the \`**Role:**\` field in person note.
## 7d: Organization/Relationship Changes
@ -862,13 +862,13 @@ Review the extracted content for signals that existing note fields should be upd
## 7e: Build State Change List
Before writing, compile all detected state changes:
```
\`\`\`
STATE CHANGES:
- [[Projects/Acme Integration]]: Status planning active (leadership approved)
- [[People/Sarah Chen]]: Role "Engineering Lead" "VP Engineering" (signature)
- [[People/Sarah Chen]]: Open item "Send API documentation" completed
- [[Organizations/Acme Corp]]: Relationship prospect customer (contract signed)
```
\`\`\`
---
@ -877,9 +877,9 @@ STATE CHANGES:
Before writing, compare extracted content against existing notes.
## Check Activity Log
```bash
\`\`\`bash
executeCommand("grep '2025-01-15' '{knowledge_folder}/People/Sarah Chen.md'")
```
\`\`\`
If an entry for this date/source already exists, this may have been processed. Skip or verify different interaction.
@ -907,9 +907,9 @@ If new info contradicts existing:
## 9a: Create and Update Notes
**For new entities (meetings and qualifying emails):**
```bash
\`\`\`bash
executeCommand("write '{knowledge_folder}/People/Jennifer.md' '{content}'")
```
\`\`\`
**For existing entities:**
- Read current content first
@ -920,11 +920,11 @@ executeCommand("write '{knowledge_folder}/People/Jennifer.md' '{content}'")
- Add new decisions
- Add new relationships
- Update summary ONLY if significant new understanding
```bash
\`\`\`bash
executeCommand("cat '{knowledge_folder}/People/Sarah Chen.md'")
# ... modify content ...
executeCommand("write '{knowledge_folder}/People/Sarah Chen.md' '{full_updated_content}'")
```
\`\`\`
## 9b: Apply State Changes
@ -936,10 +936,10 @@ If you discovered new name variants during resolution, add them to Aliases field
## 9d: Writing Rules
- **Always use absolute paths** with format `[[Folder/Name]]` for all links
- **Always use absolute paths** with format \`[[Folder/Name]]\` for all links
- Use YYYY-MM-DD format for dates
- Be concise: one line per activity entry
- Note state changes with `[Field → value]` in activity
- Note state changes with \`[Field → value]\` in activity
- Escape quotes properly in shell commands
---
@ -951,12 +951,12 @@ After writing, verify links go both ways.
## Absolute Link Format
**IMPORTANT:** Always use absolute links with the folder path:
```markdown
\`\`\`markdown
[[People/Sarah Chen]]
[[Organizations/Acme Corp]]
[[Projects/Acme Integration]]
[[Topics/Security Compliance]]
```
\`\`\`
## Bidirectional Link Rules
@ -973,7 +973,7 @@ After writing, verify links go both ways.
# Note Templates
## People
```markdown
\`\`\`markdown
# {Full Name}
## Info
@ -1000,10 +1000,10 @@ After writing, verify links go both ways.
## Open items
{Commitments and next steps only. Leave empty if none.}
```
\`\`\`
## Organizations
```markdown
\`\`\`markdown
# {Organization Name}
## Info
@ -1035,10 +1035,10 @@ After writing, verify links go both ways.
## Open items
{Commitments and next steps only. Leave empty if none.}
```
\`\`\`
## Projects
```markdown
\`\`\`markdown
# {Project Name}
## Info
@ -1072,10 +1072,10 @@ After writing, verify links go both ways.
## Key facts
{Substantive facts only. Leave empty if none.}
```
\`\`\`
## Topics
```markdown
\`\`\`markdown
# {Topic Name}
## About
@ -1103,7 +1103,7 @@ After writing, verify links go both ways.
## Key facts
{Substantive facts only. Leave empty if none.}
```
\`\`\`
---
@ -1143,7 +1143,7 @@ Before completing, verify:
- [ ] Extracted all name variants from source
- [ ] Searched notes including Aliases fields
- [ ] Built resolution map before writing
- [ ] Used absolute paths `[[Folder/Name]]` in ALL links
- [ ] Used absolute paths \`[[Folder/Name]]\` in ALL links
**Filtering:**
- [ ] Excluded self (user.name, user.email, @user.domain)
@ -1166,9 +1166,10 @@ Before completing, verify:
- [ ] Logged all state changes in activity
**Structure:**
- [ ] All entity mentions use `[[Folder/Name]]` absolute links
- [ ] All entity mentions use \`[[Folder/Name]]\` absolute links
- [ ] Activity entries are reverse chronological
- [ ] No duplicate activity entries
- [ ] Dates are YYYY-MM-DD
- [ ] Bidirectional links are consistent
- [ ] New notes in correct folders
`;