Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
57e0106
fix(linkedin): harden history checks and quota rollover
iblmediaconsultancy Sep 27, 2026
a99e786
chore(main): release 1.5.2
iblmediaconsultancy Sep 27, 2026
cec1a68
fix(linkedin): stabilize browser recipient resolution
iblmediaconsultancy Sep 28, 2026
f5cf6cb
chore(main): release 1.5.3
iblmediaconsultancy Sep 28, 2026
341c382
fix(linkedin): reconcile profile and member identities
iblmediaconsultancy Sep 28, 2026
6cd6d03
chore(main): release 1.5.4
iblmediaconsultancy Sep 28, 2026
dca90ac
fix(api): scope LinkedIn history to resolved composer
iblmediaconsultancy Sep 30, 2026
5a41586
chore(main): release 1.5.5
iblmediaconsultancy Sep 30, 2026
ac3ffcc
fix(api): wait for stable LinkedIn composer hydration
iblmediaconsultancy Sep 30, 2026
b0b60f0
chore(main): release 1.5.6
iblmediaconsultancy Sep 30, 2026
14509a8
fix: harden LinkedIn profile and conversation resolution
iblmediaconsultancy Oct 1, 2026
318e719
chore(main): release 1.5.7
iblmediaconsultancy Oct 1, 2026
1cd364a
fix: ignore LinkedIn recommendation heading in profile identity
iblmediaconsultancy Oct 1, 2026
1864307
chore(main): release 1.5.8
iblmediaconsultancy Oct 1, 2026
2bbdd5c
fix(atlas): restore authenticated email authorization flow
iblmediaconsultancy Oct 2, 2026
9a9896c
fix(db): seed workspace before Atlas membership
iblmediaconsultancy Oct 2, 2026
7985e23
style(auth): apply repository formatter
iblmediaconsultancy Oct 2, 2026
89255b4
style(db): format prisma seed
iblmediaconsultancy Oct 2, 2026
6a98d28
style(agent): format existing Eve files
iblmediaconsultancy Oct 2, 2026
27aadac
style(app): format existing UI files
iblmediaconsultancy Oct 2, 2026
cadb50e
Merge pull request #23 from iblmediaconsultancy/codex/atlas-email-aut…
iblmediaconsultancy Oct 2, 2026
a66a2d6
chore(main): release 1.5.9
github-actions[bot] Oct 2, 2026
f3d9c9e
Merge pull request #24 from iblmediaconsultancy/release-please--branc…
iblmediaconsultancy Oct 2, 2026
789e8ce
feat(atlas): restore authenticated Email dispatch readiness
iblmediaconsultancy Oct 3, 2026
84d91b1
chore: restore clean-main baseline CI (#27)
iblmediaconsultancy Oct 3, 2026
c114b56
chore(main): release 1.6.0 (#28)
iblmediaconsultancy Oct 3, 2026
cb7d19f
fix(api): resolve LinkedIn replies through stored threads (#22)
iblmediaconsultancy Oct 3, 2026
d42b146
chore(main): release 1.6.1 (#31)
iblmediaconsultancy Oct 3, 2026
c9123cc
fix(app): keep workspace label import client-safe (#33)
iblmediaconsultancy Oct 3, 2026
32ecebc
chore(main): release 1.6.2 (#35)
iblmediaconsultancy Oct 3, 2026
d16b996
fix(api): gate outbound claims before leasing (#36)
iblmediaconsultancy Oct 3, 2026
65ed0f9
chore(main): release 1.6.3 (#37)
iblmediaconsultancy Oct 3, 2026
3975096
fix(api): idle follow-up recovery while gates are off (#39)
iblmediaconsultancy Oct 4, 2026
dc51d0b
chore(main): release 1.6.4 (#41)
iblmediaconsultancy Oct 4, 2026
9de09c2
fix(api): resolve LinkedIn composer after stored-thread navigation (#42)
iblmediaconsultancy Oct 4, 2026
d4739e9
chore(main): release 1.6.5 (#43)
iblmediaconsultancy Oct 4, 2026
44e75f7
feat(api): add exact follow-up execution cohorts (#45)
iblmediaconsultancy Oct 4, 2026
9db04d4
chore(main): release 1.7.0 (#47)
iblmediaconsultancy Oct 4, 2026
c99aab8
fix(atlas): avoid follow-up cohort preview timeouts (#49)
iblmediaconsultancy Oct 5, 2026
e232958
chore(main): release 1.7.1 (#51)
iblmediaconsultancy Oct 5, 2026
a804c65
feat(linkedin): add manual operator workflow
iblmediaconsultancy Oct 15, 2026
ef76fa0
fix(atlas): harden authorization expiry validation
iblmediaconsultancy Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.git
.turbo
.vercel
.env
.env.*
!.env.example
node_modules
**/node_modules
**/.next
**/.eve
**/.output
**/coverage
**/dist
**/tsconfig.tsbuildinfo
phase6/artifacts
phase7/.rehearsal-secrets
ibl-v2-review.zip
54 changes: 46 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Postgres
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/crm?schema=public"

# Optional host port for Docker Postgres when port 5432 is already in use.
# POSTGRES_PORT="5432"

# A direct, unpooled connection to that same database, used by
# `prisma migrate deploy` when the API is built on Vercel. Set it only if
# DATABASE_URL points at a pooler; POSTGRES_URL_NON_POOLING is read as a
Expand Down Expand Up @@ -83,12 +86,17 @@ GOOGLE_CLIENT_SECRET=""
# their standard PORT variable instead.
# AGENT_PORT="2000"

# Scheduled agent dispatch is disabled unless explicitly enabled. The service
# can boot and expose health/readiness while persisted work remains untouched.
# ATLAS_SCHEDULED_EXECUTION_ENABLED="false"

# Lets a signed-in rep talk to the agent from the contact sheet.
#
# The browser never calls the agent directly. The app proxies /eve/v1/* on its
# own origin, checks the session, and mints a two-minute token signed with this
# secret; the agent verifies it and learns *which rep* is asking. Set the same
# value for both processes. openssl rand -base64 32
# secret; the agent verifies it and learns *which rep* is asking. The API also
# uses the same secret for authenticated internal agent calls. Generate one
# value with openssl rand -base64 32 and provide it to app, API, and agent.
#
# It also authorises two calls the API makes to the agent: the dispatch poke,
# which is what makes a new company's logo appear as it is added instead of on
Expand All @@ -99,7 +107,7 @@ GOOGLE_CLIENT_SECRET=""
# is skipped rather than sent unauthenticated, and a Context key is saved
# without being checked. Nothing else changes: the agent still runs on its own
# schedule.
# AGENT_BRIDGE_SECRET=""
AGENT_BRIDGE_SECRET=""

# PORT="3001"

Expand Down Expand Up @@ -139,11 +147,21 @@ GOOGLE_CLIENT_SECRET=""
# origin's URL and renders as an ordinary hotlinked image.
# BLOB_READ_WRITE_TOKEN=""

# The model the agent runs on is reached through the Vercel AI Gateway. On
# Vercel this is handled by OIDC and needs nothing; elsewhere, set a key.
# Which model is a setting, not a variable — change it on the settings page.
# https://vercel.com/docs/ai-gateway
# AI_GATEWAY_API_KEY=""
# The agent uses Google's Gemini API directly.
# https://ai.google.dev/gemini-api/docs
GOOGLE_GENERATIVE_AI_API_KEY=""

# Google Calendar is an optional meeting integration. Register this exact
# callback in Google Cloud: ${API_URL}/api/integrations/google-calendar/callback
# (local: http://localhost:3001/api/integrations/google-calendar/callback). The
# account token is encrypted with BETTER_AUTH_SECRET; these values are only
# needed to complete OAuth and identify the calendars used for availability.
# GOOGLE_CALENDAR_CLIENT_ID=""
# GOOGLE_CALENDAR_CLIENT_SECRET=""
# GOOGLE_CALENDAR_CLIENT_SECRET_FILE="/run/secrets/google_calendar_client_secret"
# GOOGLE_CALENDAR_PRIMARY_ID="primary"
# GOOGLE_CALENDAR_HVA_ID=""
# GOOGLE_CALENDAR_BLOCKER_IDS=""


# ── Optional: operations ─────────────────────────────────────────────────────
Expand All @@ -157,6 +175,26 @@ GOOGLE_CLIENT_SECRET=""
# route. The route refuses to run without it. At least 16 characters.
# CRON_SECRET=""

# IBL provider credentials are server-only and optional. Capability rows stay
# UNVERIFIED until the separate real-provider proof has passed.
# MIAB_IMAP_HOST="mail.example.test"
# MIAB_IMAP_PORT="993"
# MIAB_MAILBOX_CREDENTIALS_JSON='{"mailbox@example.test":"password"}'
# RESEND_API_KEY_FILE="/run/secrets/resend_api_key"
# RESEND_WEBHOOK_SECRET_FILE="/run/secrets/resend_webhook_secret"
# RESEND_SYSTEM_FROM_EMAIL="no-reply@example.com"
# RESEND_SYSTEM_FROM_NAME="IBL Command Center"
# RESEND_OUTREACH_FROM_EMAIL="outreach@iblmedia.com"
# RESEND_OUTREACH_FROM_NAME="IBL Media Team"
# ATLAS_LIVE_OUTREACH_ENABLED="false"
# Enables autonomous scheduled Atlas execution; leave false unless explicitly authorized.
# ATLAS_SCHEDULED_EXECUTION_ENABLED="false"
# The local LinkedIn executor cannot use authoritative CRM unless this is
# explicitly set to "true" in the operator's runtime environment. It does not
# enable Email or any other Atlas outbound capability.
# ATLAS_LINKEDIN_LOCAL_EXECUTOR_ENABLED="false"
# IBL_LOCAL_PROVIDER_DOUBLE="enabled"

# Log every SQL statement Prisma runs, at debug level, without bound
# parameters. Off by default: it buries every other line under a wall of
# SELECTs.
Expand Down
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
2 changes: 1 addition & 1 deletion .github/.release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.5.1"
".": "1.7.1"
}
10 changes: 6 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,19 @@ jobs:
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: crm
POSTGRES_DB: crm_ci
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres -d crm"
--health-cmd "pg_isready -U postgres -d crm_ci"
--health-interval 3s
--health-timeout 5s
--health-retries 20

env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/crm?schema=public
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/crm_ci?schema=public
DATABASE_MIGRATION_URL: postgresql://postgres:postgres@localhost:5432/crm_ci?schema=public
ATLAS_DISPOSABLE_DATABASE: 1
ALLOWED_SIGN_IN: example.com
BETTER_AUTH_SECRET: ci-only-secret-regenerate-for-any-real-deployment
API_URL: http://localhost:3001
Expand All @@ -57,7 +59,7 @@ jobs:
- run: bun install --frozen-lockfile

- name: Apply migrations
run: bun run db:deploy
run: bun run scripts/migrate-fresh-with-workspace-bootstrap.ts --fresh

- run: bun run check-types

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ node_modules
.env
.env.*
!.env.example
.env.phase0.local

# Testing
coverage
Expand All @@ -26,6 +27,12 @@ out/
build
dist

# V1 migration exports can contain personal and mailbox data. Only redacted
# manifests and reconciliation summaries belong in source control.
/phase6/artifacts/
/rehearsal-input/v1-authorized-copy/
/phase7/.rehearsal-secrets/


# Debug
/.logs/
Expand Down
13 changes: 8 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ rules and skills you read.

## Median Tasks

Median can use a project-local workspace binding. If this repository has
`.median/config.json`, run `mdn` commands from inside this repository so the
correct Median workspace profile is selected. The local config stores only a
profile name; API keys stay in your user config.
Median can use a project-local workspace binding when this repository is
configured for Median. If `.median/config.json` and the `mdn` command are
present, run `mdn` commands from inside this repository so the correct Median
workspace profile is selected. The local config stores only a profile name;
API keys stay in your user config.

To bind this repository to a workspace:

Expand Down Expand Up @@ -84,7 +85,9 @@ mdn create --title "Description" --status todo --priority medium --agent <your-a

## Commit Messages & Pull Requests

Always include the Median task ID in commit messages and PR titles so tasks get marked automatically.
When Median is configured and a task ID is assigned, include that Median task ID
in commit messages and PR titles so tasks get marked automatically. Otherwise,
follow the repository's normal Conventional Commit workflow.

```
git commit -m "MDN-42 fix: resolve auth token expiry"
Expand Down
114 changes: 114 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,119 @@
# Changelog

## [1.7.1](https://github.com/iblmediaconsultancy/crm/compare/v1.7.0...v1.7.1) (2026-10-05)


### Fixes

* **atlas:** avoid follow-up cohort preview timeouts ([#49](https://github.com/iblmediaconsultancy/crm/issues/49)) ([c99aab8](https://github.com/iblmediaconsultancy/crm/commit/c99aab8747e84721f1b984ea45b74b6807ffda24))

## [1.7.0](https://github.com/iblmediaconsultancy/crm/compare/v1.6.5...v1.7.0) (2026-10-04)


### Features

* **api:** add exact follow-up execution cohorts ([#45](https://github.com/iblmediaconsultancy/crm/issues/45)) ([44e75f7](https://github.com/iblmediaconsultancy/crm/commit/44e75f707b2dc0c5ffc0f7caec74793c6b6d0191))

## [1.6.5](https://github.com/iblmediaconsultancy/crm/compare/v1.6.4...v1.6.5) (2026-10-04)


### Fixes

* **api:** resolve LinkedIn composer after stored-thread navigation ([#42](https://github.com/iblmediaconsultancy/crm/issues/42)) ([9de09c2](https://github.com/iblmediaconsultancy/crm/commit/9de09c2a745b1168f0a02a5e9ef05625d02e4867))

## [1.6.4](https://github.com/iblmediaconsultancy/crm/compare/v1.6.3...v1.6.4) (2026-10-04)


### Fixes

* **api:** idle follow-up recovery while gates are off ([#39](https://github.com/iblmediaconsultancy/crm/issues/39)) ([3975096](https://github.com/iblmediaconsultancy/crm/commit/3975096655d02e6d7fabadb004ef2038a5f42831))

## [1.6.3](https://github.com/iblmediaconsultancy/crm/compare/v1.6.2...v1.6.3) (2026-10-03)


### Fixes

* **api:** gate outbound claims before leasing ([#36](https://github.com/iblmediaconsultancy/crm/issues/36)) ([d16b996](https://github.com/iblmediaconsultancy/crm/commit/d16b99650a32e56d0d68a74bcd83ddf24677f05a))

## [1.6.2](https://github.com/iblmediaconsultancy/crm/compare/v1.6.1...v1.6.2) (2026-10-03)


### Fixes

* **app:** keep workspace label import client-safe ([#33](https://github.com/iblmediaconsultancy/crm/issues/33)) ([c9123cc](https://github.com/iblmediaconsultancy/crm/commit/c9123ccb7ad7d8b255c5f798b246c327978d0308))

## [1.6.1](https://github.com/iblmediaconsultancy/crm/compare/v1.6.0...v1.6.1) (2026-10-03)


### Fixes

* **api:** resolve LinkedIn replies through stored threads ([#22](https://github.com/iblmediaconsultancy/crm/issues/22)) ([cb7d19f](https://github.com/iblmediaconsultancy/crm/commit/cb7d19fedc89d3ee428069bbaa1b0a383237645f))

## [1.6.0](https://github.com/iblmediaconsultancy/crm/compare/v1.5.9...v1.6.0) (2026-10-03)


### Features

* **atlas:** restore authenticated Email dispatch readiness ([789e8ce](https://github.com/iblmediaconsultancy/crm/commit/789e8ced1d559bc4b415d5ba9ab07d5b77eadc97))

## [1.5.9](https://github.com/iblmediaconsultancy/crm/compare/v1.5.8...v1.5.9) (2026-10-02)


### Fixes

* **atlas:** restore authenticated email authorization flow ([cadb50e](https://github.com/iblmediaconsultancy/crm/commit/cadb50eb63c019cb541dfc32229f29de6f72a956))
* **atlas:** restore authenticated email authorization flow ([2bbdd5c](https://github.com/iblmediaconsultancy/crm/commit/2bbdd5c8b077d5c04a4e912890f7429c20e29d0b))
* **db:** seed workspace before Atlas membership ([9a9896c](https://github.com/iblmediaconsultancy/crm/commit/9a9896c659cc0c77410b2fa8713c5b3fb340916d))

## [1.5.8](https://github.com/iblmediaconsultancy/crm/compare/v1.5.7...v1.5.8) (2026-10-01)


### Fixes

* ignore LinkedIn recommendation headings during profile verification ([1cd364a](https://github.com/iblmediaconsultancy/crm/commit/1cd364add6fbe8a01bc55a5edda9bf7bb031557c))

## [1.5.7](https://github.com/iblmediaconsultancy/crm/compare/v1.5.6...v1.5.7) (2026-10-01)


### Fixes

* harden LinkedIn profile and conversation resolution ([14509a8](https://github.com/iblmediaconsultancy/crm/commit/14509a894b342b2c205e807da1405d519bbbb94e))

## [1.5.6](https://github.com/iblmediaconsultancy/crm/compare/v1.5.5...v1.5.6) (2026-09-30)


### Fixes

* **api:** wait for stable LinkedIn composer hydration ([ac3ffcc](https://github.com/iblmediaconsultancy/crm/commit/ac3ffcc8e2e94dc91f238dc2bc0711737a8ca69c))

## [1.5.5](https://github.com/iblmediaconsultancy/crm/compare/v1.5.4...v1.5.5) (2026-09-30)


### Fixes

* **api:** scope LinkedIn history to resolved composer ([dca90ac](https://github.com/iblmediaconsultancy/crm/commit/dca90ac0a58c90e4b71859ede7f3e4fb353cc373))

## [1.5.4](https://github.com/iblmediaconsultancy/crm/compare/v1.5.3...v1.5.4) (2026-09-28)


### Fixes

* **linkedin:** reconcile profile and member identities ([341c382](https://github.com/iblmediaconsultancy/crm/commit/341c3820a23eb3643c578a1a56bde0ec24441827))

## [1.5.3](https://github.com/iblmediaconsultancy/crm/compare/v1.5.2...v1.5.3) (2026-09-28)


### Fixes

* **linkedin:** stabilize browser recipient resolution ([cec1a68](https://github.com/iblmediaconsultancy/crm/commit/cec1a68d8b6dd9c4078b685ddabb411b56266ba1))

## [1.5.2](https://github.com/iblmediaconsultancy/crm/compare/v1.5.1...v1.5.2) (2026-09-27)


### Fixes

* **linkedin:** harden history checks and quota rollover ([57e0106](https://github.com/iblmediaconsultancy/crm/commit/57e0106e072fb0d0f70f90bf8a710f2e73c60467))

## [1.5.1](https://github.com/trycompai/crm/compare/v1.5.0...v1.5.1) (2026-08-08)


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ A [Turborepo](https://turborepo.dev) monorepo on [Bun](https://bun.com), deploye
| | |
| --- | --- |
| **Agent** | [eve](https://eve.dev) — durable sessions, tools, skills, schedules, sandboxes |
| **Model** | [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) — no provider SDK, and OIDC on Vercel means no key to manage |
| **Model** | [Google Gemini](https://deepmind.google/models/gemini/flash/) through [`@ai-sdk/google`](https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai) — fixed `gemini-3.6-flash` |
| **Sandbox** | [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) in production, Docker or microsandbox locally |
| **Front end** | [Next.js](https://nextjs.org) App Router · [shadcn/ui](https://ui.shadcn.com) · [nuqs](https://nuqs.dev) for URL state |
| **API** | [NestJS](https://nestjs.com) with [nestjs-trpc](https://nestjs-trpc.io) — HTTP, auth, tRPC, mailbox sync |
Expand Down
28 changes: 28 additions & 0 deletions adrs/ibl/0001-comp-release-pin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ADR 0001: Pin the Comp release foundation

- Status: Accepted for Phase 0
- Date: 2026-08-08

## Context

Comp's `release` branch resolves to `c26a08d63db7d22e86bcdfe76872c86c7f640ea1`. Tag `v1.4.0` resolves to `db65dd6e2445826d7af9df2893f5f5227c0127bb`; both commits have tree `81b51d5d1191b5e6047a384fdcaa6ca4d7304865`.

## Decision

Use the release-promotion commit `c26a08d63db7d22e86bcdfe76872c86c7f640ea1` as the immutable V2 base and preserve the upstream MIT notice.

## Alternatives

Tag `v1.4.0`, a moving release branch, and latest main were rejected because the approved baseline names the promotion commit and reproducibility requires a full SHA.

## Consequences

Upstream updates require an explicit review and new ADR. Security fixes do not arrive automatically.

## Security invariants

Build inputs must resolve to the recorded commit and tree. No dependency or source is fetched from V1.

## Reversal conditions

Reverse only after reviewing a newer Comp release, its migration delta, licenses, and Phase 0 security gates.
28 changes: 28 additions & 0 deletions adrs/ibl/0002-prisma-postgres.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ADR 0002: Dedicated Prisma and PostgreSQL database

- Status: Accepted for Phase 0
- Date: 2026-08-08

## Context

Comp already uses Prisma 7 and PostgreSQL. V2 needs transactional migrations, row-level mailbox controls, and durable row leasing.

## Decision

Use a dedicated V2 PostgreSQL database managed through Prisma migrations. Phase 0 uses an isolated database, port, role, and volume.

## Alternatives

Sharing V1 Supabase, SQLite, and a second ORM were rejected because they weaken isolation or add divergence.

## Consequences

RLS SQL remains a reviewed migration concern alongside Prisma models. Release automation must run migrations explicitly.

## Security invariants

V1 and V2 never share runtime credentials, schemas, roles, or connection pools. Application traffic uses a non-owner role.

## Reversal conditions

Reverse if Comp changes its supported persistence layer or PostgreSQL cannot meet a proven requirement.
Loading
Loading