Skip to content
Merged
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
176 changes: 176 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"credentials:generate-key": "node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"",
"credentials:encrypt": "node --env-file=.env --import tsx tools/migrate-credentials-encrypt.ts",
"credentials:decrypt": "node --env-file=.env --import tsx tools/migrate-credentials-decrypt.ts",
"credentials:rotate-key": "node --env-file=.env --import tsx tools/rotate-credential-key.ts"
"credentials:rotate-key": "node --env-file=.env --import tsx tools/rotate-credential-key.ts",
"tool:test-email": "npx tsx tools/test-email-integration.ts"
},
"keywords": [
"trello",
Expand Down Expand Up @@ -75,6 +76,7 @@
"llmist": "^15.19.0",
"marklassian": "^1.1.0",
"nodemailer": "^8.0.1",
"open": "^11.0.0",
"pg": "^8.18.0",
"trello.js": "^1.2.8",
"zangief": "latest",
Expand Down
33 changes: 33 additions & 0 deletions src/agents/definitions/email-joke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
identity:
emoji: "\U0001F602"
label: Email Joke Responder
roleHint: Reads emails from a specific sender and responds with jokes
initialMessage: "**\U0001F602 Checking for emails to respond to with jokes**"

capabilities:
canEditFiles: false
canCreatePR: false
canUpdateChecklists: false
isReadOnly: true
canAccessEmail: true

tools:
sets: [email, session]
sdkTools: readOnly

strategies:
contextPipeline: []
taskPromptBuilder: emailJoke
gadgetBuilder: emailJoke

backend:
enableStopHooks: false
needsGitHubToken: false

compaction: default

trailingMessage: {}

hint: >-
Search for emails, read them, and send funny responses. Mark processed emails
as seen to prevent re-processing.
11 changes: 9 additions & 2 deletions src/agents/definitions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,15 @@ const StrategiesSchema = z.object({
'prConversation',
]),
),
taskPromptBuilder: z.enum(['workItem', 'commentResponse', 'review', 'ci', 'prCommentResponse']),
gadgetBuilder: z.enum(['workItem', 'review', 'prAgent']),
taskPromptBuilder: z.enum([
'workItem',
'commentResponse',
'review',
'ci',
'prCommentResponse',
'emailJoke',
]),
gadgetBuilder: z.enum(['workItem', 'review', 'prAgent', 'emailJoke']),
gadgetBuilderOptions: GadgetBuilderOptionsSchema,
});

Expand Down
10 changes: 9 additions & 1 deletion src/agents/definitions/strategies.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ContextInjection } from '../../backends/types.js';
import type { AgentCapabilities } from '../shared/capabilities.js';
import {
buildEmailJokeGadgets,
buildPRAgentGadgets,
buildReviewGadgets,
buildWorkItemGadgets,
Expand Down Expand Up @@ -56,7 +57,13 @@ export const GITHUB_CI_TOOLS = [
];

/** Email tools for agents that need email access */
export const EMAIL_TOOLS = ['SendEmail', 'SearchEmails', 'ReadEmail', 'ReplyToEmail'];
export const EMAIL_TOOLS = [
'SendEmail',
'SearchEmails',
'ReadEmail',
'ReplyToEmail',
'MarkEmailAsSeen',
];

export const SESSION_TOOL = 'Finish';

Expand Down Expand Up @@ -122,4 +129,5 @@ export const GADGET_BUILDER_REGISTRY: Record<
workItem: (caps) => buildWorkItemGadgets(caps),
review: () => buildReviewGadgets(),
prAgent: (_caps, options) => buildPRAgentGadgets(options),
emailJoke: () => buildEmailJokeGadgets(),
};
2 changes: 2 additions & 0 deletions src/agents/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ export interface TaskPromptContext {
prBranch?: string;
commentBody?: string;
commentPath?: string;
// Email-joke agent fields
senderEmail?: string;
[key: string]: unknown;
}

Expand Down
21 changes: 21 additions & 0 deletions src/agents/prompts/task-templates/emailJoke.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Your Task

1. Use **SearchEmails** to find unread emails<% if (it.senderEmail) { %> from: **<%= it.senderEmail %>**<% } %>
- Search criteria: `{ unseen: true<% if (it.senderEmail) { %>, from: "<%= it.senderEmail %>"<% } %> }`
- Limit results to prevent overwhelming responses

2. For each email found:
- Use **ReadEmail** to read the full content
- Compose a friendly, funny response that relates to the email content
- Use **ReplyToEmail** to send your joke response
- Use **MarkEmailAsSeen** to mark the email as read

3. Call **Finish** when you've processed all emails (or if none were found)

<% if (it.senderEmail) { %>
## Sender Filter
Only respond to emails from: **<%= it.senderEmail %>**
<% } else { %>
## Note
No sender filter configured. Processing all unread emails.
<% } %>
36 changes: 36 additions & 0 deletions src/agents/prompts/templates/email-joke.eta
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
You are a friendly comedian bot who reads emails and responds with relevant, tasteful jokes.

## Your Mission

Search for unread emails from a specific sender, read their content, and respond with a friendly message that includes a relevant, funny joke.

## Guidelines

### Joke Quality
- Keep jokes clean, professional, and appropriate for all audiences
- Tailor the humor to the email content when possible
- Be warm and friendly in your responses
- If the email is serious or urgent, acknowledge it briefly before the joke
- Vary your joke styles: puns, one-liners, observational humor, etc.

### Response Format
- Start with a brief, friendly acknowledgment of their email
- Include your joke (clearly formatted)
- Sign off as "Your Friendly Joke Bot 😂"

### Email Processing
- After successfully replying to an email, ALWAYS mark it as seen using MarkEmailAsSeen
- This prevents re-processing the same email on subsequent runs
- Process one email at a time: read → reply → mark as seen → move to next

### Completion
- When you've processed all matching emails (or found none), call Finish
- If no emails are found, that's fine — just call Finish with a brief summary

## Available Tools

- **SearchEmails**: Find emails by sender, subject, date, etc.
- **ReadEmail**: Read the full content of an email by its UID
- **ReplyToEmail**: Send a reply to an email thread
- **MarkEmailAsSeen**: Mark an email as read (prevents re-processing)
- **Finish**: Complete the session
Loading