> ## Documentation Index
> Fetch the complete documentation index at: https://labs.prompthon.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Email Assist Starter

<div className="not-prose my-4 rounded-md border border-gray-200 bg-gray-50 p-2 text-sm dark:border-gray-800 dark:bg-gray-900/40">
  <div className="mb-2 px-1 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Need help?</div>

  <div className="flex flex-wrap gap-2">
    <a className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1.5 font-medium text-gray-700 no-underline shadow-sm hover:border-gray-300 hover:bg-gray-50 dark:border-gray-800 dark:bg-gray-950/60 dark:text-gray-200 dark:hover:bg-gray-900" href="https://discord.gg/sDE2HhGTg4" target="_blank" rel="noreferrer">
      <Icon icon="discord" iconType="brands" size={14} />

      <span>Ask in Discord</span>
    </a>

    <a className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1.5 font-medium text-gray-700 no-underline shadow-sm hover:border-gray-300 hover:bg-gray-50 dark:border-gray-800 dark:bg-gray-950/60 dark:text-gray-200 dark:hover:bg-gray-900" href="https://github.com/Prompthon-IO/agent-systems-handbook/issues/new/choose" target="_blank" rel="noreferrer">
      <Icon icon="github" iconType="brands" size={14} />

      <span>Open a GitHub issue</span>
    </a>

    <a className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1.5 font-medium text-gray-700 no-underline shadow-sm hover:border-gray-300 hover:bg-gray-50 dark:border-gray-800 dark:bg-gray-950/60 dark:text-gray-200 dark:hover:bg-gray-900" href="https://github.com/Prompthon-IO/agent-systems-handbook/blob/main/SUPPORT.md" target="_blank" rel="noreferrer">
      <Icon icon="life-ring" size={14} />

      <span>Support guide</span>
    </a>
  </div>
</div>

## Summary

This starter extends the customer-support case study into a connector-first
local workflow: read Gmail through the Codex Gmail connector, classify customer
issues with deterministic rules first, persist them in SQLite, and review or
approve replies through a local dashboard.

## Status

`starter`

Source code: [case-studies/examples/customer-email-assist-starter](https://github.com/Prompthon-IO/agent-systems-handbook/tree/main/case-studies/examples/customer-email-assist-starter)

## Why It Exists

The earlier [Customer Support Email Agent Starter](/case-studies/examples/customer-support-email-agent-starter)
shows the safer draft-only boundary. This follow-on starter keeps the same
local-first posture, but adds the operational surfaces teams usually need next:
Gmail connector import, local SQLite state, customer review queues, and a
deterministic dashboard.

## Related Lab Pages

* [Customer Support Agents](/case-studies/customer-support-agents)
* [Customer Support Email Agent Starter](/case-studies/examples/customer-support-email-agent-starter)
* [Case Studies Overview](/case-studies)

## Folder Structure

```text theme={null}
customer-email-assist-starter/
├── app/
│   ├── api/
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── index.mdx
├── package.json
├── README.md
├── scripts/
│   └── customer-email-assist.ts
├── skill/
│   └── SKILL.md
└── src/
    ├── components/
    ├── lib/
    └── test/
```

## Quick Start

Install dependencies and run the local dashboard:

```bash theme={null}
npm install
npm run setup-local
npm run dev
```

For the default connector-assisted low-token path, let Codex use the Gmail
connector to produce a prepared inbound batch, then import that batch:

```bash theme={null}
tsx scripts/customer-email-assist.ts import-prepared-batch --input /tmp/prepared-inbound.json
```

Write intermediate batches to files instead of printing large JSON blobs to
stdout:

```bash theme={null}
tsx scripts/customer-email-assist.ts prepare-draft-batch --out /tmp/draft-batch.json
```

An advanced direct Gmail OAuth adapter still exists for standalone local
integrations, but it is not the default setup path:

```bash theme={null}
npm run sync:oauth
```

For the direct OAuth auto-send path, follow the starter README and use the
dashboard `Connect Gmail` button after creating the Google OAuth client:

```text theme={null}
case-studies/examples/customer-email-assist-starter/README.md
```

In connector-first mode, dashboard approval stores issues as
`approved_to_send` for the Codex Gmail connector runner. If local OAuth is
connected, the dashboard can execute the deterministic send path immediately
after the undo countdown.

## Minimal-Token Boundary

This starter is designed to minimize model usage.

* Hard logic handles Gmail connector import shaping, HTML stripping, signature
  trimming, quoted history removal, customer matching, SQLite persistence,
  analytics, and queue state.
* The intended skill workflow reserves model usage for only two JSON-only
  surfaces:
  * understanding cleaned inbound customer messages
  * generating template-field JSON for reply drafts
* The rendered reply text and HTML are produced locally from fixed templates,
  not from free-form model output.

## Included Sample Files

* `skill/SKILL.md`: Codex workflow for the two model-backed steps plus the
  deterministic CLI commands
* `src/lib/email-processing.ts`: HTML cleanup, quoted-history trimming, and
  heuristic classification helpers
* `src/lib/repository.ts`: SQLite queries for issues, customers, analytics, and
  review actions
* `src/lib/sync.ts`: deterministic inbound batch import, draft preparation, and
  draft rendering coordination
* `app/page.tsx`: Next.js App Router dashboard with issues, analysis, customer
  review, and customer management
* `README.md`: direct Gmail OAuth setup, dashboard connection flow, and optional
  runtime variables

## Constraints

* Gmail authentication is expected to happen through the Codex Gmail connector
  in the normal workflow.
* Direct Gmail OAuth exists as an advanced adapter for standalone local runs.
  Its required values are `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`; the
  dashboard connection flow stores the refresh token in local state.
* Attachments and inline images are ignored in v1.
* The dashboard is single-operator and local-machine oriented.
* `import-prepared-batch` uses deterministic fallback understanding and drafting
  so the starter remains runnable without a live model call.
* `npm run setup-local` resets the local SQLite database to an empty state.

## Verification

* `npm run lint`
* `npm run test`
* `npm run build`

## Next Steps

* Insert a real model-backed `understand` step between `prepare-inbound-batch`
  and `persist-understanding`.
* Insert a model-backed `draft-fields` step between `prepare-draft-batch` and
  `render-save-drafts`.
* Add a fixture-driven Gmail replay suite with sample labeled threads.
