Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/web/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const env = createEnv({
AI_GATEWAY_API_KEY: z.string().optional(),
OLLAMA_BASE_URL: z.string().optional(),

OPENAI_ZERO_DATA_RETENTION: z.coerce.boolean().optional().default(false),

UPSTASH_REDIS_URL: z.string().optional(),
UPSTASH_REDIS_TOKEN: z.string().optional(),
REDIS_URL: z.string().optional(), // used for subscriptions
Expand Down
6 changes: 3 additions & 3 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@mdx-js/loader": "3.1.1",
"@mdx-js/react": "3.1.1",
"@microsoft/microsoft-graph-client": "3.0.7",
"@modelcontextprotocol/sdk": "^1.20.1",
"@modelcontextprotocol/sdk": "1.24.0",
"@mux/mux-player-react": "3.6.1",
"@next/mdx": "15.5.6",
"@next/third-parties": "15.5.6",
Expand Down Expand Up @@ -92,7 +92,7 @@
"@vercel/analytics": "1.5.0",
"@vercel/speed-insights": "1.2.0",
"ai": "5.0.76",
"better-auth": "1.3.28",
"better-auth": "1.4.5",
"braintrust": "0.4.6",
"capital-case": "2.0.0",
"cheerio": "1.0.0",
Expand Down Expand Up @@ -130,7 +130,7 @@
"next-axiom": "1.9.3",
"next-safe-action": "8.0.11",
"next-themes": "0.4.6",
"nodemailer": "7.0.9",
"nodemailer": "7.0.11",
"nuqs": "2.7.2",
"ollama-ai-provider": "1.2.0",
"openai": "6.6.0",
Expand Down
10 changes: 10 additions & 0 deletions apps/web/utils/llms/model.ts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue on line in apps/web/utils/llms/model.ts:153:

Suggestion: Add defensive checks/defaults in apps/web/utils/llms/model.ts before provider construction—validate env.BEDROCK_ACCESS_KEY/env.BEDROCK_SECRET_KEY and default providerOptions/providerOptions.openai to empty objects—to prevent runtime errors.

+       if (!env.BEDROCK_ACCESS_KEY || !env.BEDROCK_SECRET_KEY) {
+         throw new Error("Bedrock selected but BEDROCK_ACCESS_KEY and/or BEDROCK_SECRET_KEY are not set");
+       }

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,22 @@ function selectModel(
switch (aiProvider) {
case Provider.OPEN_AI: {
const modelName = aiModel || "gpt-5.1";
// When Zero Data Retention is enabled, set store: false to avoid
// "Items are not persisted for Zero Data Retention organizations" errors
// See: https://github.com/vercel/ai/issues/10060
const openAiProviderOptions = env.OPENAI_ZERO_DATA_RETENTION
? {
...providerOptions,
openai: { ...providerOptions?.openai, store: false },
}
: providerOptions;
return {
Comment on lines +71 to 80
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Prevent runtime error when OPENAI_ZERO_DATA_RETENTION is enabled

providerOptions is optional, but this branch assumes it’s always an object:

const openAiProviderOptions = env.OPENAI_ZERO_DATA_RETENTION
  ? {
      ...providerOptions,
      openai: { ...providerOptions?.openai, store: false },
    }
  : providerOptions;

If providerOptions is undefined (which can happen from callers like selectEconomyModel / selectChatModel), ...providerOptions will throw TypeError: Cannot convert undefined or null to object as soon as OPENAI_ZERO_DATA_RETENTION is set to true.

You can avoid this by normalizing to an empty object before spreading:

-    case Provider.OPEN_AI: {
+    case Provider.OPEN_AI: {
       const modelName = aiModel || "gpt-5.1";
-      // When Zero Data Retention is enabled, set store: false to avoid
-      // "Items are not persisted for Zero Data Retention organizations" errors
-      // See: https://github.com/vercel/ai/issues/10060
-      const openAiProviderOptions = env.OPENAI_ZERO_DATA_RETENTION
-        ? {
-            ...providerOptions,
-            openai: { ...providerOptions?.openai, store: false },
-          }
-        : providerOptions;
+      // When Zero Data Retention is enabled, set store: false to avoid
+      // "Items are not persisted for Zero Data Retention organizations" errors
+      // See: https://github.com/vercel/ai/issues/10060
+      const baseOptions = providerOptions ?? {};
+      const openAiProviderOptions = env.OPENAI_ZERO_DATA_RETENTION
+        ? {
+            ...baseOptions,
+            openai: { ...(baseOptions.openai ?? {}), store: false },
+          }
+        : providerOptions;
       return {
         provider: Provider.OPEN_AI,
         modelName,
         model: createOpenAI({ apiKey: aiApiKey || env.OPENAI_API_KEY })(
           modelName,
         ),
-        providerOptions: openAiProviderOptions,
+        providerOptions: openAiProviderOptions,
         backupModel: getBackupModel(aiApiKey),
       };
     }

This keeps behavior unchanged when the flag is false and avoids crashes when it’s true.

Also applies to: 86-86

🤖 Prompt for AI Agents
In apps/web/utils/llms/model.ts around lines 71 to 80 (also apply same fix at
line 86), the code spreads providerOptions without ensuring it's defined when
OPENAI_ZERO_DATA_RETENTION is true, causing a TypeError if providerOptions is
undefined; fix by normalizing providerOptions to an object before spreading
(e.g., use a fallback {} when spreading and when accessing
providerOptions.openai) so the branch safely constructs openAiProviderOptions
even if providerOptions is undefined.

provider: Provider.OPEN_AI,
modelName,
model: createOpenAI({ apiKey: aiApiKey || env.OPENAI_API_KEY })(
modelName,
),
providerOptions: openAiProviderOptions,
backupModel: getBackupModel(aiApiKey),
};
}
Expand Down
2 changes: 2 additions & 0 deletions apps/web/utils/mcp/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ async function getOAuthClient(
response_types: ["code"],
token_endpoint_auth_method: "none", // Public client with PKCE
scope: integrationConfig.scopes.join(" "),
logo_uri: "https://getinboxzero.com/icon.png",
tos_uri: "https://getinboxzero.com/terms",
};

const registered = await registerClient(metadata.registration_endpoint, {
Expand Down
9 changes: 8 additions & 1 deletion apps/web/utils/outlook/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { SafeError } from "@/utils/error";

const logger = createScopedLogger("outlook/client");

// Add buffer time to prevent token expiry during long-running operations
const TOKEN_REFRESH_BUFFER_MS = 10 * 60 * 1000; // 10 minutes

// Wrapper class to hold both the Microsoft Graph client and its access token
export class OutlookClient {
private readonly client: Client;
Expand Down Expand Up @@ -97,7 +100,11 @@ export const getOutlookClientWithRefresh = async ({

// Check if token needs refresh
const expiryDate = expiresAt ? expiresAt : null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expiry check mixes seconds (expires_at) with milliseconds (Date.now() + TOKEN_REFRESH_BUFFER_MS). Consider converting expires_at to ms before comparing to avoid unnecessary refreshes.

-  const expiryDate = expiresAt ? expiresAt : null;
+  const expiryDateMs = expiresAt ? expiresAt * 1000 : null;
   if (
-    accessToken &&
-    expiryDate &&
-    expiryDate > Date.now() + TOKEN_REFRESH_BUFFER_MS
+    accessToken &&
+    expiryDateMs &&
+    expiryDateMs > Date.now() + TOKEN_REFRESH_BUFFER_MS
   ) {
     return createOutlookClient(accessToken);
   }

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

if (accessToken && expiryDate && expiryDate > Date.now()) {
if (
accessToken &&
expiryDate &&
expiryDate > Date.now() + TOKEN_REFRESH_BUFFER_MS
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: expiryDate is stored in seconds but is compared to Date.now() (milliseconds), so this condition is always false and forces needless token refreshes. Convert one side so both are in the same units.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/outlook/client.ts, line 106:

<comment>`expiryDate` is stored in seconds but is compared to `Date.now()` (milliseconds), so this condition is always false and forces needless token refreshes. Convert one side so both are in the same units.</comment>

<file context>
@@ -97,7 +100,11 @@ export const getOutlookClientWithRefresh = async ({
+  if (
+    accessToken &amp;&amp;
+    expiryDate &amp;&amp;
+    expiryDate &gt; Date.now() + TOKEN_REFRESH_BUFFER_MS
+  ) {
     return createOutlookClient(accessToken);
</file context>
Suggested change
expiryDate > Date.now() + TOKEN_REFRESH_BUFFER_MS
expiryDate * 1000 > Date.now() + TOKEN_REFRESH_BUFFER_MS
Fix with Cubic

) {
return createOutlookClient(accessToken);
}

Expand Down
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
"overrides": {
"@types/react": "19.0.10",
"@types/react-dom": "19.0.4"
},
"patchedDependencies": {
"better-auth@1.3.28": "patches/better-auth@1.3.28.patch"
}
}
}
716 changes: 288 additions & 428 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.21.34
v2.21.37
Loading