diff --git a/packages/core/src/resources/store.ts b/packages/core/src/resources/store.ts index 2d45842a..6f300c85 100644 --- a/packages/core/src/resources/store.ts +++ b/packages/core/src/resources/store.ts @@ -27,6 +27,17 @@ export interface ResourceMeta { let _initialized = false; +const DEFAULT_LEARNINGS_MD = `# Learnings + +Record user preferences, corrections, and patterns here. The agent reads this at the start of every conversation. + +## Preferences + +## Corrections + +## Patterns +`; + const DEFAULT_AGENTS_MD = `# Agent Instructions This file customizes how the AI agent behaves in this app. Edit it to add your own instructions, preferences, and context. @@ -81,20 +92,39 @@ async function ensureTable(): Promise { ) `); - // Seed default shared AGENTS.md if it doesn't exist (INSERT OR IGNORE to avoid race conditions) + // Seed default shared resources if they don't exist (INSERT OR IGNORE to avoid race conditions) const now = Date.now(); - const size = Buffer.byteLength(DEFAULT_AGENTS_MD, "utf8"); + const seedSql = isPostgres() + ? `INSERT INTO resources (id, path, owner, content, mime_type, size, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (path, owner) DO NOTHING` + : `INSERT OR IGNORE INTO resources (id, path, owner, content, mime_type, size, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`; + + // AGENTS.md — shared agent instructions + const agentsSize = Buffer.byteLength(DEFAULT_AGENTS_MD, "utf8"); await client.execute({ - sql: isPostgres() - ? `INSERT INTO resources (id, path, owner, content, mime_type, size, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (path, owner) DO NOTHING` - : `INSERT OR IGNORE INTO resources (id, path, owner, content, mime_type, size, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + sql: seedSql, args: [ crypto.randomUUID(), "AGENTS.md", SHARED_OWNER, DEFAULT_AGENTS_MD, "text/markdown", - size, + agentsSize, + now, + now, + ], + }); + + // LEARNINGS.md — shared learnings (preferences, corrections, patterns) + const learningsSize = Buffer.byteLength(DEFAULT_LEARNINGS_MD, "utf8"); + await client.execute({ + sql: seedSql, + args: [ + crypto.randomUUID(), + "LEARNINGS.md", + SHARED_OWNER, + DEFAULT_LEARNINGS_MD, + "text/markdown", + learningsSize, now, now, ], diff --git a/packages/core/src/server/auth.ts b/packages/core/src/server/auth.ts index cd443b58..193509be 100644 --- a/packages/core/src/server/auth.ts +++ b/packages/core/src/server/auth.ts @@ -196,13 +196,23 @@ const DEV_SESSION: AuthSession = { email: "local@localhost" }; /** * Get the current auth session for a request. * - * - In dev mode: always returns { email: "local@localhost" } + * - In dev mode: checks for a session cookie first (e.g. from Google OAuth), + * so the real email is used when sharing a DB with production. + * Falls back to { email: "local@localhost" } if no session cookie. * - In production with built-in auth: returns session if cookie is valid * - With custom auth (BYOA): delegates to the custom getSession */ export async function getSession(event: H3Event): Promise { - if (isDevMode()) return DEV_SESSION; - if (authDisabledMode) return DEV_SESSION; + if (isDevMode() || authDisabledMode) { + // Check for a real session cookie (created by Google OAuth callback) + // so dev and prod share the same identity on the same DB + const cookie = getCookie(event, COOKIE_NAME); + if (cookie) { + const email = await getSessionEmail(cookie); + if (email) return { email, token: cookie }; + } + return DEV_SESSION; + } if (customGetSession) return customGetSession(event); diff --git a/templates/mail/server/handlers/emails.ts b/templates/mail/server/handlers/emails.ts index 3e4e12f1..f32a83f0 100644 --- a/templates/mail/server/handlers/emails.ts +++ b/templates/mail/server/handlers/emails.ts @@ -97,11 +97,9 @@ async function getAccessToken(accountEmail: string): Promise { async function getAccountTokens( forEmail?: string, ): Promise> { - // In dev mode (local@localhost), show all accounts regardless of owner - const accounts = - forEmail && forEmail !== "local@localhost" - ? await listOAuthAccountsByOwner("google", forEmail) - : await listOAuthAccounts("google"); + const accounts = forEmail + ? await listOAuthAccountsByOwner("google", forEmail) + : await listOAuthAccounts("google"); const results: Array<{ email: string; accessToken: string }> = []; diff --git a/templates/mail/server/lib/google-auth.ts b/templates/mail/server/lib/google-auth.ts index 888dda14..ab26844f 100644 --- a/templates/mail/server/lib/google-auth.ts +++ b/templates/mail/server/lib/google-auth.ts @@ -235,8 +235,7 @@ export async function getClients( * checks only that specific account. */ export async function isConnected(forEmail?: string): Promise { - // In dev mode, check all accounts regardless of owner - if (forEmail && forEmail !== "local@localhost") { + if (forEmail) { const accounts = await listOAuthAccountsByOwner("google", forEmail); return accounts.length > 0; } @@ -264,8 +263,7 @@ export async function getAuthStatus( accountId: string; tokens: Record; }>; - // In dev mode (local@localhost), show all accounts regardless of owner - if (forEmail && forEmail !== "local@localhost") { + if (forEmail) { oauthAccounts = await listOAuthAccountsByOwner("google", forEmail); } else { oauthAccounts = await listOAuthAccounts("google");