# @kody/morning-briefing — agent notes Human setup and intent live in [`README.md`](./README.md). This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Never paste OAuth client secrets. Do not disable or enable live jobs unless the operator explicitly asks — the bundled `daily-morning-briefing` job stays `enabled: false` until they save config and approve a dry-run. ## Auth | Source | Credential | | --- | --- | | calendar | Google OAuth via `@kody/google` (`account`, default `personal`) | | weather / health | none (Open-Meteo public APIs) | | inbox | Google OAuth with `gmail.readonly` (BYO client) | | home | caller-supplied `home.items` only | | email / notify output | `kody.emailSend` to signed-in account (no API-key secret) | Hosts: `www.googleapis.com`, `gmail.googleapis.com` (inbox), Open-Meteo geocoding / forecast / air-quality hosts. No `/account/secrets/new` for this package. ## Import paths | Export | Import | | --- | --- | | root (build + optional deliver) | `kody:@kody/morning-briefing` | | run-morning-briefing | `kody:@kody/morning-briefing/run-morning-briefing` | | build-report | `kody:@kody/morning-briefing/build-report` | | get-report | `kody:@kody/morning-briefing/get-report` | Prefer static `kody:@kody/morning-briefing/...` imports from `execute`. Do not lead with `packages.invoke`. ## Smoke test (read-only / no send) Weather-only (no OAuth): ```js import buildReport from 'kody:@kody/morning-briefing/build-report' export default async function main() { const report = await buildReport({ location: 'Austin', sources: ['weather'], }) return { date: report.date, sections: report.sections.map((s) => ({ id: s.id, status: s.status })), } } ``` Delivery dry-run (does not send): ```js import runMorningBriefing from 'kody:@kody/morning-briefing/run-morning-briefing' export default async function main() { return await runMorningBriefing({ dryRun: true, location: 'Austin', sources: ['weather'], output: 'email', }) } ``` Compose preview with optional sources: ```js import runMorningBriefing from 'kody:@kody/morning-briefing' export default async function main() { return await runMorningBriefing({ dryRun: true, location: 'Denver, CO', timezone: 'America/Denver', sources: ['calendar', 'weather', 'inbox', 'health', 'home'], output: 'email', calendar: { accounts: ['personal'] }, inbox: { account: 'personal', query: 'is:unread newer_than:1d' }, home: { items: [{ title: 'Thermostat 70°', detail: 'Heat, occupied' }] }, }) } ``` ## Edge cases - Missing Google auth becomes a warning/skipped section; do not abort the whole briefing for one bad source. - `@kody/google` `listEventsAcrossCalendars` stops at its wall-clock budget. A truncated scan becomes a calendar warning so a partial day is not reported as complete. - 403 / insufficient-scope must name the missing scope and the connect URL — never fail silently. - This official listing does **not** post to Discord and has no Kent-specific Kit/YouTube/GitHub/Tesla/apartment wiring. - Do not enable `daily-morning-briefing` on the official listing; after a fork, enable only when config is saved and a dry-run looks right. - `output: 'notify'` is short callout-only body; `email` is the full markdown.