This commit is contained in:
ZacharyZhang-NY committed 2026-03-17 12:31:06 -04:00
1 parent 54bcc77ef7
commit 7a3db27ffa
33 files changed
+5906 -3089

No files matched your search

+1 -7
View File
@@ -1,13 +1,7 @@
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
/**
* Lightweight HMAC-based agent token system.
* Tokens are short-lived and scoped to a specific agent + company + run.
* No external JWT library needed.
*/
const SIGNING_KEY = randomBytes(32);
const TOKEN_TTL_MS = 12 * 60 * 60 * 1000; // 12 hours
const TOKEN_TTL_MS = 48 * 60 * 60 * 1000; // 48 hours
interface TokenPayload {
agentId: string;
+422 -144
View File
File diff suppressed because it is too large. Load diff
+176 -77
View File
@@ -3,19 +3,10 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import log from "electron-log/main.js";
/**
* Browser Automation Manager — Electron-native edition.
*
* Uses Electron's own BrowserWindow and persistent sessions instead of
* external Playwright, so it works in packaged apps with zero extra deps.
*
* Each social account gets its own Electron session partition so cookies,
* localStorage, and login state are isolated and persisted across app restarts.
*/
export interface BrowserAction {
id: string;
socialAccountId: string;
platform: string;
actionType: string;
payload: Record<string, unknown>;
}
@@ -33,56 +24,116 @@ interface QueuedAction {
reject: (error: Error) => void;
}
interface PlatformRule {
loginUrl: string;
authCheckUrl: string;
allowedHosts: string[];
cookieOrigins: string[];
unauthenticatedUrlMarkers: string[];
}
const PLATFORM_RULES: Record<string, PlatformRule> = {
twitter: {
loginUrl: "https://x.com/i/flow/login",
authCheckUrl: "https://x.com/home",
allowedHosts: ["x.com", "twitter.com"],
cookieOrigins: ["https://x.com", "https://twitter.com"],
unauthenticatedUrlMarkers: ["/i/flow/login", "/login"],
},
linkedin: {
loginUrl: "https://www.linkedin.com/login",
authCheckUrl: "https://www.linkedin.com/feed/",
allowedHosts: ["linkedin.com"],
cookieOrigins: ["https://www.linkedin.com"],
unauthenticatedUrlMarkers: ["/login", "/checkpoint"],
},
instagram: {
loginUrl: "https://www.instagram.com/accounts/login/",
authCheckUrl: "https://www.instagram.com/",
allowedHosts: ["instagram.com"],
cookieOrigins: ["https://www.instagram.com"],
unauthenticatedUrlMarkers: ["/accounts/login"],
},
facebook: {
loginUrl: "https://www.facebook.com/login",
authCheckUrl: "https://www.facebook.com/",
allowedHosts: ["facebook.com"],
cookieOrigins: ["https://www.facebook.com"],
unauthenticatedUrlMarkers: ["/login"],
},
reddit: {
loginUrl: "https://www.reddit.com/login",
authCheckUrl: "https://www.reddit.com/",
allowedHosts: ["reddit.com"],
cookieOrigins: ["https://www.reddit.com"],
unauthenticatedUrlMarkers: ["/login"],
},
youtube: {
loginUrl: "https://accounts.google.com/signin",
authCheckUrl: "https://www.youtube.com/",
allowedHosts: ["youtube.com", "google.com"],
cookieOrigins: ["https://www.youtube.com", "https://accounts.google.com"],
unauthenticatedUrlMarkers: ["/signin", "/ServiceLogin"],
},
tiktok: {
loginUrl: "https://www.tiktok.com/login",
authCheckUrl: "https://www.tiktok.com/foryou",
allowedHosts: ["tiktok.com"],
cookieOrigins: ["https://www.tiktok.com"],
unauthenticatedUrlMarkers: ["/login"],
},
bluesky: {
loginUrl: "https://bsky.app",
authCheckUrl: "https://bsky.app/",
allowedHosts: ["bsky.app"],
cookieOrigins: ["https://bsky.app"],
unauthenticatedUrlMarkers: ["/login"],
},
other: {
loginUrl: "https://google.com",
authCheckUrl: "https://google.com",
allowedHosts: ["google.com"],
cookieOrigins: ["https://google.com"],
unauthenticatedUrlMarkers: ["/login"],
},
};
export class BrowserManager {
private screenshotDir: string;
private queues = new Map<string, QueuedAction[]>();
private processing = new Set<string>();
/** Track open login windows so we don't open duplicates */
private loginWindows = new Map<string, BrowserWindow>();
private initializedSessions = new Set<string>();
constructor(profileDir: string) {
this.screenshotDir = join(profileDir, "browser-screenshots");
if (!existsSync(this.screenshotDir)) mkdirSync(this.screenshotDir, { recursive: true });
}
/**
* Get a persistent Electron session for a social account.
* Using `persist:` prefix ensures data survives app restarts.
*/
private getSession(socialAccountId: string): Electron.Session {
return session.fromPartition(`persist:social-${socialAccountId}`);
const partition = `persist:social-${socialAccountId}`;
const socialSession = session.fromPartition(partition);
if (!this.initializedSessions.has(partition)) {
socialSession.setPermissionCheckHandler(() => false);
socialSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
this.initializedSessions.add(partition);
}
return socialSession;
}
/**
* Launch a real browser window for manual login.
* The window uses a persistent session so cookies are saved.
* Returns a promise that resolves when the user closes the window.
*/
async launchLoginBrowser(
socialAccountId: string,
platform: string,
loginUrl?: string,
): Promise<BrowserActionResult> {
// Don't open duplicate login windows for the same account
const existing = this.loginWindows.get(socialAccountId);
if (existing && !existing.isDestroyed()) {
existing.focus();
return { success: true, summary: "Login window already open", screenshotPath: null, error: null };
}
const defaultUrls: Record<string, string> = {
twitter: "https://x.com/i/flow/login",
linkedin: "https://www.linkedin.com/login",
instagram: "https://www.instagram.com/accounts/login/",
facebook: "https://www.facebook.com/login",
reddit: "https://www.reddit.com/login",
youtube: "https://accounts.google.com/signin",
tiktok: "https://www.tiktok.com/login",
bluesky: "https://bsky.app",
other: "https://google.com",
};
const url = loginUrl || defaultUrls[platform] || defaultUrls.other;
const rules = this.getPlatformRules(platform);
const url = loginUrl || rules.loginUrl;
const ses = this.getSession(socialAccountId);
log.info(`[browser] Opening login window for ${platform} → ${url}`);
@@ -93,7 +144,6 @@ export class BrowserManager {
title: `Login to ${platform} — AgentCompany`,
webPreferences: {
session: ses,
// Standard web page — no node integration, no preload
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
@@ -102,43 +152,32 @@ export class BrowserManager {
this.loginWindows.set(socialAccountId, win);
// Remove menu bar for a cleaner look
win.setMenuBarVisibility(false);
// Load the login page
try {
await win.loadURL(url);
} catch (err) {
log.error(`[browser] Failed to load ${url}: ${err}`);
// Don't fail — the page might redirect or have an expected error
}
// Wait for the user to close the window
return new Promise<BrowserActionResult>((resolve) => {
win.on("closed", () => {
this.loginWindows.delete(socialAccountId);
log.info(`[browser] Login window closed for ${platform} — session saved`);
resolve({
success: true,
summary: `Login session saved for ${platform}`,
screenshotPath: null,
error: null,
void this.verifyLoginSession(socialAccountId, platform).then((verified) => {
log.info(`[browser] Login window closed for ${platform} — verified=${verified}`);
resolve({
success: verified,
summary: verified
? `Authenticated session detected for ${platform}`
: `No authenticated ${platform} session detected`,
screenshotPath: null,
error: verified ? null : `Authentication for ${platform} was not completed.`,
});
});
});
});
}
/**
* Check if browser automation is available.
* Always true — we use Electron's own Chromium.
*/
async checkPlaywright(): Promise<boolean> {
return true;
}
/**
* Queue a browser action for execution.
*/
async executeAction(action: BrowserAction): Promise<BrowserActionResult> {
return new Promise((resolve, reject) => {
const queue = this.queues.get(action.socialAccountId) ?? [];
@@ -166,17 +205,14 @@ export class BrowserManager {
this.processing.delete(socialAccountId);
}
/**
* Execute a single browser action using an Electron BrowserWindow.
*/
private async runAction(action: BrowserAction): Promise<BrowserActionResult> {
const ses = this.getSession(action.socialAccountId);
const payload = action.payload;
const screenshotPath = join(this.screenshotDir, `${action.id}.png`);
const platform = action.platform;
log.info(`[browser] Executing ${action.actionType} for account ${action.socialAccountId}`);
log.info(`[browser] Executing ${action.actionType} for ${platform}/${action.socialAccountId}`);
// Create a visible browser window for the action
const win = new BrowserWindow({
width: 1280,
height: 860,
@@ -197,6 +233,7 @@ export class BrowserManager {
switch (action.actionType) {
case "navigate": {
const url = String(payload.url || "https://google.com");
this.assertAllowedUrl(platform, url, "navigate");
await win.loadURL(url);
await this.waitForLoad(win);
const title = await win.webContents.executeJavaScript("document.title");
@@ -205,7 +242,6 @@ export class BrowserManager {
}
case "post": {
const platform = String(payload.platform || "twitter");
const content = String(payload.content || "");
const postUrl = this.getComposeUrl(platform);
await win.loadURL(postUrl);
@@ -213,7 +249,6 @@ export class BrowserManager {
await this.delay(2000);
if (platform === "twitter") {
// Type into tweet composer
await win.webContents.executeJavaScript(`
(async () => {
const editor = document.querySelector('[data-testid="tweetTextarea_0"], [role="textbox"]');
@@ -224,7 +259,6 @@ export class BrowserManager {
})()
`);
await this.delay(1000);
// Click post button
await win.webContents.executeJavaScript(`
(() => {
const btn = document.querySelector('[data-testid="tweetButton"], [data-testid="tweetButtonInline"]');
@@ -264,6 +298,7 @@ export class BrowserManager {
case "reply": {
const targetUrl = String(payload.targetUrl || "");
this.assertAllowedUrl(platform, targetUrl, "reply");
const replyContent = String(payload.content || "");
await win.loadURL(targetUrl);
await this.waitForLoad(win);
@@ -292,6 +327,7 @@ export class BrowserManager {
case "like": {
const targetUrl = String(payload.targetUrl || "");
this.assertAllowedUrl(platform, targetUrl, "like");
await win.loadURL(targetUrl);
await this.waitForLoad(win);
await this.delay(2000);
@@ -308,6 +344,7 @@ export class BrowserManager {
case "follow": {
const targetUrl = String(payload.targetUrl || payload.profileUrl || "");
this.assertAllowedUrl(platform, targetUrl, "follow");
await win.loadURL(targetUrl);
await this.waitForLoad(win);
await this.delay(2000);
@@ -323,14 +360,13 @@ export class BrowserManager {
}
case "browse_feed": {
const platform = String(payload.platform || "twitter");
const feedUrl = String(payload.url || this.getFeedUrl(platform));
this.assertAllowedUrl(platform, feedUrl, "browse_feed");
const maxPosts = Number(payload.maxPosts || 10);
await win.loadURL(feedUrl);
await this.waitForLoad(win);
await this.delay(3000);
// Scroll to load content
for (let i = 0; i < 3; i++) {
await win.webContents.executeJavaScript("window.scrollBy(0, window.innerHeight)");
await this.delay(1500);
@@ -348,8 +384,8 @@ export class BrowserManager {
case "search": {
const query = String(payload.query || "");
const platform = String(payload.platform || "twitter");
const searchUrl = this.getSearchUrl(platform, query);
this.assertAllowedUrl(platform, searchUrl, "search");
await win.loadURL(searchUrl);
await this.waitForLoad(win);
await this.delay(3000);
@@ -367,6 +403,7 @@ export class BrowserManager {
case "screenshot": {
const url = String(payload.url || "about:blank");
if (url !== "about:blank") {
this.assertAllowedUrl(platform, url, "screenshot");
await win.loadURL(url);
await this.waitForLoad(win);
await this.delay(2000);
@@ -377,10 +414,9 @@ export class BrowserManager {
}
default:
summary = `Unknown action type: ${action.actionType}`;
throw new Error(`Unsupported browser action type: ${action.actionType}`);
}
// Capture screenshot
try {
const image: NativeImage = await win.webContents.capturePage();
if (!image.isEmpty()) {
@@ -390,7 +426,6 @@ export class BrowserManager {
log.warn(`[browser] Screenshot failed: ${err}`);
}
// Close the action window after a brief pause so user can see result
await this.delay(1000);
if (!win.isDestroyed()) win.close();
@@ -401,7 +436,6 @@ export class BrowserManager {
error: null,
};
} catch (error) {
// Try to capture error screenshot
try {
const image: NativeImage = await win.webContents.capturePage();
if (!image.isEmpty()) writeFileSync(screenshotPath, image.toPNG());
@@ -420,8 +454,6 @@ export class BrowserManager {
}
}
// ── Helpers ──────────────────────────────────────────────────────
private delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
@@ -440,6 +472,69 @@ export class BrowserManager {
});
}
private getPlatformRules(platform: string): PlatformRule {
return PLATFORM_RULES[platform] ?? PLATFORM_RULES.other;
}
private assertAllowedUrl(platform: string, rawUrl: string, actionType: string) {
const allowedHosts = this.getPlatformRules(platform).allowedHosts;
let url: URL;
try {
url = new URL(rawUrl);
} catch {
throw new Error(`Invalid URL for ${actionType}: ${rawUrl}`);
}
const host = url.hostname.toLowerCase();
const isAllowed = allowedHosts.some((allowedHost) => host === allowedHost || host.endsWith(`.${allowedHost}`));
if (!isAllowed) {
throw new Error(`Navigation blocked for ${actionType}. ${host} is outside the allowed ${platform} domains.`);
}
}
private async verifyLoginSession(socialAccountId: string, platform: string): Promise<boolean> {
const ses = this.getSession(socialAccountId);
const rules = this.getPlatformRules(platform);
const cookieSets = await Promise.all(
rules.cookieOrigins.map(async (origin) => {
try {
return await ses.cookies.get({ url: origin });
} catch {
return [];
}
}),
);
const hasCookies = cookieSets.some((cookies) => cookies.length > 0);
if (!hasCookies) {
return false;
}
const verificationWindow = new BrowserWindow({
show: false,
webPreferences: {
session: ses,
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
},
});
try {
await verificationWindow.loadURL(rules.authCheckUrl);
await this.waitForLoad(verificationWindow, 10000);
const finalUrl = verificationWindow.webContents.getURL();
const finalUrlLower = finalUrl.toLowerCase();
const bouncedToLogin = rules.unauthenticatedUrlMarkers.some((marker) => finalUrlLower.includes(marker.toLowerCase()));
return !bouncedToLogin;
} catch (error) {
log.warn(`[browser] Login verification failed for ${platform}: ${error instanceof Error ? error.message : String(error)}`);
return false;
} finally {
if (!verificationWindow.isDestroyed()) {
verificationWindow.close();
}
}
}
private getComposeUrl(platform: string): string {
const urls: Record<string, string> = {
twitter: "https://x.com/compose/post",
@@ -448,8 +543,10 @@ export class BrowserManager {
facebook: "https://www.facebook.com/",
reddit: "https://www.reddit.com/submit",
bluesky: "https://bsky.app/",
youtube: "https://www.youtube.com/",
tiktok: "https://www.tiktok.com/",
};
return urls[platform] || "https://google.com";
return urls[platform] || this.getPlatformRules(platform).loginUrl;
}
private getFeedUrl(platform: string): string {
@@ -457,10 +554,13 @@ export class BrowserManager {
twitter: "https://x.com/home",
linkedin: "https://www.linkedin.com/feed/",
instagram: "https://www.instagram.com/",
facebook: "https://www.facebook.com/",
reddit: "https://www.reddit.com/",
bluesky: "https://bsky.app/",
youtube: "https://www.youtube.com/",
tiktok: "https://www.tiktok.com/",
};
return urls[platform] || "https://google.com";
return urls[platform] || this.getPlatformRules(platform).loginUrl;
}
private getSearchUrl(platform: string, query: string): string {
@@ -474,7 +574,6 @@ export class BrowserManager {
}
shutdown() {
// Close any open login windows
for (const [, win] of this.loginWindows) {
if (!win.isDestroyed()) win.close();
}
-4
View File
@@ -263,10 +263,6 @@ const CONNECTORS: ConnectorDefinition[] = [
},
];
export function listConnectorDefinitions(): ConnectorDefinition[] {
return CONNECTORS;
}
export function getConnectorDefinition(id: ConnectorId): ConnectorDefinition {
const definition = CONNECTORS.find((entry) => entry.id === id);
if (!definition) {
+674 -268
View File
File diff suppressed because it is too large. Load diff
+1861 -764
View File
File diff suppressed because it is too large. Load diff
+12 -36
View File
@@ -2,37 +2,19 @@ import { runningProcesses } from "@agentcompany/adapter-utils/server-utils";
import { getConnectorDefinition } from "@main/connectors";
import type { ConnectorId, RunStatus } from "@shared/types";
/**
* Environment variables allowed to propagate from the host process to connector
* child processes. Any env var NOT in this list is stripped. Secret bindings
* resolved from the vault are always injected regardless of this allowlist.
*/
const ENV_ALLOWLIST: ReadonlySet<string> = new Set([
// PATH is required for command resolution
"PATH",
// Basic locale / terminal
"HOME", "USER", "SHELL", "LANG", "LC_ALL", "LC_CTYPE", "TERM", "TMPDIR",
// macOS-specific
"DEVELOPER_DIR", "SDKROOT",
// Node
"NODE_ENV", "NODE_OPTIONS",
// Git
"GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL",
"GIT_SSH_COMMAND", "GIT_ASKPASS", "GIT_TERMINAL_PROMPT",
// XDG
"XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_RUNTIME_DIR",
// Platform / display (Linux)
"DISPLAY", "WAYLAND_DISPLAY", "DBUS_SESSION_BUS_ADDRESS",
// Windows
"APPDATA", "LOCALAPPDATA", "USERPROFILE", "SYSTEMROOT", "COMSPEC", "PROGRAMFILES",
// AgentCompany injected
"AGENT_COMPANY_API_URL", "AGENT_COMPANY_API_KEY", "AGENT_COMPANY_RUN_ID",
]);
/**
* Build a sanitized environment for a connector child process.
* Only allowlisted host vars + explicitly resolved secret bindings are included.
*/
function buildSanitizedEnv(
hostEnv: Record<string, string>,
secretBindings: Record<string, string>,
@@ -43,25 +25,18 @@ function buildSanitizedEnv(
result[key] = hostEnv[key];
}
}
// Secret bindings always override — they are resolved from the vault
Object.assign(result, secretBindings);
return result;
}
/**
* Build a log redaction function that masks any secret values present in the env.
* Only values that look like secrets (length >= 8, not PATH-like) are redacted.
*/
function buildRedactor(env: Record<string, string>): (text: string) => string {
const sensitivePatterns: string[] = [];
for (const [key, value] of Object.entries(env)) {
// Skip short values, PATH, HOME, and other system vars unlikely to be secrets
if (value.length < 8) continue;
if (["PATH", "HOME", "USER", "SHELL", "LANG", "TMPDIR", "TERM"].includes(key)) continue;
sensitivePatterns.push(value);
}
if (sensitivePatterns.length === 0) return (text) => text;
// Sort by length descending so longer patterns are replaced first
sensitivePatterns.sort((a, b) => b.length - a.length);
return (text: string) => {
let result = text;
@@ -85,14 +60,13 @@ interface RunJob {
connectorId: ConnectorId;
workspaceId: string | null;
workspacePath: string | null;
workspaceMode: "exclusive" | "shared" | "none";
command: string;
model: string | null;
thinkingEffort: string | null;
env: Record<string, string>;
sessionParams: Record<string, unknown> | null;
/** Rich system prompt with API instructions for autonomous operation */
systemPrompt: string | null;
/** Heartbeat trigger that caused this run */
trigger: string | null;
}
@@ -134,6 +108,9 @@ function post(message: ChildMessage) {
}
function workspaceKey(job: RunJob) {
if (job.workspaceMode !== "exclusive") {
return null;
}
return job.workspaceId ?? job.workspacePath ?? `task:${job.taskId}`;
}
@@ -152,7 +129,8 @@ async function maybeStartQueuedRuns() {
const readyJobs = [...queue];
for (const job of readyJobs) {
if (activeByAgent.has(job.agentId)) continue;
if (activeByWorkspace.has(workspaceKey(job))) continue;
const lockKey = workspaceKey(job);
if (lockKey && activeByWorkspace.has(lockKey)) continue;
const index = queue.findIndex((entry) => entry.runId === job.runId);
if (index >= 0) queue.splice(index, 1);
void startRun(job);
@@ -163,7 +141,9 @@ async function startRun(job: RunJob) {
const definition = getConnectorDefinition(job.connectorId);
const lockKey = workspaceKey(job);
activeByAgent.set(job.agentId, job.runId);
activeByWorkspace.set(lockKey, job.runId);
if (lockKey) {
activeByWorkspace.set(lockKey, job.runId);
}
post({
type: "run-status",
runId: job.runId,
@@ -171,7 +151,6 @@ async function startRun(job: RunJob) {
message: `Running with ${definition.label}.`,
});
// Build sanitized env: only allowlisted host vars + resolved secret bindings
const sanitizedEnv = buildSanitizedEnv(
Object.fromEntries(
Object.entries(process.env).filter((e): e is [string, string] => typeof e[1] === "string"),
@@ -179,7 +158,6 @@ async function startRun(job: RunJob) {
job.env,
);
// Build log redactor from secret bindings to prevent accidental secret leakage
const redact = buildRedactor(job.env);
try {
@@ -205,13 +183,9 @@ async function startRun(job: RunJob) {
cwd: job.workspacePath ?? process.cwd(),
model: job.model,
env: sanitizedEnv,
// Claude: thinking effort
effort: job.connectorId === "claude_local" ? (job.thinkingEffort ?? "") : "",
// Codex: reasoning effort
modelReasoningEffort: job.connectorId === "codex_local" ? (job.thinkingEffort ?? "") : "",
// Claude: run without permission prompts — required for headless operation
dangerouslySkipPermissions: true,
// Codex: bypass sandbox and approval prompts, skip git trust check
dangerouslyBypassApprovalsAndSandbox: true,
extraArgs: job.connectorId === "codex_local" ? ["--skip-git-repo-check"] : [],
},
@@ -276,7 +250,9 @@ async function startRun(job: RunJob) {
} finally {
cancelledRuns.delete(job.runId);
activeByAgent.delete(job.agentId);
activeByWorkspace.delete(lockKey);
if (lockKey) {
activeByWorkspace.delete(lockKey);
}
void maybeStartQueuedRuns();
}
}
+573 -317
View File
File diff suppressed because it is too large. Load diff
+1 -175
View File
@@ -1,10 +1,5 @@
import type { CompanyTemplate, DepartmentId } from "@shared/types";
import type { CompanyTemplate } from "@shared/types";
/**
* Pre-built company templates for one-click company setup.
* Each template defines a complete organizational structure with
* agents, goals, and role definitions.
*/
export const COMPANY_TEMPLATES: CompanyTemplate[] = [
{
id: "software-startup",
@@ -313,10 +308,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── Fortune 500 Enterprise ────────────────────────────────────────────
// A truly realistic, fully-staffed company with 37 agents across 12
// departments. Modeled after a mature tech company or Fortune 500 org
// with every function needed to operate at scale.
{
id: "fortune500-enterprise",
name: "Fortune 500 Enterprise",
@@ -337,11 +328,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
{ id: "research", name: "Research & Innovation", description: "Applied research, competitive intelligence, prototyping, and publication of thought-leadership papers." },
],
agents: [
// ────────────────────────────────────────────────────────────────────
// EXECUTIVE SUITE — indices 0-4
// ────────────────────────────────────────────────────────────────────
{
// index 0
name: "Victoria Sterling",
role: "CEO",
title: "Chief Executive Officer",
@@ -352,7 +339,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 1
name: "James Whitfield",
role: "COO",
title: "Chief Operating Officer",
@@ -363,7 +349,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 220,
},
{
// index 2
name: "Marcus Chen",
role: "CTO",
title: "Chief Technology Officer",
@@ -374,7 +359,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 3
name: "Priya Sharma",
role: "CFO",
title: "Chief Financial Officer",
@@ -385,7 +369,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 4
name: "Nadira Osei-Mensah",
role: "CHRO",
title: "Chief Human Resources Officer",
@@ -396,11 +379,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
// ────────────────────────────────────────────────────────────────────
// ENGINEERING — indices 5-12
// ────────────────────────────────────────────────────────────────────
{
// index 5
name: "Elena Rodriguez",
role: "VP Engineering",
title: "Vice President of Engineering",
@@ -411,7 +390,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 220,
},
{
// index 6
name: "Tomasz Kowalski",
role: "Principal Engineer",
title: "Principal Software Engineer",
@@ -422,7 +400,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 280,
},
{
// index 7
name: "Kai Nakamura",
role: "Senior Backend Engineer",
title: "Senior Backend Engineer",
@@ -433,7 +410,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 8
name: "Sofia Andersson",
role: "Senior Frontend Engineer",
title: "Senior Frontend Engineer",
@@ -444,7 +420,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 9
name: "Omar Hassan",
role: "Platform / DevOps Engineer",
title: "Senior Platform Engineer",
@@ -455,7 +430,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 10
name: "Yuki Tanaka",
role: "QA Lead",
title: "Quality Assurance Lead",
@@ -466,7 +440,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 11
name: "Amina Diallo",
role: "Security Engineer",
title: "Application Security Engineer",
@@ -477,7 +450,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 12
name: "Ravi Krishnamurthy",
role: "Data Engineer",
title: "Senior Data Engineer",
@@ -488,11 +460,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
// ────────────────────────────────────────────────────────────────────
// PRODUCT & DESIGN — indices 13-16
// ────────────────────────────────────────────────────────────────────
{
// index 13
name: "Amara Okafor",
role: "VP Product",
title: "Vice President of Product",
@@ -503,7 +471,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 14
name: "Daniel Okonkwo",
role: "Senior Product Manager",
title: "Senior Product Manager",
@@ -514,7 +481,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 15
name: "Ingrid Johansson",
role: "UX Director",
title: "Director of User Experience",
@@ -525,7 +491,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 16
name: "Leo Moretti",
role: "UI Designer",
title: "Senior UI Designer",
@@ -536,11 +501,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 160,
},
// ────────────────────────────────────────────────────────────────────
// MARKETING — indices 17-20
// ────────────────────────────────────────────────────────────────────
{
// index 17
name: "Jasmine Delacroix",
role: "CMO",
title: "Chief Marketing Officer",
@@ -551,7 +512,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 18
name: "Noah Park",
role: "Content Director",
title: "Director of Content",
@@ -562,7 +522,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 19
name: "Zara Ahmed",
role: "Growth Marketing Manager",
title: "Growth Marketing Manager",
@@ -573,7 +532,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 170,
},
{
// index 20
name: "Clara Dubois",
role: "Brand & PR Manager",
title: "Brand & Public Relations Manager",
@@ -584,11 +542,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 150,
},
// ────────────────────────────────────────────────────────────────────
// SALES — indices 21-23
// ────────────────────────────────────────────────────────────────────
{
// index 21
name: "Daniel Brooks",
role: "VP Sales",
title: "Vice President of Sales",
@@ -599,7 +553,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 22
name: "Grace Kimura",
role: "Enterprise Account Executive",
title: "Enterprise Account Executive",
@@ -610,7 +563,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 23
name: "Marcus Adebayo",
role: "SDR Manager",
title: "Sales Development Representative Manager",
@@ -621,11 +573,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 150,
},
// ────────────────────────────────────────────────────────────────────
// HR / PEOPLE — indices 24-26
// ────────────────────────────────────────────────────────────────────
{
// index 24
name: "Olivia Nguyen",
role: "VP People",
title: "Vice President of People",
@@ -636,7 +584,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 170,
},
{
// index 25
name: "Farid Al-Rashidi",
role: "Recruiting Lead",
title: "Head of Talent Acquisition",
@@ -647,7 +594,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 140,
},
{
// index 26
name: "Hannah Johansson",
role: "People Operations Manager",
title: "People Operations Manager",
@@ -658,11 +604,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 120,
},
// ────────────────────────────────────────────────────────────────────
// FINANCE & LEGAL — indices 27-29
// ────────────────────────────────────────────────────────────────────
{
// index 27
name: "Richard Okonkwo",
role: "VP Finance",
title: "Vice President of Finance",
@@ -673,7 +615,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 28
name: "Mei-Ling Wu",
role: "Financial Controller",
title: "Financial Controller",
@@ -684,7 +625,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 140,
},
{
// index 29
name: "Alejandro Vega",
role: "General Counsel",
title: "General Counsel",
@@ -695,11 +635,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
// ────────────────────────────────────────────────────────────────────
// CUSTOMER SUCCESS — indices 30-32
// ────────────────────────────────────────────────────────────────────
{
// index 30
name: "Aisha Williams",
role: "VP Customer Success",
title: "Vice President of Customer Success",
@@ -710,7 +646,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 31
name: "Sergei Volkov",
role: "Senior Support Engineer",
title: "Senior Technical Support Engineer",
@@ -721,7 +656,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 160,
},
{
// index 32
name: "Camila Reyes",
role: "Customer Success Manager",
title: "Customer Success Manager",
@@ -732,11 +666,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 140,
},
// ────────────────────────────────────────────────────────────────────
// OPERATIONS — indices 33-34
// ────────────────────────────────────────────────────────────────────
{
// index 33
name: "Kenji Watanabe",
role: "Director of Operations",
title: "Director of Operations",
@@ -747,7 +677,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 160,
},
{
// index 34
name: "Laura Petrov",
role: "Program Manager",
title: "Senior Program Manager",
@@ -758,11 +687,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 150,
},
// ────────────────────────────────────────────────────────────────────
// RESEARCH — indices 35-36
// ────────────────────────────────────────────────────────────────────
{
// index 35
name: "Dr. Lena Hoffmann",
role: "Director of Research",
title: "Director of Research & Innovation",
@@ -773,7 +698,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 36
name: "Dr. Aarav Mehta",
role: "Research Scientist",
title: "Research Scientist",
@@ -828,7 +752,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── E-Commerce Company ──────────────────────────────────────────────
{
id: "ecommerce-company",
name: "E-Commerce Business",
@@ -922,7 +845,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── Consulting Firm ──────────────────────────────────────────────────
{
id: "consulting-firm",
name: "Consulting Firm",
@@ -992,9 +914,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── AI Research Company ───────────────────────────────────────────────
// Modeled after organizations like OpenAI, Anthropic, and DeepMind.
// Research-first with strong engineering to ship models into production.
{
id: "ai-research-company",
name: "AI Research Company",
@@ -1013,9 +932,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
{ id: "legal", name: "Legal & Policy", description: "IP strategy, regulatory affairs, AI governance, data licensing, and compliance." },
],
agents: [
// ── Executive (0-1) ──
{
// index 0
name: "Dr. Julian Crane",
role: "CEO",
title: "Chief Executive Officer",
@@ -1026,7 +943,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 1
name: "Dr. Mei-Xing Zhou",
role: "Chief Scientist",
title: "Chief Scientist",
@@ -1036,10 +952,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 280,
},
// ── Research (2-6) ──
{
// index 2
name: "Dr. Sasha Petrov",
role: "Research Lead — Pre-training",
title: "Senior Research Scientist — Pre-training",
@@ -1050,7 +963,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 260,
},
{
// index 3
name: "Dr. Nneka Adeyemi",
role: "Research Lead — Post-training",
title: "Senior Research Scientist — RLHF & Fine-tuning",
@@ -1061,7 +973,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 260,
},
{
// index 4
name: "Dr. Hiroshi Takeda",
role: "Research Scientist — Reasoning",
title: "Research Scientist — Reasoning & Agents",
@@ -1072,7 +983,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 240,
},
{
// index 5
name: "Dr. Amara Osei",
role: "Research Scientist — Safety",
title: "Research Scientist — AI Safety",
@@ -1083,7 +993,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 240,
},
{
// index 6
name: "Lila Fernandez",
role: "Research Engineer",
title: "Senior Research Engineer",
@@ -1093,10 +1002,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 250,
},
// ── Engineering (7-10) ──
{
// index 7
name: "Rajiv Sundaram",
role: "VP Engineering",
title: "Vice President of Engineering",
@@ -1107,7 +1013,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 220,
},
{
// index 8
name: "Christine Bergstrom",
role: "ML Infrastructure Engineer",
title: "Senior ML Infrastructure Engineer",
@@ -1118,7 +1023,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 9
name: "Andre Williams",
role: "Backend Engineer",
title: "Senior Backend Engineer",
@@ -1129,7 +1033,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 10
name: "Yuna Kim",
role: "Frontend Engineer",
title: "Senior Frontend Engineer",
@@ -1139,10 +1042,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 220,
},
// ── Product & Design (11-13) ──
{
// index 11
name: "Tara Blackwell",
role: "VP Product",
title: "Vice President of Product",
@@ -1153,7 +1053,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 12
name: "Omar Khalil",
role: "Product Manager — Developer Platform",
title: "Product Manager — Developer Platform",
@@ -1164,7 +1063,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 160,
},
{
// index 13
name: "Vera Lindqvist",
role: "Product Designer",
title: "Senior Product Designer",
@@ -1174,10 +1072,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 160,
},
// ── Marketing & DevRel (14-15) ──
{
// index 14
name: "Marcus Rhodes",
role: "Head of Marketing",
title: "Head of Marketing & Communications",
@@ -1188,7 +1083,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 170,
},
{
// index 15
name: "Priya Khatri",
role: "Developer Advocate",
title: "Senior Developer Advocate",
@@ -1198,10 +1092,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 170,
},
// ── People & Finance & Legal (16-17) ──
{
// index 16
name: "Danielle Moreau",
role: "Head of People",
title: "Head of People",
@@ -1212,7 +1103,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 150,
},
{
// index 17
name: "Gabriel Souza",
role: "Head of Legal & Policy",
title: "Head of Legal & Policy",
@@ -1251,9 +1141,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── Digital Agency ────────────────────────────────────────────────────
// A full-service digital agency that designs, builds, and markets
// digital products for clients.
{
id: "digital-agency",
name: "Full-Service Digital Agency",
@@ -1268,9 +1155,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
{ id: "operations", name: "Operations", description: "Resource planning, process management, and quality assurance." },
],
agents: [
// ── Leadership (0-1) ──
{
// index 0
name: "Isabelle Fontaine",
role: "Managing Director",
title: "Managing Director",
@@ -1281,7 +1166,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 1
name: "Victor Nascimento",
role: "Account Director",
title: "Account Director",
@@ -1291,10 +1175,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 170,
},
// ── Creative & Design (2-4) ──
{
// index 2
name: "Saoirse O'Brien",
role: "Creative Director",
title: "Creative Director",
@@ -1305,7 +1186,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 3
name: "Mateo Cruz",
role: "UX/UI Designer",
title: "Senior UX/UI Designer",
@@ -1316,7 +1196,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 170,
},
{
// index 4
name: "Yuki Aoyama",
role: "Motion & Visual Designer",
title: "Motion & Visual Designer",
@@ -1326,10 +1205,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 140,
},
// ── Development (5-7) ──
{
// index 5
name: "Nadia Kowalczyk",
role: "Technical Director",
title: "Technical Director",
@@ -1340,7 +1216,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 220,
},
{
// index 6
name: "Kwame Asante",
role: "Full-Stack Developer",
title: "Senior Full-Stack Developer",
@@ -1351,7 +1226,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 7
name: "Elina Koskinen",
role: "Frontend Developer",
title: "Frontend Developer",
@@ -1361,10 +1235,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 180,
},
// ── Digital Marketing (8-10) ──
{
// index 8
name: "Tariq Benali",
role: "Head of Digital",
title: "Head of Digital Marketing",
@@ -1375,7 +1246,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 9
name: "Jessica Torres",
role: "SEO & Content Specialist",
title: "SEO & Content Specialist",
@@ -1386,7 +1256,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 150,
},
{
// index 10
name: "Ryan Gallagher",
role: "Paid Media Specialist",
title: "Paid Media Specialist",
@@ -1396,10 +1265,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 150,
},
// ── Strategy & Content (11-12) ──
{
// index 11
name: "Ananya Chakraborty",
role: "Strategy Lead",
title: "Digital Strategy Lead",
@@ -1410,7 +1276,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 12
name: "Liam O'Sullivan",
role: "Copywriter",
title: "Senior Copywriter",
@@ -1420,10 +1285,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 160,
},
// ── Operations (13) ──
{
// index 13
name: "Sandra Muller",
role: "Project Manager",
title: "Senior Project Manager",
@@ -1454,9 +1316,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
],
},
// ── B2B SaaS Company ──────────────────────────────────────────────────
// A product-led B2B SaaS company with the complete set of functions
// needed to build, sell, and support a subscription software product.
{
id: "saas-company",
name: "B2B SaaS Company",
@@ -1474,9 +1333,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
{ id: "hr", name: "People", description: "Recruiting, onboarding, culture, and people operations." },
],
agents: [
// ── Executive (0-1) ──
{
// index 0
name: "Nathan Cross",
role: "CEO",
title: "Chief Executive Officer",
@@ -1487,7 +1344,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 1
name: "Samira Rostami",
role: "CTO",
title: "Chief Technology Officer",
@@ -1497,10 +1353,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 240,
},
// ── Engineering (2-6) ──
{
// index 2
name: "Derek Johansson",
role: "VP Engineering",
title: "Vice President of Engineering",
@@ -1511,7 +1364,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 210,
},
{
// index 3
name: "Priya Venkatesh",
role: "Senior Backend Engineer",
title: "Senior Backend Engineer",
@@ -1522,7 +1374,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 250,
},
{
// index 4
name: "Luis Mendoza",
role: "Senior Frontend Engineer",
title: "Senior Frontend Engineer",
@@ -1533,7 +1384,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 240,
},
{
// index 5
name: "Anika Bergman",
role: "Platform Engineer",
title: "Senior Platform Engineer",
@@ -1544,7 +1394,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 230,
},
{
// index 6
name: "Tomoko Sato",
role: "QA Engineer",
title: "Quality Assurance Engineer",
@@ -1554,10 +1403,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 180,
},
// ── Product & Design (7-9) ──
{
// index 7
name: "Rachel Abrams",
role: "VP Product",
title: "Vice President of Product",
@@ -1568,7 +1414,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 8
name: "Jake Morrison",
role: "Product Manager",
title: "Product Manager",
@@ -1579,7 +1424,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 170,
},
{
// index 9
name: "Chloe Laurent",
role: "Product Designer",
title: "Senior Product Designer",
@@ -1589,10 +1433,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 170,
},
// ── Marketing (10-12) ──
{
// index 10
name: "Ava Richardson",
role: "VP Marketing",
title: "Vice President of Marketing",
@@ -1603,7 +1444,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 190,
},
{
// index 11
name: "Benjamin Hart",
role: "Content Marketing Manager",
title: "Content Marketing Manager",
@@ -1614,7 +1454,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 160,
},
{
// index 12
name: "Daria Volkov",
role: "Growth & Demand Gen Manager",
title: "Growth & Demand Generation Manager",
@@ -1624,10 +1463,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 160,
},
// ── Sales (13-15) ──
{
// index 13
name: "Carter Ellison",
role: "VP Sales",
title: "Vice President of Sales",
@@ -1638,7 +1474,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 200,
},
{
// index 14
name: "Nina Johansson",
role: "Enterprise Account Executive",
title: "Enterprise Account Executive",
@@ -1649,7 +1484,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 15
name: "Travis Chen",
role: "SDR Lead",
title: "Sales Development Representative Lead",
@@ -1659,10 +1493,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "claude_local",
budgetMonthlyUsd: 130,
},
// ── Customer Success & Support (16-18) ──
{
// index 16
name: "Miriam Okafor",
role: "VP Customer Success",
title: "Vice President of Customer Success",
@@ -1673,7 +1504,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 180,
},
{
// index 17
name: "Alex Petridis",
role: "Customer Success Manager",
title: "Customer Success Manager",
@@ -1684,7 +1514,6 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
budgetMonthlyUsd: 140,
},
{
// index 18
name: "Rina Tanaka",
role: "Support Engineer",
title: "Technical Support Engineer",
@@ -1694,10 +1523,7 @@ export const COMPANY_TEMPLATES: CompanyTemplate[] = [
connectorPreference: "codex_local",
budgetMonthlyUsd: 140,
},
// ── Finance & People (19) ──
{
// index 19
name: "Henrik Larsen",
role: "Head of Finance",
title: "Head of Finance",