/** * facade-inbound-mail.ts, assembling the inbound-mail graph for the daemon. * * Every piece here already exists and is already tested. This file is the * wiring, and it is separate from `facade-composition.ts` for the same reason * `facade-cluster.ts` is: it is one subsystem's decisions in one place, and the * decisions are the kind that must be visible rather than buried in a hundred * lines of unrelated construction. * * Three of those decisions are worth reading before changing anything. * * **The stores live under `~/.goodvibes/daemon/`**, `resolveUserPath('daemon', * …)`, the surface-scoped mechanism, not a hand-built path. All three outlive a * restart, so all three are swept at load by the housekeeper before the watcher * serves any mail. * * **The expectation book is constructed HERE, once, with the real authority * probe.** It had never been constructed in production at all: its own * defensive check, refuse to open an expectation if email ever gained command * authority, could not have fired, because there was nothing for it to fire * in. `InboundExpectationRegistry` defaults to the genuine predicate, so * building it is what takes that check off the shelf. * * **The verbs are registered here too.** `email.expectation.open/list/cancel` * were cataloged with no production call site, which made the whole capability * inert in exactly the way §2.3 describes, the middle of the chain missing * while both ends worked. They are attached to the registry that this function * builds, alongside `email.inbound.status` over the supervisor. * * **The Gmail source is CONSTRUCTED here, and that is new.** `GmailMailSource` * was complete, tested and exported, and nothing in production ever built one: * `createInboundMailSourceFactory` took the builder as an optional `deps.gmail` * and this file passed nothing, so the factory answered `null` for * `kind: 'gmail'` on every machine while `selectionFacts` reported * `googleAdopted: options.gmail !== undefined`, permanently false. An owner * with Google adopted and no IMAP set up therefore had no inbound mail at all, * and was told "no Google credentials have been adopted on this machine". * * So `gmailReader` is a REQUIRED option rather than an optional one. The * compiler is the gate: a composition that stops supplying it stops building, * which is the one check that cannot itself go inert. The reader arrives as a * provider (see `facade-gmail-reader.ts`); the cursor store, the expectation * predicate and the clock are this file's own; and the poll cadence and the * capability re-probe wait arrive on the builder's input, read once in * `source-factory.ts` at create time. One key, one reader, a second read here * would be a second answer to a question the owner has already answered, and * the config gate accepts either call form so it could not tell them apart. * * **Whether this mailbox is a Gmail one is decided from the CREDENTIAL.** See * `isGmailMailbox`: a resolved reader is Google having answered, and * `surfaces.email.inbound.accounts` is read as a filter over that rather than * as the evidence for it. Making the config key the evidence is what let this * path stay dead for an owner who filled the key in the way its own description * tells the owner to. */ import { InboundMailSupervisor } from '../email/inbound/index.js'; import type { GmailInboundReaderProvider } from '../google/gmail-inbound-reader.js'; import type { AutomationRouteBinding } from '../automation/routes.js'; import type { GatewayMethodCatalog } from '../control-plane/index.js'; import type { ConfigManager } from '../config/manager.js'; import type { RouteBindingManager } from '../channels/index.js'; import type { SecretsManager } from '../config/secrets.js'; import type { ShellPathService } from '../runtime/shell-paths.js'; import type { StructuredNotice } from '../email/inbound-notice.js'; import type { SurfaceNoticeDelivery } from './types.js'; export interface InboundMailCompositionOptions { readonly configManager: ConfigManager; readonly secretsManager: Pick; readonly shellPaths: Pick; /** * `isRouteBindingEnabled` is in the slice deliberately. Without it, a build * with `integrations.routeBinding` switched off is indistinguishable from one * where the owner has simply connected nothing: both answer `[]` from * `listBindings()`, and inbound mail silently became a recorder. */ readonly routeBindings: Pick; readonly gatewayMethods: GatewayMethodCatalog; /** * `DaemonSurfaceDeliveryHelper.deliverStructuredNotice`. * * Takes the STRUCTURE, not a rendered string, so nothing on the inbound path * ever holds channel-formatted text. The helper resolves the surface from the * binding and picks the escaper there, the only code that turns spans into * text is the code that knows where they are going. */ readonly deliverStructuredNotice: (binding: AutomationRouteBinding | undefined, notice: StructuredNotice) => Promise; /** * How this machine's Google credential is turned into Gmail-reading I/O. * * REQUIRED, deliberately. Its optional predecessor (`gmail?: * GmailSourceBuilder`) is what let the whole Gmail path ship inert: nothing * ever filled it, and an unfilled optional field is indistinguishable from a * machine with no Google account. A required provider that ANSWERS * `unavailable` with a reason carries the same information and cannot be * forgotten, `createBuiltinChannelRuntime` stops compiling if it is dropped. * * Called once per supervisor start rather than held as a resolved value, so a * credential adopted while the daemon is running is picked up on the next * start instead of at the next restart. */ readonly gmailReader: GmailInboundReaderProvider; } /** * Build the inbound-mail supervisor and register its verbs, or answer `null` * when this composition watches no mailbox. * * `null` is not a failure and it is not silence: `BuiltinChannelRuntime` * reports at ERROR when inbound mail is enabled and nothing was composed, and * the cluster registration declines to contest a surface this node cannot * serve, a node that won that election would stand every other node down and * then read nothing. */ export declare function composeInboundMail(options: InboundMailCompositionOptions): InboundMailSupervisor | null; //# sourceMappingURL=facade-inbound-mail.d.ts.map