Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d8e8731
feat: add Codex local provider integration for desktop
ross0x01 Mar 21, 2026
1127e87
feat: add notes API bridge for Codex local provider
fkesheh Mar 24, 2026
751eb9a
fix: address CodeRabbit review findings on Codex integration
fkesheh Mar 25, 2026
daf1dc2
fix: replace service key auth with user token auth for notes API
fkesheh Mar 25, 2026
876d733
fix: respect notes disabled setting in Codex local provider
fkesheh Mar 25, 2026
b98ce76
fix: address review findings — heredoc injection, category mismatch, …
fkesheh Mar 25, 2026
d9da1df
fix: address remaining CodeRabbit review findings
fkesheh Mar 26, 2026
ff1828c
fix: address inline and duplicate CodeRabbit review findings
fkesheh Mar 26, 2026
4f4fab6
fix: parseGitDiff header filter inside hunks and chat creation order
fkesheh Mar 26, 2026
e7e216c
merge: resolve conflicts with main
fkesheh Mar 26, 2026
fe1206b
fix: hide sandbox selector when Codex local provider is selected
fkesheh Mar 26, 2026
c26c9a2
fix: use login shell for command execution and show setup dialog for …
ross0x01 Mar 27, 2026
270d678
fix: remove standalone GPT-5.4 from model selector
ross0x01 Mar 27, 2026
00b469f
refactor: extract notification handler and add Codex usage tracking
ross0x01 Mar 27, 2026
81a37cc
fix: strip login-shell wrapper from Codex commands in UI
ross0x01 Mar 27, 2026
5e18d7a
fix: restore saved Codex model on chat reopen to lock selector
ross0x01 Mar 27, 2026
c2ae835
fix: prevent Codex model errors on web and preserve model lock across…
ross0x01 Mar 27, 2026
80e883f
feat: show desktop download prompt for Codex models on web
ross0x01 Mar 27, 2026
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
6 changes: 6 additions & 0 deletions __mocks__/workos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ export const useAuth = () => ({
signIn: jest.fn(),
signOut: jest.fn(),
});

export const useAccessToken = () => ({
getAccessToken: jest.fn().mockResolvedValue("mock-access-token"),
accessToken: "mock-access-token",
refresh: jest.fn().mockResolvedValue("mock-access-token"),
});
60 changes: 35 additions & 25 deletions app/components/ChatInput/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type RateLimitWarningData,
} from "../RateLimitWarning";
import { isAgentMode } from "@/lib/utils/mode-helpers";
import { isCodexLocal } from "@/types/chat";
import { NULL_THREAD_DRAFT_ID } from "@/lib/utils/client-storage";
import { SandboxSelector } from "../SandboxSelector";
import { ChatInputTextarea } from "./ChatInputTextarea";
Expand Down Expand Up @@ -72,6 +73,7 @@ export const ChatInput = ({
setQueueBehavior,
sandboxPreference,
setSandboxPreference,
selectedModel,
subscription,
isCheckingProPlan,
temporaryChatsEnabled,
Expand Down Expand Up @@ -149,15 +151,19 @@ export const ChatInput = ({
)}

{/* Sandbox selector for new chats on mobile: shown above input & file upload.
On desktop, it's shown below the input (order-3). */}
{isMobile && isNewChat && isAgentMode(chatMode) && (
<div className="flex px-1 pb-2 min-h-9">
<SandboxSelector
value={sandboxPreference}
onChange={setSandboxPreference}
/>
</div>
)}
On desktop, it's shown below the input (order-3).
Hidden when Codex is selected (it manages its own sandbox). */}
{isMobile &&
isNewChat &&
isAgentMode(chatMode) &&
!isCodexLocal(selectedModel) && (
<div className="flex px-1 pb-2 min-h-9">
<SandboxSelector
value={sandboxPreference}
onChange={setSandboxPreference}
/>
</div>
)}

{uploadedFiles && uploadedFiles.length > 0 && (
<FileUploadPreview
Expand Down Expand Up @@ -198,28 +204,32 @@ export const ChatInput = ({
chatMode={chatMode}
contextUsage={contextUsage}
showContextIndicator={showContextIndicator}
hasMessages={hasMessages}
/>
</div>

{/* Sandbox selector below input.
Desktop new chats: absolutely positioned to avoid shifting the centered layout.
Existing chats (all screens): normal flow.
Mobile new chats: hidden (uses above-input placement). */}
{isAgentMode(chatMode) && (!isMobile || !isNewChat) && (
<div
className={`order-3 flex items-center px-1 pt-2 ${isNewChat ? "absolute left-4 right-4 top-full" : ""}`}
>
<SandboxSelector
value={sandboxPreference}
onChange={setSandboxPreference}
/>
{showContextIndicator && contextUsage && (
<div className="ml-auto">
<ContextUsageIndicator {...contextUsage} />
</div>
)}
</div>
)}
Mobile new chats: hidden (uses above-input placement).
Hidden when Codex is selected (it manages its own sandbox). */}
{isAgentMode(chatMode) &&
(!isMobile || !isNewChat) &&
!isCodexLocal(selectedModel) && (
<div
className={`order-3 flex items-center px-1 pt-2 ${isNewChat ? "absolute left-4 right-4 top-full" : ""}`}
>
<SandboxSelector
value={sandboxPreference}
onChange={setSandboxPreference}
/>
{showContextIndicator && contextUsage && (
<div className="ml-auto">
<ContextUsageIndicator {...contextUsage} />
</div>
)}
</div>
)}

{onScrollToBottom && (
<div className="absolute -top-16 left-1/2 -translate-x-1/2 z-40">
Expand Down
21 changes: 13 additions & 8 deletions app/components/ChatInput/ChatInputToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,42 @@ import {
type ContextUsageData,
} from "@/app/components/ContextUsageIndicator";
import { useGlobalState } from "@/app/contexts/GlobalState";
import { isCodexLocal } from "@/types/chat";

export interface ChatInputToolbarProps extends SubmitStopButtonProps {
onAttachClick: () => void;
contextUsage?: ContextUsageData;
showContextIndicator?: boolean;
hasMessages?: boolean;
}

export function ChatInputToolbar({
onAttachClick,
contextUsage,
showContextIndicator = false,
chatMode,
hasMessages = false,
...submitStopProps
}: ChatInputToolbarProps) {
const { selectedModel, setSelectedModel } = useGlobalState();

// Lock switching away from Codex mid-conversation (but allow sub-model changes)
const modelLocked = isCodexLocal(selectedModel) && hasMessages;

return (
<div className="px-3 flex gap-2 items-center min-w-0">
<div className="shrink-0">
<AttachmentButton onAttachClick={onAttachClick} />
</div>
<ChatModeSelector />
{chatMode === "ask" && (
<ModelSelector
value={selectedModel}
onChange={setSelectedModel}
mode={chatMode}
/>
)}
<ModelSelector
value={selectedModel}
onChange={setSelectedModel}
mode={chatMode}
locked={modelLocked}
/>
<div className="ml-auto shrink-0 flex items-center gap-2.5">
{showContextIndicator && contextUsage && chatMode !== "agent" && (
{showContextIndicator && contextUsage && (
<ContextUsageIndicator {...contextUsage} />
)}
<SubmitStopButton
Expand Down
20 changes: 19 additions & 1 deletion app/components/DiffView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import React, { useState } from "react";
import React, { useState, useRef, useEffect } from "react";
import { DiffEditor } from "@monaco-editor/react";
import { ComputerCodeBlock } from "./ComputerCodeBlock";

Expand All @@ -20,6 +20,23 @@ export const DiffView: React.FC<DiffViewProps> = ({
wrap = true,
}) => {
const [viewMode, setViewMode] = useState<ViewMode>("diff");
const editorRef = useRef<any>(null);

// Safely dispose editor on unmount to prevent "TextModel got disposed" errors
useEffect(() => {
return () => {
try {
editorRef.current?.dispose();
} catch {
// Ignore disposal errors
}
editorRef.current = null;
};
}, []);

const handleEditorMount = (editor: any) => {
editorRef.current = editor;
};

const tabs: Array<{ id: ViewMode; label: string }> = [
{ id: "diff", label: "Diff" },
Expand Down Expand Up @@ -99,6 +116,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
modified={modifiedContent}
language={getMonacoLanguage(language)}
theme="vs-dark"
onMount={handleEditorMount}
options={{
readOnly: true,
renderSideBySide: false,
Expand Down
8 changes: 8 additions & 0 deletions app/components/MessagePartHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MemoryToolHandler } from "./tools/MemoryToolHandler";
import { NotesToolHandler } from "./tools/NotesToolHandler";
import { GetTerminalFilesHandler } from "./tools/GetTerminalFilesHandler";
import { SummarizationHandler } from "./tools/SummarizationHandler";
import { CodexToolHandler } from "./tools/CodexToolHandler";
import type { ChatStatus } from "@/types";
import type { FileDetails } from "@/types/file";
import { ReasoningHandler } from "./ReasoningHandler";
Expand Down Expand Up @@ -215,6 +216,13 @@ export const MessagePartHandler = memo(function MessagePartHandler({
);

default:
// Generic Codex tool handler — matches any tool-codex_* type
if (
typeof part.type === "string" &&
part.type.startsWith("tool-codex_")
) {
return <CodexToolHandler part={part} status={status} />;
}
return null;
}
}, arePropsEqual);
Loading
Loading