4 Commits

Author SHA1 Message Date
KS Jannette
25ac1ec68f revise tool calls 2026-08-31 09:21:06 -04:00
KS Jannette
e7feb61e6f add start tool 2026-08-31 09:11:43 -04:00
dac6f37331 Merge pull request 'minor prompt refinements; coserve tokens' (#7) from FEAT-tweak-actions into master
Reviewed-on: #7
2026-08-31 12:27:43 +00:00
KS Jannette
54c6f7e81b minor prompt refinements; coserve tokens 2026-08-31 08:26:48 -04:00
4 changed files with 72 additions and 18 deletions

View File

@@ -97,10 +97,10 @@ The server fetches unread emails, classifies them using your configured prompts,
| Category | Description | Default Action | | Category | Description | Default Action |
|----------|-------------|----------------| |----------|-------------|----------------|
| **A** | Acknowledgements, auto-replies | Delete | | **A** | Acknowledgements, auto-replies | Star and mark read |
| **B** | Advancement (interview requests, next steps) | Log + Calendar event | | **B** | Advancement (interview requests, next steps) | Log + Calendar event (never star) |
| **C** | Rejections | Delete | | **C** | Rejections | Star and mark read |
| **D** | Other/uncategorized | Log for review | | **D** | Other/uncategorized | Star, mark read, and log for review |
### Customizing Prompts ### Customizing Prompts
@@ -119,7 +119,7 @@ Changes take effect immediately without rebuilding.
|------|-------------| |------|-------------|
| `fetch_new_emails` | Fetch unread emails with classification instructions appended | | `fetch_new_emails` | Fetch unread emails with classification instructions appended |
| `delete_emails` | Move specified message IDs to trash | | `delete_emails` | Move specified message IDs to trash |
| `star_emails` | Star messages and mark as read | | `star_emails` | Star Category A/C/D messages and mark as read |
| `append_to_summary` | Log email metadata to local JSON (auto-purges after 30 days) | | `append_to_summary` | Log email metadata to local JSON (auto-purges after 30 days) |
| `log_recruiter_contact` | Append or update a row in Google Sheets | | `log_recruiter_contact` | Append or update a row in Google Sheets |
| `create_calendar_event` | Create a Google Calendar event (skips past dates) | | `create_calendar_event` | Create a Google Calendar event (skips past dates) |
@@ -130,6 +130,12 @@ Changes take effect immediately without rebuilding.
fetch_new_emails(account: "work", maxResults: 50) fetch_new_emails(account: "work", maxResults: 50)
``` ```
### Example: Star and Mark Read (Categories A, C, D only)
```
star_emails(account: "work", messageIds: ["msg-id-1", "msg-id-2"])
```
### Example: Create Calendar Event ### Example: Create Calendar Event
``` ```

View File

@@ -9,28 +9,27 @@ STEP 2: CLASSIFY each email into exactly one category.
Category A - Acknowledgement Only Category A - Acknowledgement Only
The employer confirms receipt of my application but requires no action from me. Typical language: "We received your application," "Your application is under review," "We will contact you if selected." No next steps are requested. The employer confirms receipt of my application but requires no action from me. Typical language: "We received your application," "Your application is under review," "We will contact you if selected." No next steps are requested.
Action you should take: Add a "Star" using the gmail built-in actions. Action you should take: Call star_emails with this message's ID (same account used for fetch_new_emails). That stars the email and marks it read.
Category B - Advancement to Next Step Category B - Advancement to Next Step
The employer wants to move forward. This includes: invitations to schedule a phone screen or interview, requests to complete an assessment or assignment, requests for additional information or documents, or any communication that requires a response or action from me. If an email both acknowledges receipt AND requests action, classify it as B. The employer wants to move forward. This includes: invitations to schedule a phone screen or interview, requests to complete an assessment or assignment, requests for additional information or documents, or any communication that requires a response or action from me. If an email both acknowledges receipt AND requests action, classify it as B.
Actions you should take: 1) Add a "Star" using the gmail built-in actions. 2) add a calendar event with all pertinent info: a) person I will be meeting with, Actions you should take: 1) add a calendar event with all pertinent info: a) person I will be meeting with,
b) company c) role I will be discussing d) method for joining - ie, telephone? If yes, will the representative all me, and is the number correct (718) 749-8292 c) or, is it Zoom, Google Meet, Webex or another video format, if so, include the link for joining b) company c) role I will be discussing d) method for joining meeting - ie, telephone? If yes, will the representative all me, and is the number correct (718) 749-8292 c) or, is it Zoom, Google Meet, Webex or another video format, if so, include the link for joining
NEVER ADD A STAR
NEVER, EVER ADD A STAR TO A CATEGORY B EMAIL. Do not pass Category B message IDs to star_emails.
Category C - Rejection Category C - Rejection
The employer declines to move forward. Typical language: "We have decided to pursue other candidates," "Unfortunately, you were not selected," "We will not be moving forward with your application." The employer declines to move forward. Typical language: "We have decided to pursue other candidates," "Unfortunately, you were not selected," "We will not be moving forward with your application."
Action you should take: Add a "Star" using the gmail built-in actions. Action you should take: Call star_emails with this message's ID (same account used for fetch_new_emails). That stars the email and marks it read.
Category D - Other Category D - Other
The email does not fit into categories A, B, or C. This includes non-job-application emails, newsletters, promotional content, or ambiguous messages that do not clearly match another category. The email does not fit into categories A, B, or C. This includes non-job-application emails, newsletters, promotional content, or ambiguous messages that do not clearly match another category.
Action you should take: Add a "Star" using the gmail built-in actions. Action you should take: Call star_emails with this message's ID (same account used for fetch_new_emails). That stars the email and marks it read.
STEP 3: SUMMARIZE emails classified as Category B and Category D in the chat in an organized list with headings. Example format for this: STEP 3: SUMMARIZE emails classified as Category B and Category D in the chat in an organized list with headings. Example format for this:
1. Sender name and email address 1. Sender name and email address
2. Email subject line 2. Email subject line
3. Date and time received 3. Date and time received
4. Category (B or D) 4. Category (B or D)
5. Suggested action I should take 5. Suggested action I might take

View File

@@ -55,8 +55,9 @@ server.registerTool(
{ {
description: description:
"Fetch unread emails from a Gmail inbox. Returns sender, date, " + "Fetch unread emails from a Gmail inbox. Returns sender, date, " +
"subject, message ID, and body text for each message. The message IDs " + "subject, message ID, and body text for each message. After evaluation, " +
"must be passed to star_emails after evaluation. Specify which account to fetch from.", "pass Category A, C, and D message IDs to star_emails. Never pass " +
"Category B IDs to star_emails. Specify which account to fetch from.",
inputSchema: { inputSchema: {
account: accountSchema, account: accountSchema,
maxResults: z maxResults: z
@@ -310,8 +311,9 @@ server.registerTool(
"star_emails", "star_emails",
{ {
description: "Mark reviewed emails with a star and mark them as read in Gmail. " + description: "Mark reviewed emails with a star and mark them as read in Gmail. " +
"Use this for all reviewed messages (Categories A, B, C, and D) " + "Use this for Category A (acknowledgements), C (rejections), and D (other) " +
"to signal they have been processed. Specify which account the emails belong to.", "messages to signal they have been processed. Never use this for Category B " +
"(advancement) emails. Specify which account the emails belong to.",
inputSchema: { inputSchema: {
account: accountSchema, account: accountSchema,
messageIds: z messageIds: z

View File

@@ -14,6 +14,7 @@ import { MOCK_MESSAGE_LIST, MOCK_MESSAGES, MOCK_EMPTY_LIST } from "../fixtures/m
const mockGmailList = vi.fn(); const mockGmailList = vi.fn();
const mockGmailGet = vi.fn(); const mockGmailGet = vi.fn();
const mockGmailTrash = vi.fn(); const mockGmailTrash = vi.fn();
const mockGmailBatchModify = vi.fn();
let tmpDir: string; let tmpDir: string;
vi.mock("../../src/loaders/prompt-config-loaders.js", () => { vi.mock("../../src/loaders/prompt-config-loaders.js", () => {
@@ -45,6 +46,7 @@ vi.mock("../../src/loaders/prompt-config-loaders.js", () => {
list: mockGmailList, list: mockGmailList,
get: mockGmailGet, get: mockGmailGet,
trash: mockGmailTrash, trash: mockGmailTrash,
batchModify: mockGmailBatchModify,
}, },
}, },
}), }),
@@ -207,6 +209,51 @@ describe("Phase 1: Email Review Workflow", () => {
}); });
}); });
// ----- Step 2b: Star A, C, and D emails -----
describe("star_emails", () => {
it("stars and marks read the specified message IDs", async () => {
mockGmailBatchModify.mockResolvedValue({});
const text = await callTool("star_emails", {
account: "work",
messageIds: ["msg-cat-a-001", "msg-cat-c-001", "msg-cat-d-001"],
});
expect(mockGmailBatchModify).toHaveBeenCalledTimes(1);
expect(mockGmailBatchModify).toHaveBeenCalledWith({
userId: "me",
requestBody: {
ids: ["msg-cat-a-001", "msg-cat-c-001", "msg-cat-d-001"],
addLabelIds: ["STARRED"],
removeLabelIds: ["UNREAD"],
},
});
expect(text).toContain("Successfully starred and marked read 3 email(s).");
});
it("returns early when no message IDs are provided", async () => {
const text = await callTool("star_emails", {
account: "work",
messageIds: [],
});
expect(mockGmailBatchModify).not.toHaveBeenCalled();
expect(text).toBe("No message IDs provided.");
});
it("handles Gmail API errors gracefully", async () => {
mockGmailBatchModify.mockRejectedValue(new Error("Quota exceeded"));
const text = await callTool("star_emails", {
account: "work",
messageIds: ["msg-cat-a-001"],
});
expect(text).toContain("Error starring emails");
expect(text).toContain("Quota exceeded");
});
});
// ----- Step 3: Summarize B and D ----- // ----- Step 3: Summarize B and D -----
describe("append_to_summary", () => { describe("append_to_summary", () => {
it("creates a summary file and appends entries", async () => { it("creates a summary file and appends entries", async () => {