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
266 changes: 266 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@
"drizzle-orm": "^0.45.1",
"eta": "^4.5.0",
"hono": "^4.6.14",
"imapflow": "^1.2.10",
"jira.js": "^5.3.0",
"js-yaml": "^4.1.1",
"llmist": "^15.19.0",
"marklassian": "^1.1.0",
"nodemailer": "^8.0.1",
"pg": "^8.18.0",
"trello.js": "^1.2.8",
"zangief": "latest",
Expand All @@ -90,6 +92,7 @@
"@types/dockerode": "^3.3.47",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.10.2",
"@types/nodemailer": "^7.0.11",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
Expand Down
5 changes: 4 additions & 1 deletion src/agents/definitions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ const CapabilitiesSchema = z.object({
canCreatePR: z.boolean(),
canUpdateChecklists: z.boolean(),
isReadOnly: z.boolean(),
canAccessEmail: z.boolean().optional(),
});

const ToolsSchema = z.object({
/** Named tool set references resolved via TOOL_SET_REGISTRY */
sets: z.array(z.enum(['pm', 'pm_checklist', 'session', 'github_review', 'github_ci', 'all'])),
sets: z.array(
z.enum(['pm', 'pm_checklist', 'session', 'github_review', 'github_ci', 'email', 'all']),
),
/** SDK tools preset: "all" or "readOnly" */
sdkTools: z.enum(['all', 'readOnly']),
});
Expand Down
4 changes: 4 additions & 0 deletions src/agents/definitions/strategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export const GITHUB_CI_TOOLS = [
'UpdatePRComment',
];

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

export const SESSION_TOOL = 'Finish';

export const ALL_SDK_TOOLS = ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'];
Expand All @@ -69,6 +72,7 @@ export const TOOL_SET_REGISTRY: Record<string, string[]> = {
session: [SESSION_TOOL],
github_review: GITHUB_REVIEW_TOOLS,
github_ci: GITHUB_CI_TOOLS,
email: EMAIL_TOOLS,
// 'all' is a sentinel — handled by returning allTools unfiltered
};

Expand Down
2 changes: 2 additions & 0 deletions src/agents/shared/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface AgentCapabilities {
canUpdateChecklists: boolean;
/** True for agents that only interact with the PM system (no repo changes) */
isReadOnly: boolean;
/** Can the agent send/search/read emails? (default: false) */
canAccessEmail?: boolean;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/agents/shared/gadgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RipGrep } from '../../gadgets/RipGrep.js';
import { Sleep } from '../../gadgets/Sleep.js';
import { VerifyChanges } from '../../gadgets/VerifyChanges.js';
import { WriteFile } from '../../gadgets/WriteFile.js';
import { ReadEmail, ReplyToEmail, SearchEmails, SendEmail } from '../../gadgets/email/index.js';
import {
CreatePR,
CreatePRReview,
Expand Down Expand Up @@ -75,6 +76,10 @@ export function buildWorkItemGadgets(caps: AgentCapabilities): CreateBuilderOpti
// UpdateChecklistItem gated by capability — prevents planning from marking items complete
// prematurely, while respond-to-planning-comment CAN update them
...(caps.canUpdateChecklists ? [new PMUpdateChecklistItem(), new PMDeleteChecklistItem()] : []),
// Email gadgets (gated by capability — disabled by default)
...(caps.canAccessEmail
? [new SendEmail(), new SearchEmails(), new ReadEmail(), new ReplyToEmail()]
: []),
// Session control
new Finish(),
];
Expand Down
13 changes: 11 additions & 2 deletions src/config/integrationRoles.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export type IntegrationCategory = 'pm' | 'scm';
export type IntegrationProvider = 'trello' | 'jira' | 'github';
export type IntegrationCategory = 'pm' | 'scm' | 'email';
export type IntegrationProvider = 'trello' | 'jira' | 'github' | 'imap';

export const PROVIDER_CATEGORY: Record<IntegrationProvider, IntegrationCategory> = {
trello: 'pm',
jira: 'pm',
github: 'scm',
imap: 'email',
};

export interface CredentialRoleDef {
Expand All @@ -30,4 +31,12 @@ export const PROVIDER_CREDENTIAL_ROLES: Record<IntegrationProvider, CredentialRo
},
{ role: 'reviewer_token', label: 'Reviewer Token', envVarKey: 'GITHUB_TOKEN_REVIEWER' },
],
imap: [
{ role: 'imap_host', label: 'IMAP Host', envVarKey: 'EMAIL_IMAP_HOST' },
{ role: 'imap_port', label: 'IMAP Port', envVarKey: 'EMAIL_IMAP_PORT' },
{ role: 'smtp_host', label: 'SMTP Host', envVarKey: 'EMAIL_SMTP_HOST' },
{ role: 'smtp_port', label: 'SMTP Port', envVarKey: 'EMAIL_SMTP_PORT' },
{ role: 'username', label: 'Username/Email', envVarKey: 'EMAIL_USERNAME' },
{ role: 'password', label: 'Password/App Password', envVarKey: 'EMAIL_PASSWORD' },
],
};
41 changes: 41 additions & 0 deletions src/db/migrations/0017_email_integration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- 0017_email_integration.sql
-- Add email integration category with IMAP/SMTP provider support.

BEGIN;

-- ============================================================================
-- 1. Update category/provider CHECK constraint to include email/imap
-- ============================================================================

ALTER TABLE project_integrations
DROP CONSTRAINT IF EXISTS chk_integration_category_provider;

ALTER TABLE project_integrations
ADD CONSTRAINT chk_integration_category_provider
CHECK (
(category = 'pm' AND provider IN ('trello', 'jira'))
OR (category = 'scm' AND provider IN ('github'))
OR (category = 'email' AND provider IN ('imap'))
);

-- ============================================================================
-- 2. Update credential role CHECK constraint to include email roles
-- ============================================================================

ALTER TABLE integration_credentials
DROP CONSTRAINT IF EXISTS chk_integration_credential_role;

ALTER TABLE integration_credentials
ADD CONSTRAINT chk_integration_credential_role
CHECK (
role IN (
-- PM roles
'api_key', 'token', 'email', 'api_token',
-- SCM roles
'implementer_token', 'reviewer_token',
-- Email roles
'imap_host', 'imap_port', 'smtp_host', 'smtp_port', 'username', 'password'
)
);

COMMIT;
7 changes: 7 additions & 0 deletions src/db/migrations/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@
"when": 1751000000000,
"tag": "0016_add_task_prompt_column",
"breakpoints": false
},
{
"idx": 17,
"version": "7",
"when": 1752000000000,
"tag": "0017_email_integration",
"breakpoints": false
}
]
}
Loading