Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 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
640a6c5
fix(api): route LinkedIn replies through stored conversation
iblmediaconsultancy Oct 1, 2026
0f375de
fix(api): normalize encoded LinkedIn thread keys
iblmediaconsultancy Oct 1, 2026
efb0fa9
fix(ci): bootstrap workspace before fresh migration replay
iblmediaconsultancy Oct 2, 2026
80234a8
fix(db): use the pinned Prisma CLI for fresh replay
iblmediaconsultancy Oct 2, 2026
9660361
fix(ci): resolve Prisma config in migration bootstrap
iblmediaconsultancy Oct 2, 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
52 changes: 44 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,24 @@ 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"
# 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.6.0"
}
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
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,70 @@
# Changelog

## [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.
28 changes: 28 additions & 0 deletions adrs/ibl/0003-better-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ADR 0003: Better Auth with invite and reset migration

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

## Context

Comp already uses Better Auth. Password hashes and active sessions are not assumed portable from V1.

## Decision

Keep Better Auth. Import user identity records through repeatable ETL, then use controlled invitations or password resets and revoke legacy sessions.

## Alternatives

Copying password/session material and retaining V1 authentication were rejected because compatibility and revocation cannot be guaranteed.

## Consequences

Users may complete a one-time activation flow. Phase 1 must design invitation expiry, reset delivery, audit, and support procedures.

## Security invariants

Authenticated IDs are server verified; roles never grant mailbox access; client identity claims never override the session.

## Reversal conditions

Reverse if a reviewed identity provider integration provides safer migration and equivalent Better Auth compatibility.
28 changes: 28 additions & 0 deletions adrs/ibl/0004-mailbox-isolation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ADR 0004: Owner-only mailbox isolation

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

## Context

Email bodies are private user data. CRM administration and mailbox delegation are different authorities.

## Decision

Enforce owner or explicit read-grant access in both repositories and PostgreSQL RLS. Admin is not a mailbox permission. Workers receive one leased mailbox scope.

## Alternatives

Workspace-wide visibility, Admin bypass, and application-only filtering were rejected as excessive privilege or single-layer controls.

## Consequences

Cross-mailbox reporting requires purpose-built, privacy-reviewed projections. Support cannot inspect bodies by default.

## Security invariants

Unscoped roles see zero rows. Any cross-user body exposure is a release blocker. Worker context cannot enumerate other mailboxes.

## Reversal conditions

Only an explicit product/legal decision with a new threat model, auditable consent, and equivalent database enforcement can reverse this ADR.
Loading