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: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
"vitest": "catalog:",
"wait-on": "^8.0.2"
},
"productName": "T3 Code (Alpha)"
"productName": "Haven Code (Alpha)"
}
4 changes: 2 additions & 2 deletions apps/desktop/scripts/electron-launcher.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const APP_BUNDLE_ID = "com.t3tools.t3code";
const APP_DISPLAY_NAME = isDevelopment ? "Haven Code (Dev)" : "Haven Code (Alpha)";
const APP_BUNDLE_ID = "com.haven.havencode";
const LAUNCHER_VERSION = 1;

const __dirname = dirname(fileURLToPath(import.meta.url));
Expand Down
12 changes: 6 additions & 6 deletions apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download";
const UPDATE_INSTALL_CHANNEL = "desktop:update-install";
const STATE_DIR =
process.env.T3CODE_STATE_DIR?.trim() || Path.join(OS.homedir(), ".t3", "userdata");
process.env.T3CODE_STATE_DIR?.trim() || Path.join(OS.homedir(), ".haven-code", "userdata");
const DESKTOP_SCHEME = "t3";
const ROOT_DIR = Path.resolve(__dirname, "../../..");
const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL);
const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const APP_USER_MODEL_ID = "com.t3tools.t3code";
const USER_DATA_DIR_NAME = isDevelopment ? "t3code-dev" : "t3code";
const APP_DISPLAY_NAME = isDevelopment ? "Haven Code (Dev)" : "Haven Code (Alpha)";
const APP_USER_MODEL_ID = "com.haven.havencode";
const USER_DATA_DIR_NAME = isDevelopment ? "havencode-dev" : "havencode";

Choose a reason for hiding this comment

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

P1 Badge Keep previous userData directory as migration fallback

Changing the primary desktop userData directory name to havencode without also checking the prior t3code path causes existing installs to boot against a fresh profile directory. resolveUserDataPath() only falls back to the legacy display-name folder, so users with data under the old normalized folder lose local app state/history visibility after upgrade.

Useful? React with 👍 / 👎.

const LEGACY_USER_DATA_DIR_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)";
const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i;
const COMMIT_HASH_DISPLAY_LENGTH = 12;
Expand Down Expand Up @@ -440,7 +440,7 @@ function handleFatalStartupError(stage: string, error: unknown): void {
console.error(`[desktop] fatal startup error (${stage})`, error);
if (!isQuitting) {
isQuitting = true;
dialog.showErrorBox("T3 Code failed to start", `Stage: ${stage}\n${message}${detail}`);
dialog.showErrorBox("Haven Code failed to start", `Stage: ${stage}\n${message}${detail}`);
}
stopBackend();
restoreStdIoCapture?.();
Expand Down Expand Up @@ -545,7 +545,7 @@ async function checkForUpdatesFromMenu(): Promise<void> {
void dialog.showMessageBox({
type: "info",
title: "You're up to date!",
message: `T3 Code ${updateState.currentVersion} is currently the newest version available.`,
message: `Haven Code ${updateState.currentVersion} is currently the newest version available.`,
buttons: ["OK"],
});
} else if (updateState.status === "error") {
Expand Down
8 changes: 4 additions & 4 deletions apps/server/src/checkpointing/Layers/CheckpointStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ const makeCheckpointStore = Effect.gen(function* () {
const commitEnv: NodeJS.ProcessEnv = {
...process.env,
GIT_INDEX_FILE: tempIndexPath,
GIT_AUTHOR_NAME: "T3 Code",
GIT_AUTHOR_EMAIL: "t3code@users.noreply.github.com",
GIT_COMMITTER_NAME: "T3 Code",
GIT_COMMITTER_EMAIL: "t3code@users.noreply.github.com",
GIT_AUTHOR_NAME: "Haven Code",
GIT_AUTHOR_EMAIL: "havencode@users.noreply.github.com",
GIT_COMMITTER_NAME: "Haven Code",
GIT_COMMITTER_EMAIL: "havencode@users.noreply.github.com",
};

const headExists = yield* hasHeadCommit(input.cwd);
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/codexAppServerManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ describe("startSession", () => {
it("enables Codex experimental api capabilities during initialize", () => {
expect(buildCodexInitializeParams()).toEqual({
clientInfo: {
name: "t3code_desktop",
title: "T3 Code Desktop",
name: "havencode_desktop",
title: "Haven Code Desktop",
version: "0.1.0",
},
capabilities: {
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/codexAppServerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ export function normalizeCodexModelSlug(
export function buildCodexInitializeParams() {
return {
clientInfo: {
name: "t3code_desktop",
title: "T3 Code Desktop",
name: "havencode_desktop",
title: "Haven Code Desktop",
version: "0.1.0",
},
capabilities: {
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/git/Layers/GitCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ const makeGitCore = Effect.gen(function* () {
const repoName = path.basename(input.cwd);
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
const worktreePath =
input.path ?? path.join(homeDir, ".t3", "worktrees", repoName, sanitizedBranch);
input.path ?? path.join(homeDir, ".haven-code", "worktrees", repoName, sanitizedBranch);
const args = input.newBranch
? ["worktree", "add", "-b", input.newBranch, worktreePath, input.branch]
: ["worktree", "add", worktreePath, input.branch];
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ const makeServerProgram = (input: CliInput) =>
? `http://${formatHostForUrl(config.host)}:${config.port}`
: localUrl;
const { authToken, devUrl, ...safeConfig } = config;
yield* Effect.logInfo("T3 Code running", {
yield* Effect.logInfo("Haven Code running", {
...safeConfig,
devUrl: devUrl?.toString(),
authEnabled: Boolean(authToken),
Expand Down Expand Up @@ -342,6 +342,6 @@ export const t3Cli = Command.make("t3", {
autoBootstrapProjectFromCwd: autoBootstrapProjectFromCwdFlag,
logWebSocketEvents: logWebSocketEventsFlag,
}).pipe(
Command.withDescription("Run the T3 Code server."),
Command.withDescription("Run the Haven Code server."),
Command.withHandler((input) => Effect.scoped(makeServerProgram(input))),
);
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const serverCommandId = (tag: string): CommandId =>
const HANDLED_TURN_START_KEY_MAX = 10_000;
const HANDLED_TURN_START_KEY_TTL = Duration.minutes(30);
const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access";
const WORKTREE_BRANCH_PREFIX = "t3code";
const WORKTREE_BRANCH_PREFIX = "havencode";
const TEMP_WORKTREE_BRANCH_PATTERN = new RegExp(`^${WORKTREE_BRANCH_PREFIX}\\/[0-9a-f]{8}$`);

const sameModelOptions = (
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/os-jank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const expandHomePath = Effect.fn(function* (input: string) {
export const resolveStateDir = Effect.fn(function* (raw: string | undefined) {
const { join, resolve } = yield* Path.Path;
if (!raw || raw.trim().length === 0) {
return join(OS.homedir(), ".t3", "userdata");
return join(OS.homedir(), ".haven-code", "userdata");
}
return resolve(yield* expandHomePath(raw.trim()));
});
2 changes: 1 addition & 1 deletion apps/server/src/provider/Layers/ProviderHealth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
assert.strictEqual(status.authStatus, "unknown");
assert.strictEqual(
status.message,
"Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.",
"Codex CLI v0.36.0 is too old for Haven Code. Upgrade to v0.37.0 or newer and restart Haven Code.",
);
}).pipe(
Effect.provide(
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/provider/codexCliVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,5 @@ export function isCodexCliVersionSupported(version: string): boolean {

export function formatCodexCliUpgradeMessage(version: string | null): string {
const versionLabel = version ? `v${version}` : "the installed version";
return `Codex CLI ${versionLabel} is too old for T3 Code. Upgrade to v${MINIMUM_CODEX_CLI_VERSION} or newer and restart T3 Code.`;
return `Codex CLI ${versionLabel} is too old for Haven Code. Upgrade to v${MINIMUM_CODEX_CLI_VERSION} or newer and restart Haven Code.`;
}
2 changes: 1 addition & 1 deletion apps/server/src/telemetry/Identify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const upsertAnonymousId = Effect.gen(function* () {
/**
* getTelemetryIdentifier - Users are "identified" by finding the first match of the following, then hashing the value.
* 1. ~/.codex/auth.json tokens.account_id
* 2. ~/.t3/telemetry/anonymous-id
* 2. ~/.haven-code/telemetry/anonymous-id
*/
export const getTelemetryIdentifier = Effect.gen(function* () {
const codexAccountId = yield* Effect.result(getCodexAccountId);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..800;1,9..40,300..800&display=swap"
rel="stylesheet"
/>
<title>T3 Code (Alpha)</title>
<title>Haven Code (Alpha)</title>
</head>
<body>
<div id="root"></div>
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/appSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const AppSettingsSchema = Schema.Struct({
Schema.withConstructorDefault(() => Option.some([])),
),
textGenerationModel: Schema.optional(TrimmedNonEmptyString),
enableCodexProvider: Schema.Boolean.pipe(
Schema.withConstructorDefault(() => Option.some(false)),
),
claudeUseBedrock: Schema.Boolean.pipe(
Schema.withConstructorDefault(() => Option.some(false)),
),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/branding.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const APP_BASE_NAME = "T3 Code";
export const APP_BASE_NAME = "Haven Code";
export const APP_STAGE_LABEL = import.meta.env.DEV ? "Dev" : "Alpha";
export const APP_DISPLAY_NAME = `${APP_BASE_NAME} (${APP_STAGE_LABEL})`;
export const APP_VERSION = import.meta.env.APP_VERSION || "0.0.0";
16 changes: 12 additions & 4 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ export default function ChatView({ threadId }: ChatViewProps) {
const setStoreThreadError = useStore((store) => store.setError);
const setStoreThreadBranch = useStore((store) => store.setThreadBranch);
const { settings } = useAppSettings();
const filteredAvailableProviders = useMemo(
() =>
AVAILABLE_PROVIDER_OPTIONS.filter(
(option) => option.value !== "codex" || settings.enableCodexProvider,
),
[settings.enableCodexProvider],
);
const timestampFormat = settings.timestampFormat;
const navigate = useNavigate();
const rawSearch = useSearch({
Expand Down Expand Up @@ -460,7 +467,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
? buildLocalDraftThread(
threadId,
draftThread,
fallbackDraftProject?.model ?? DEFAULT_MODEL_BY_PROVIDER.codex,
fallbackDraftProject?.model ?? DEFAULT_MODEL_BY_PROVIDER.claudeAgent,
localDraftError,
)
: undefined,
Expand Down Expand Up @@ -589,7 +596,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
const lockedProvider: ProviderKind | null = hasThreadStarted
? (sessionProvider ?? selectedProviderByThreadId ?? null)
: null;
const selectedProvider: ProviderKind = lockedProvider ?? selectedProviderByThreadId ?? "codex";
const selectedProvider: ProviderKind = lockedProvider ?? selectedProviderByThreadId ?? "claudeAgent";
const baseThreadModel = resolveModelSlugForProvider(
selectedProvider,
activeThread?.model ?? activeProject?.model ?? getDefaultModel(selectedProvider),
Expand Down Expand Up @@ -718,7 +725,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
}, [modelOptionsByProvider, selectedModelForPicker, selectedProvider]);
const searchableModelOptions = useMemo(
() =>
AVAILABLE_PROVIDER_OPTIONS.filter(
filteredAvailableProviders.filter(
(option) => lockedProvider === null || option.value === lockedProvider,
).flatMap((option) =>
modelOptionsByProvider[option.value].map(({ slug, name }) => ({
Expand All @@ -731,7 +738,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
searchProvider: option.label.toLowerCase(),
})),
),
[lockedProvider, modelOptionsByProvider],
[filteredAvailableProviders, lockedProvider, modelOptionsByProvider],
);
const phase = derivePhase(activeThread?.session ?? null);
const isSendBusy = sendPhase !== "idle";
Expand Down Expand Up @@ -3825,6 +3832,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
model={selectedModelForPickerWithCustomFallback}
lockedProvider={lockedProvider}
modelOptionsByProvider={modelOptionsByProvider}
availableProviders={filteredAvailableProviders}
ultrathinkActive={isClaudeUltrathink}
bedrockActive={
settings.claudeUseBedrock ||
Expand Down
26 changes: 8 additions & 18 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,12 @@ function prStatusIndicator(pr: ThreadPr): PrStatusIndicator | null {
return null;
}

function T3Wordmark() {
function HavenWordmark() {
return (
<svg
aria-label="T3"
className="h-2.5 w-auto shrink-0 text-foreground"
viewBox="15.5309 37 94.3941 56.96"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M33.4509 93V47.56H15.5309V37H64.3309V47.56H46.4109V93H33.4509ZM86.7253 93.96C82.832 93.96 78.9653 93.4533 75.1253 92.44C71.2853 91.3733 68.032 89.88 65.3653 87.96L70.4053 78.04C72.5386 79.5867 75.0186 80.8133 77.8453 81.72C80.672 82.6267 83.5253 83.08 86.4053 83.08C89.6586 83.08 92.2186 82.44 94.0853 81.16C95.952 79.88 96.8853 78.12 96.8853 75.88C96.8853 73.7467 96.0586 72.0667 94.4053 70.84C92.752 69.6133 90.0853 69 86.4053 69H80.4853V60.44L96.0853 42.76L97.5253 47.4H68.1653V37H107.365V45.4L91.8453 63.08L85.2853 59.32H89.0453C95.9253 59.32 101.125 60.8667 104.645 63.96C108.165 67.0533 109.925 71.0267 109.925 75.88C109.925 79.0267 109.099 81.9867 107.445 84.76C105.792 87.48 103.259 89.6933 99.8453 91.4C96.432 93.1067 92.0586 93.96 86.7253 93.96Z"
fill="currentColor"
/>
</svg>
<span className="truncate text-sm font-semibold tracking-tight text-foreground">
Haven
<span className="font-medium text-muted-foreground"> Code</span>
</span>
);
}

Expand Down Expand Up @@ -432,7 +425,7 @@ export default function Sidebar() {
projectId,
title,
workspaceRoot: cwd,
defaultModel: DEFAULT_MODEL_BY_PROVIDER.codex,
defaultModel: DEFAULT_MODEL_BY_PROVIDER.claudeAgent,
createdAt,
});
await handleNewThread(projectId, {
Expand Down Expand Up @@ -1150,11 +1143,8 @@ export default function Sidebar() {
<Tooltip>
<TooltipTrigger
render={
<div className="flex min-w-0 flex-1 items-center gap-1 ml-1 cursor-pointer">
<T3Wordmark />
<span className="truncate text-sm font-medium tracking-tight text-muted-foreground">
Code
</span>
<div className="flex min-w-0 flex-1 items-center gap-1.5 ml-1 cursor-pointer">
<HavenWordmark />
<span className="rounded-full bg-muted/50 px-1.5 py-0.5 text-[8px] font-medium uppercase tracking-[0.18em] text-muted-foreground/60">
{APP_STAGE_LABEL}
</span>
Expand Down
16 changes: 12 additions & 4 deletions apps/web/src/components/chat/ProviderModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,21 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
model: ModelSlug;
lockedProvider: ProviderKind | null;
modelOptionsByProvider: Record<ProviderKind, ReadonlyArray<{ slug: string; name: string }>>;
availableProviders?: ReadonlyArray<{ value: ProviderKind; label: string; available: true }>;
ultrathinkActive?: boolean;
bedrockActive?: boolean;
compact?: boolean;
disabled?: boolean;
onProviderModelChange: (provider: ProviderKind, model: ModelSlug) => void;
}) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const availableProviderOptions = props.availableProviders ?? AVAILABLE_PROVIDER_OPTIONS;
const unavailableProviderOptions = props.availableProviders
? PROVIDER_OPTIONS.filter(
(option) =>
option.available && !props.availableProviders!.some((p) => p.value === option.value),
)
: UNAVAILABLE_PROVIDER_OPTIONS;
const activeProvider = props.lockedProvider ?? props.provider;
const selectedProviderOptions = props.modelOptionsByProvider[activeProvider];
const selectedModelLabel =
Expand Down Expand Up @@ -177,7 +185,7 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
</MenuGroup>
) : (
<>
{AVAILABLE_PROVIDER_OPTIONS.map((option) => {
{availableProviderOptions.map((option) => {
const OptionIcon = PROVIDER_ICON_BY_PROVIDER[option.value];
return (
<MenuSub key={option.value}>
Expand Down Expand Up @@ -212,8 +220,8 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
</MenuSub>
);
})}
{UNAVAILABLE_PROVIDER_OPTIONS.length > 0 && <MenuDivider />}
{UNAVAILABLE_PROVIDER_OPTIONS.map((option) => {
{unavailableProviderOptions.length > 0 && <MenuDivider />}
{unavailableProviderOptions.map((option) => {
const OptionIcon = PROVIDER_ICON_BY_PROVIDER[option.value];
return (
<MenuItem key={option.value} disabled>
Expand All @@ -228,7 +236,7 @@ export const ProviderModelPicker = memo(function ProviderModelPicker(props: {
</MenuItem>
);
})}
{UNAVAILABLE_PROVIDER_OPTIONS.length === 0 && <MenuDivider />}
{unavailableProviderOptions.length === 0 && <MenuDivider />}
{COMING_SOON_PROVIDER_OPTIONS.map((option) => {
const OptionIcon = option.icon;
return (
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/components/desktopUpdate.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ export function getArm64IntelBuildWarningDescription(state: DesktopUpdateState):

const action = resolveDesktopUpdateButtonAction(state);
if (action === "download") {
return "This Mac has Apple Silicon, but T3 Code is still running the Intel build under Rosetta. Download the available update to switch to the native Apple Silicon build.";
return "This Mac has Apple Silicon, but Haven Code is still running the Intel build under Rosetta. Download the available update to switch to the native Apple Silicon build.";
}
if (action === "install") {
return "This Mac has Apple Silicon, but T3 Code is still running the Intel build under Rosetta. Restart to install the downloaded Apple Silicon build.";
return "This Mac has Apple Silicon, but Haven Code is still running the Intel build under Rosetta. Restart to install the downloaded Apple Silicon build.";
}
return "This Mac has Apple Silicon, but T3 Code is still running the Intel build under Rosetta. The next app update will replace it with the native Apple Silicon build.";
return "This Mac has Apple Silicon, but Haven Code is still running the Intel build under Rosetta. The next app update will replace it with the native Apple Silicon build.";
}

export function getDesktopUpdateButtonTooltip(state: DesktopUpdateState): string {
Expand Down
18 changes: 17 additions & 1 deletion apps/web/src/routes/_chat.settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ function SettingsRouteView() {
<div className="mb-4">
<h2 className="text-sm font-medium text-foreground">Appearance</h2>
<p className="mt-1 text-xs text-muted-foreground">
Choose how T3 Code looks across the app.
Choose how Haven Code looks across the app.
</p>
</div>

Expand Down Expand Up @@ -342,6 +342,22 @@ function SettingsRouteView() {
</div>

<div className="space-y-4">
<div className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2">
<div>
<p className="text-sm font-medium text-foreground">Enable Codex provider</p>
<p className="text-xs text-muted-foreground">
Show the Codex (OpenAI) provider in the model picker. Disabled by default.
</p>
</div>
<Switch
checked={settings.enableCodexProvider}
onCheckedChange={(checked) =>
updateSettings({ enableCodexProvider: Boolean(checked) })
}
aria-label="Enable Codex provider"
/>
</div>

<label htmlFor="codex-binary-path" className="block space-y-1">
<span className="text-xs font-medium text-foreground">Codex binary path</span>
<Input
Expand Down
Loading
Loading