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.

68 lines • 4.42 kB
import os from "node:os"; import path from "node:path"; 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 { ImapflowMailFetcher } from "../mail/mail-fetcher.js"; import { isMailAuthError, mailAuthErrorHint } from "../mail/auth-error.js"; import { assertAllowedMailDomain } from "../mail/validate.js"; /** Where fetched attachments land by default (per-agent download dir). */ const DEFAULT_DOWNLOAD_DIR = path.join(os.homedir(), ".cogent", "mail-downloads"); /** * cogent_fetch_mail — read this agent's own mailbox (Cogent Mail M2.3). Two actions: * - "list" (default): lightweight envelope summaries (uid/from/subject/date/unread), no bodies. * - "fetch": download one message by uid — text body + attachments saved to a local dir. * On-demand only (no background polling — Hybrid C). imapflow loads lazily off the cold path. * `deps.fetcherFactory` lets tests drive a fake IMAP inbox instead of a real server. */ export function registerFetchMailTool(server, deps = {}) { const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds)); server.registerTool("cogent_fetch_mail", { title: "Read this agent's mailbox", description: "List messages in this agent's Cogent mailbox, or fetch one message (text + attachments " + "saved locally) by uid. Requires cogent_setup_mail to have stored the mailbox credentials.", inputSchema: { action: z.enum(["list", "fetch"]).optional().describe("'list' (default) or 'fetch' one message"), uid: z.number().int().positive().optional().describe("Message uid to fetch (required when action='fetch')"), unreadOnly: z.boolean().optional().describe("List only unread messages (default false)"), limit: z.number().int().positive().optional().describe("Max messages to list (default 25)"), downloadDir: z .string() .optional() .describe(`Directory to save fetched attachments (default ${DEFAULT_DOWNLOAD_DIR})`), }, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, }, async ({ action, uid, unreadOnly, limit, downloadDir }) => { try { const creds = await loadMailCredentials(); if (!creds) { return errorResult(new BridgeError(BridgeErrorCode.INVALID_INPUT, "No mailbox is configured for this agent", MAIL_NOT_CONFIGURED_HINT)); } // F4: org-only mail — refuse to connect to a mailbox on a non-allowed domain (guards creds // that predate the allowlist, e.g. an externally-configured box). assertAllowedMailDomain(creds.address, "mailbox"); const fetcher = makeFetcher(creds); if (action === "fetch") { if (uid === undefined) { return errorResult(new BridgeError(BridgeErrorCode.INVALID_INPUT, "uid is required when action='fetch'", "Pass the uid from a cogent_fetch_mail list")); } const message = await fetcher.fetchOne(uid, downloadDir ?? DEFAULT_DOWNLOAD_DIR); return successResult({ mailbox: creds.address, message }); } const messages = await fetcher.list({ unreadOnly, limit: limit ?? 25 }); return successResult({ mailbox: creds.address, count: messages.length, messages }); } catch (err) { // Cogent Mail F3: on an IMAP 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=fetch-mail.js.map