Business-first applied AI project that converts high-volume enterprise inbox traffic into reliable, auditable workflow execution.
Cycle 1 + Cycle 2 now include:
- JSONL inbox ingestion and normalization
- intent/priority/action decision engine (heuristic baseline)
- policy gating (
AUTO_EXECUTE,REQUIRE_REVIEW,DENY) - mock tool execution with audit logging
- evaluation harness with reproducible metrics
- demo/review queue artifacts for portfolio walkthroughs
- offline channel adapters for Slack events and email threads
- Slack live webhook integration with request signature verification
- Gmail live ingestion runner with mock/live API mode
- ticketing and CRM business handoff adapters
- explicit human review state workflow (
PENDING,APPROVED,REJECTED)
npm install
npm run devUseful commands:
npm run typechecknpm run testnpm run demonpm run demo:channelsnpm run slack:livenpm run gmail:livenpm run demo:handoffnpm run demo:reviewnpm run review:decidenpm run review:metricsnpm run evalnpm run build
npm run demo
npm run demo:channels
npm run gmail:live
npm run demo:handoff
npm run demo:review
npm run review:metrics
npm run evalOutputs:
- Demo summary:
reports/demo-summary.md - Review queue report:
reports/review-queue.md - Channel integration demo:
reports/channel-integration-demo.md - Gmail integration demo:
reports/gmail-live-demo.md - Business handoff demo:
reports/handoff-demo.md - Review workflow:
reports/review-workflow.md - Review metrics:
reports/review-metrics.md - Evaluation report:
reports/eval-report.md
- Enable the Gmail API with
gmail.readonlyandgmail.sendscopes (seedocs/setup-manifests/google-gmail-oauth.template.json). - Populate these env vars before running live ingestion:
GMAIL_ACCESS_TOKEN,GMAIL_QUERY(e.g.,subject:"[AGENT-LIVE-TEST]" newer_than:1d),GMAIL_REPLY_MODE=live, andGMAIL_REPLY_ALLOWLISTwith the sender(s) you want to reply to. - Execute
GMAIL_MODE=live GMAIL_REPLY_MODE=live \
GMAIL_QUERY='subject:"[AGENT-LIVE-TEST]" newer_than:1d' \
GMAIL_REPLY_ALLOWLIST='aerissat@youngtoon.com,hallo@tatua.site' \
GMAIL_ACCESS_TOKEN=... \
npm run gmail:liveAuto-reply actions are recorded as stage: "gmail_reply" in logs/audit-gmail-live.jsonl; review/deny outcomes do not trigger replies.
src/slack-live-server.ts: live Slack webhook ingestion serversrc/gmail-live-runner.ts: Gmail ingestion runnersrc/integrations-business-handoff.ts: ticketing/CRM adapter layersrc/review-workflow.ts: human review state and metrics modeldocs/CODE_EXECUTION_PATH.md: non-technical execution walkthroughdocs/INSTALLATION_TUTORIAL.md: non-technical workplace setup by integration platformdocs/CYCLE2_IMPLEMENTATION_PLAN.md: ordered cycle plan and done criteria
Use synthetic inbox events in this repository. Keep connectors modular so Slack/Gmail/Outlook/Jira/CRM live adapters can be added with limited core changes.
This guide is for non-technical users who want to set up this system in a workplace.
- Reads incoming business messages (chat/email)
- Decides what type of request each message is
- Either handles it automatically or sends it to review
- Logs everything for traceability
You need support from one technical person in your company for initial credentials.
Minimum requirements:
- A computer/server where the app can run
- Access to your company Slack and/or email platform
- Permission to create app credentials (tokens/secrets)
- Choose the platforms you want to connect.
- Use the matching setup file in
docs/setup-manifests/. - Collect credentials for those platforms.
- Enter those credentials as environment variables.
- Run the relevant command for each platform.
- Verify output reports and logs.
From the project folder:
npm install
npm run typecheck
npm run testUse this only when running locally on your laptop/desktop.
Slack must send events to a public HTTPS URL. For local testing, use ngrok.
macOS (Homebrew):
brew install ngrok/ngrok/ngrokWindows (winget):
winget install ngrok.ngrok- Go to dashboard.ngrok.com/signup and create a free account.
- In ngrok dashboard, open Your Authtoken.
- Copy the token.
- In terminal, run:
ngrok config add-authtoken <YOUR_AUTHTOKEN>In project folder:
npm run slack:liveYou should see:
Slack live server listening on http://localhost:8787/slack/events
In a second terminal:
ngrok http 8787ngrok will show a Forwarding URL like:
https://abcd-12-34-56-78.ngrok-free.app
- Open your Slack app settings at api.slack.com/apps
- Go to Event Subscriptions
- Turn Enable Events ON
- Set Request URL to:
https://abcd-12-34-56-78.ngrok-free.app/slack/events
- Save changes
- Terminal 1:
npm run slack:live - Terminal 2:
ngrok http 8787
If you stop either one, Slack events will fail.
If your app is running on a remote Linux server with a real public domain and HTTPS, you usually do not need ngrok.
In production/workplace environments, preferred path is:
- Open HTTPS ingress on your cloud + VM
- Serve your app behind Caddy or Nginx with TLS
- Configure Slack directly with your domain:
https://your-domain.com/slack/events
This section is for persistent deployment on a cloud VM.
Important:
- On a remote Linux server with a real domain and valid HTTPS, you do not use ngrok.
- Slack can call your server directly at
https://your-domain.com/slack/events. - ngrok is only for local machine testing when your laptop is not publicly reachable.
Assumptions:
- Ubuntu 22.04 (or similar)
- You have a domain name pointing to the server public IP
- You can SSH into the server with sudo access
ssh <your-user>@<your-server-ip>sudo apt update
sudo apt install -y curl unzip git
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -vClone/copy project:
git clone <YOUR_REPO_URL> enterprise-inbox-agent
cd enterprise-inbox-agent
npm install
npm run typechecknano .envExample minimum values:
SLACK_SIGNING_SECRET=<your-signing-secret>
SLACK_BOT_USER_OAUTH_TOKEN=<your-bot-user-oauth-token>
SLACK_OUTBOUND_MODE=live
PORT=8787Create service:
sudo nano /etc/systemd/system/inbox-agent.servicePaste:
[Unit]
Description=Enterprise Inbox Workflow Agent
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/<your-user>/enterprise-inbox-agent
ExecStart=/usr/bin/npm run slack:live
Restart=always
RestartSec=5
User=<your-user>
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.targetStart it:
sudo systemctl daemon-reload
sudo systemctl enable inbox-agent
sudo systemctl start inbox-agent
sudo systemctl status inbox-agent --no-pagerYou need both layers configured:
- Cloud network firewall/security rules
- OS firewall on the VM (if enabled)
Why both 80 and 443:
443serves HTTPS traffic80is often needed for automatic certificate validation (Let’s Encrypt)
In GCP, this is done mainly through VPC Firewall Rules attached to your VM network/tags.
Checklist:
- Open Google Cloud Console -> VPC network -> Firewall.
- Create or update a rule that targets your VM (network tag or all instances).
- Ingress allow TCP ports
80,443from source0.0.0.0/0. - Confirm your VM has the matching network tag if rule is tag-based.
Common GCP pitfall:
- Firewall rule exists, but VM does not have the rule's target tag.
In OCI, ingress can be controlled at two different layers:
- Security Lists (subnet level)
- Network Security Groups (VNIC/instance level)
Checklist:
- Open OCI Console -> Networking -> Virtual Cloud Networks.
- Check subnet Security List rules for ingress TCP
80and443from0.0.0.0/0. - If NSG is used, also add ingress TCP
80and443there. - Verify your instance VNIC is actually attached to that NSG.
Common OCI pitfall:
- Port opened in Security List but blocked in NSG (or vice versa).
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw statusCaddy is easiest for non-technical installs because TLS certificates are automatic.
Install Caddy:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddyCreate Caddy config:
sudo nano /etc/caddy/CaddyfileExample:
your-domain.com {
reverse_proxy 127.0.0.1:8787
}Reload Caddy:
sudo systemctl reload caddy
sudo systemctl status caddy --no-pagerCaddy will automatically provision TLS certificates if DNS and ports are correct.
Quick verification:
curl -I https://your-domain.com/slack/eventsYou should get an HTTPS response (even if method/path is not fully handled by GET).
Nginx is also valid, but TLS is usually handled by Certbot after base reverse proxy is set.
Install Nginx + Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxCreate Nginx site config:
sudo nano /etc/nginx/sites-available/inbox-agentExample HTTP reverse-proxy block:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Enable and test config:
sudo ln -s /etc/nginx/sites-available/inbox-agent /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxIssue TLS certificate:
sudo certbot --nginx -d your-domain.comAfter Certbot, Nginx is usually updated with:
- HTTPS listener on
443 - certificate paths
- HTTP -> HTTPS redirect
Verify:
sudo nginx -t
curl -I https://your-domain.com/slack/eventsIn Slack App -> Event Subscriptions -> Request URL:
https://your-domain.com/slack/events
If verification succeeds, Slack is connected.
journalctl -u inbox-agent -fPost a message in Slack channel and confirm:
- Bot responds in thread
- New entries appear in app logs
Use this setup file:
docs/setup-manifests/slack-app-manifest.yaml
How to use it:
- Go to api.slack.com/apps
- Click
Create New App - Choose
From a manifest - Paste content from
slack-app-manifest.yaml - Replace
https://YOUR_HOST/slack/eventswith your real URL - Install app to workspace
Local credentials file:
.env(already supported by runtime)- Never commit this file
Required runtime values:
SLACK_SIGNING_SECRET- Optional for live replies:
SLACK_BOT_TOKENorSLACK_BOT_USER_OAUTH_TOKEN - Optional default human reviewers:
SLACK_REVIEW_OWNER_USER_ID(one or comma-separated Slack user IDs, e.g.U123ABC456,U234BCD567) - Optional queue-based reviewer routing:
SLACK_REVIEW_QUEUE_OWNERS_JSON(JSON map like{"billing":["U123...","U234..."],"revops":"U456..."})
Start local server in safe mode:
npm run slack:liveRun local signed test request:
npm run slack:testExpected result:
npm run slack:testreturns HTTP status200logs/audit-slack-live.jsonlreceives new entries
Events endpoint for Slack cloud events:
https://<your-host>/slack/events
To enable live threaded replies after install:
SLACK_OUTBOUND_MODE=live npm run slack:live- Invite the bot to your channel:
- In Slack channel, run
/invite @<your-bot-name>
- Post a test message in that channel, for example:
Need access to billing dashboard for finance close
-
Confirm bot response appears in thread.
-
For human-review notification proof, set reviewers and retest:
- In
.env:SLACK_REVIEW_OWNER_USER_ID=<SLACK_USER_ID_1>,<SLACK_USER_ID_2>,<SLACK_USER_ID_3> - Restart server and post a review-worthy message (for example:
Can the external audit team get time with our finance department next week?) - Confirm second bot thread reply mentions the reviewer(s) as
<@USER_ID>and each reviewer receives a DM from the bot
- Confirm logs update:
logs/audit-slack-live.jsonl
- Take screenshots/recordings of:
- Slack app Event Subscriptions page with verified Request URL
- Terminal with
npm run slack:live(orsystemctl status inbox-agenton server) - Auto-execute thread showing
Evidence=... record_id=... ref=...(record update proof) - Review thread showing reviewer mention notification plus one reviewer DM window
logs/mock-crm-updates.jsonlshowing new record update entry- Audit log lines showing
decision,policy, andtool_executionstages
Use this setup file:
docs/setup-manifests/google-gmail-oauth.template.json
Required values:
GMAIL_ACCESS_TOKENGMAIL_REPLY_MODE(mockorlive)GMAIL_REPLY_ALLOWLIST(comma-separated emails; required when reply mode islive)
Mock mode first:
npm run gmail:liveLive ingestion only (no outbound reply):
GMAIL_MODE=live GMAIL_REPLY_MODE=mock GMAIL_ACCESS_TOKEN=... GMAIL_QUERY='newer_than:7d' npm run gmail:liveControlled live reply mode:
GMAIL_MODE=live GMAIL_REPLY_MODE=live GMAIL_ACCESS_TOKEN=... GMAIL_QUERY='subject:"[AGENT-LIVE-TEST]" newer_than:1d' GMAIL_REPLY_ALLOWLIST='your-test-sender@example.com' npm run gmail:liveExpected live-reply behavior:
- Auto-execute +
replyaction: Gmail reply is sent in-thread - Review/deny outcomes: no Gmail reply is sent
- Reply events are logged under
stage: "gmail_reply"inlogs/audit-gmail-live.jsonl
Use this setup file:
docs/setup-manifests/business-handoff-webhooks.template.env
Mock mode:
BUSINESS_HANDOFF_MODE=mock TICKETING_PROVIDER=jira npm run demo:handoffAlternative provider:
BUSINESS_HANDOFF_MODE=mock TICKETING_PROVIDER=linear npm run demo:handoffLive mode requires:
BUSINESS_HANDOFF_MODE=liveTICKETING_WEBHOOK_URL=<endpoint>
Use this setup file:
docs/setup-manifests/business-handoff-webhooks.template.env
Mock mode:
BUSINESS_HANDOFF_MODE=mock CRM_PROVIDER=hubspot npm run demo:handoffAlternative provider:
BUSINESS_HANDOFF_MODE=mock CRM_PROVIDER=salesforce npm run demo:handoffLive mode requires:
BUSINESS_HANDOFF_MODE=liveCRM_WEBHOOK_URL=<endpoint>
This adds a controlled human approval step.
- Generate pending review items:
npm run demo:review- Approve or reject each item:
REVIEW_MESSAGE_ID=<id> REVIEW_DECISION=APPROVED REVIEWER_NAME=<name> npm run review:decideor
REVIEW_MESSAGE_ID=<id> REVIEW_DECISION=REJECTED REVIEWER_NAME=<name> npm run review:decide- Generate review metrics:
npm run review:metricsOutput files:
logs/review-items.jsonllogs/review-decisions.jsonlreports/review-workflow.mdreports/review-metrics.md
Use this setup file:
docs/setup-manifests/microsoft-graph-mail.template.json
Status:
- Not implemented yet in this codebase.
Use this setup file:
docs/setup-manifests/microsoft-teams-bot.template.json
Status:
- Not implemented yet in this codebase.
npm run demo
npm run demo:channels
npm run gmail:live
npm run demo:handoff
npm run demo:review
npm run review:metrics
npm run eval- Start with mock mode for each integration
- Confirm review routing and decision logs are working
- Confirm logs are written for every run
- Enable live mode one platform at a time
- Keep one designated reviewer for the first week
If Slack requests are rejected:
- Check
SLACK_SIGNING_SECRET - Check Slack URL points to
/slack/events - Run
npm run slack:testto verify local signature flow
If Slack live server fails with EADDRINUSE (port already in use):
- Find the existing process:
lsof -nP -iTCP:8787 -sTCP:LISTEN - Stop it:
kill <PID> - Or start on another port:
PORT=8788 npm run slack:live - If using ngrok, match that port:
ngrok http 8788
If Gmail live mode fails:
- Check
GMAIL_ACCESS_TOKENvalidity and permissions
If Gmail live reply mode sends no replies:
- Confirm
GMAIL_REPLY_MODE=live - Confirm
GMAIL_REPLY_ALLOWLISTcontains the sender email - Confirm message policy outcome is
AUTO_EXECUTEand action includesreply
If handoff fails in live mode:
- Check
BUSINESS_HANDOFF_MODE=live - Check webhook URLs are reachable
If review decisions fail:
- Check
REVIEW_MESSAGE_IDexists inlogs/review-items.jsonl - Check
REVIEW_DECISIONisAPPROVEDorREJECTED
Recommended roles:
- Operations owner: review policy and queue monitoring
- IT/admin owner: credential and secret management
- Technical owner: runtime, logs, and incident response
This tutorial should be updated whenever a new platform integration is added or command/env names change.