UNPKG

@essentialai/cogent-bridge

Version:

Cogent Bridge — cross-agent comms for Claude Code, OpenAI Codex and Slack. Codex users: install with `curl -fsSL https://cogent.tools/install.sh | sh`, then start with `cogent-codex` for real-time peer wake.

59 lines • 3.63 kB
import { z } from "zod"; import { BridgeError, BridgeErrorCode, successResult, errorResult } from "../errors.js"; import { loadMailCredentials, MAIL_NOT_CONFIGURED_HINT } from "../cloud/mail-credential-store.js"; import { NodemailerMailSender, buildAndSendMail } from "../mail/mail-sender.js"; import { assertValidEmail, assertAllowedMailDomain } from "../mail/validate.js"; import { isMailAuthError, mailAuthErrorHint } from "../mail/auth-error.js"; /** * cogent_send_mail — send an email from this agent's mailbox (Cogent Mail M2.2). Reads the * agent's own creds from the local store, composes the message with optional attachments read * from local file paths, and submits over SMTP (as the agent — no admin). The heavy SMTP lib * loads lazily only when this runs. Thin glue over buildAndSendMail (unit-tested in mail-sender.test). * `deps.senderFactory` lets tests drive an in-memory sender instead of real SMTP. */ export function registerSendMailTool(server, deps = {}) { const makeSender = deps.senderFactory ?? ((creds) => new NodemailerMailSender(creds)); server.registerTool("cogent_send_mail", { title: "Send an email from this agent's mailbox", description: "Send an email (with optional file attachments) from this agent's Cogent mailbox to another " + "address. Requires cogent_setup_mail to have stored the mailbox credentials first.", inputSchema: { to: z.string().describe("Recipient email address, e.g. other-agent-backend@mail.cogent.tools"), subject: z.string().describe("Email subject"), body: z.string().describe("Plain-text email body"), attachments: z .array(z.string()) .optional() .describe("Optional local file paths to attach (each is read and attached by basename)"), }, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, }, async ({ to, subject, body, attachments }) => { try { assertValidEmail(to, "to"); // fail fast at the tool boundary, before the SMTP sink assertAllowedMailDomain(to, "to"); // F4: org-only mail — no sending to external domains const creds = await loadMailCredentials(); if (!creds) { return errorResult(new BridgeError(BridgeErrorCode.INVALID_INPUT, "No mailbox is configured for this agent", MAIL_NOT_CONFIGURED_HINT)); } const result = await buildAndSendMail(makeSender(creds), creds, { to: [to], subject, text: body, attachmentPaths: attachments, }); return successResult({ ...result, attachmentCount: attachments?.length ?? 0 }); } catch (err) { // Cogent Mail F3: on an SMTP AUTH failure, do NOT clear the local creds — a temporary admin // SUSPEND is wire-indistinguishable from a reap-delete, and wiping still-valid creds would // permanently lock the agent out after the box is resumed. A genuine reap already clears creds // via the deregister deprovision-header path; here we just surface an actionable hint. if (isMailAuthError(err)) { const detail = err instanceof Error ? err.message : String(err); return errorResult(new BridgeError(BridgeErrorCode.INVALID_INPUT, `Mailbox login was refused: ${detail}`, mailAuthErrorHint())); } return errorResult(err); } }); } //# sourceMappingURL=send-mail.js.map