Merge branch 'dev' into docs/microsoft-oauth-email-verification

This commit is contained in:
BilalG1 2026-06-17 16:29:20 -07:00 committed by GitHub
commit 7ae78d6e52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 56 additions and 26 deletions

View File

@ -26,8 +26,22 @@ export const POST = createSmartRouteHandler({
tsx_source: yupString().defined(),
}).defined(),
}),
handler: async ({ body }) => {
const rewriteResult = await rewriteTemplateSourceWithAI(body.template_tsx_source);
handler: async ({ body }, fullReq) => {
// Forward the caller's Hexclave/Stack auth headers so the inner AI call
// (which is a fresh HTTP request to /ai/query/generate) is authenticated
// and resolves to the authenticated model tier rather than falling back
// to the unauthenticated one.
const authHeadersMap = new Map<string, string>();
for (const [key, value] of Object.entries(fullReq.headers)) {
if (value == null) continue;
const lower = key.toLowerCase();
if (lower.startsWith("x-stack-") || lower.startsWith("x-hexclave-")) {
authHeadersMap.set(key, value.join(","));
}
}
const authHeaders: Record<string, string> = Object.fromEntries(authHeadersMap);
const rewriteResult = await rewriteTemplateSourceWithAI(body.template_tsx_source, authHeaders);
if (rewriteResult.status === "error") {
throw new KnownErrors.TemplateSourceRewriteError(rewriteResult.error);
}

View File

@ -19,31 +19,31 @@ const MODEL_SELECTION_MATRIX: Record<
dumb: {
slow: {
authenticated: { modelId: "z-ai/glm-4.5-air:free" },
unauthenticated: { modelId: "z-ai/glm-4.5-air:free" },
unauthenticated: { modelId: "nvidia/nemotron-3-super-120b-a12b" },
},
fast: {
authenticated: { modelId: "openai/gpt-oss-120b:nitro" },
unauthenticated: { modelId: "openai/gpt-oss-120b:nitro" },
unauthenticated: { modelId: "nvidia/nemotron-3-super-120b-a12b:nitro" },
},
},
smart: {
slow: {
authenticated: { modelId: "x-ai/grok-build-0.1" },
unauthenticated: { modelId: "deepseek/deepseek-v4-flash" },
authenticated: { modelId: "openai/gpt-5.5" },
unauthenticated: { modelId: "z-ai/glm-5.2" },
},
fast: {
authenticated: { modelId: "x-ai/grok-build-0.1" },
unauthenticated: { modelId: "nvidia/nemotron-3-super-120b-a12b:nitro" },
authenticated: { modelId: "openai/gpt-5.5" },
unauthenticated: { modelId: "google/gemini-3.5-flash" },
},
},
smartest: {
slow: {
authenticated: { modelId: "openai/gpt-5.5" },
unauthenticated: { modelId: "deepseek/deepseek-v4-flash" },
unauthenticated: { modelId: "z-ai/glm-5.2" },
},
fast: {
authenticated: { modelId: "openai/gpt-5.5" },
unauthenticated: { modelId: "deepseek/deepseek-v4-flash:nitro" },
unauthenticated: { modelId: "google/gemini-3.5-flash" },
},
},
};

View File

@ -13,16 +13,16 @@ function isMockMode() {
return key === MOCK_API_KEY_SENTINEL || key === "FORWARD_TO_PRODUCTION";
}
async function rewriteTemplateSourceWithCurrentAIPlumbing(templateTsxSource: string): Promise<Result<string, string>> {
async function rewriteTemplateSourceWithCurrentAIPlumbing(templateTsxSource: string, authHeaders: Record<string, string>): Promise<Result<string, string>> {
const backendUrl = getEnvVariable("NEXT_PUBLIC_STACK_API_URL");
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), AI_REQUEST_TIMEOUT_MS);
try {
const response = await fetch(`${backendUrl}/api/latest/ai/query/generate`, {
method: "POST",
headers: { "content-type": "application/json" },
headers: { "content-type": "application/json", ...authHeaders },
body: JSON.stringify({
quality: "smart",
quality: "dumb",
speed: "slow",
tools: [],
systemPrompt: "rewrite-template-source",
@ -115,7 +115,7 @@ function stripCodeFences(text: string): string {
return output;
}
export async function rewriteTemplateSourceWithAI(templateTsxSource: string): Promise<Result<string, string>> {
export async function rewriteTemplateSourceWithAI(templateTsxSource: string, authHeaders: Record<string, string>): Promise<Result<string, string>> {
if (isMockMode()) {
const mockRewrittenSource = rewriteTemplateSourceInMockMode(templateTsxSource);
const mockRenderResult = await renderEmailWithTemplate(mockRewrittenSource, emptyEmailTheme, {
@ -130,7 +130,7 @@ export async function rewriteTemplateSourceWithAI(templateTsxSource: string): Pr
let lastError = "Unknown rewrite failure";
for (let attempt = 0; attempt < MAX_REWRITE_ATTEMPTS; attempt++) {
const rewriteResult = await rewriteTemplateSourceWithCurrentAIPlumbing(templateTsxSource);
const rewriteResult = await rewriteTemplateSourceWithCurrentAIPlumbing(templateTsxSource, authHeaders);
if (rewriteResult.status === "error") {
lastError = rewriteResult.error;
continue;

View File

@ -124,7 +124,7 @@ const CreateDashboardPreviewInner = memo(function CreateDashboardPreviewInner({
systemPrompt: "create-dashboard",
tools: ["update-dashboard"],
quality: "smart",
speed: "slow",
speed: "fast",
projectId: projectIdRef.current,
transformMessages: async (userMessages) => {
const contextMessages = await buildDashboardMessages(

View File

@ -333,12 +333,7 @@ function getSandboxDocument(artifact: DashboardArtifact, baseUrl: string, dashbo
</head>
<body>
<div id="root"></div>
<!-- Babel (for JSX transpilation). crossorigin=anonymous is required so that
errors thrown from inside Babel (e.g. JSX SyntaxErrors from AI-generated
code) are not sanitized to "Script error." with no message unpkg sends
the matching Access-Control-Allow-Origin header. -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.7/babel.min.js" crossorigin="anonymous"></script>
<!-- Install a global error listener BEFORE any AI code runs so that Babel parse
errors, uncaught runtime throws, and async rejections all reach the parent.
@ -464,6 +459,7 @@ function getSandboxDocument(artifact: DashboardArtifact, baseUrl: string, dashbo
projectOwnerSession: async () => {
return await requestAccessToken();
},
analytics: { enabled: false },
});
// Expose under both names. AI-generated dashboards (post-PR2 prompt)

View File

@ -151,7 +151,7 @@ export function createDashboardChatAdapter(
systemPrompt: "create-dashboard",
tools,
quality: "smart",
speed: "slow",
speed: "fast",
projectId,
sanitizeContent: sanitizeAiContent,
transformMessages: async (messages) => {

View File

@ -10,7 +10,7 @@ const TOOL_ROUTE_HEADERS = {
"Access-Control-Allow-Headers": "*",
};
const MCP_RPC_TIMEOUT_MS = 15_000;
const MCP_RPC_TIMEOUT_MS = 45_000;
type JsonRecord = Record<string, unknown>;

View File

@ -2713,7 +2713,7 @@
"/emails/outbox": {
"get": {
"summary": "List email outbox",
"description": "Lists all emails in the outbox with optional filtering by status or simple_status.",
"description": "Lists all emails in the outbox with optional filtering by status, simple_status, or user_id.",
"parameters": [
{
"name": "status",
@ -2731,6 +2731,16 @@
},
"required": false
},
{
"name": "user_id",
"in": "query",
"schema": {
"type": "string",
"description": "Filter for emails whose recipient is the given user ID."
},
"description": "Filter for emails whose recipient is the given user ID.",
"required": false
},
{
"name": "limit",
"in": "query",

View File

@ -2713,7 +2713,7 @@
"/emails/outbox": {
"get": {
"summary": "List email outbox",
"description": "Lists all emails in the outbox with optional filtering by status or simple_status.",
"description": "Lists all emails in the outbox with optional filtering by status, simple_status, or user_id.",
"parameters": [
{
"name": "status",
@ -2731,6 +2731,16 @@
},
"required": false
},
{
"name": "user_id",
"in": "query",
"schema": {
"type": "string",
"description": "Filter for emails whose recipient is the given user ID."
},
"description": "Filter for emails whose recipient is the given user ID.",
"required": false
},
{
"name": "limit",
"in": "query",