Skip to content

Commit ea09e5a

Browse files
committed
Merge remote-tracking branch 'upstream/main'
# Conflicts: # apps/marketing/package.json # apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts # apps/web/src/appSettings.ts # apps/web/src/components/ChatView.tsx # apps/web/src/components/Sidebar.tsx # apps/web/src/composer-logic.ts # apps/web/src/routes/_chat.$threadId.tsx # apps/web/src/session-logic.test.ts # apps/web/src/session-logic.ts # bun.lock
2 parents 4ef1bcc + 876bbd7 commit ea09e5a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1729
-789
lines changed

KEYBINDINGS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Invalid rules are ignored. Invalid config files are ignored. Warnings are logged
5151
- `terminal.new`: create new terminal (in focused terminal context by default)
5252
- `terminal.close`: close/kill the focused terminal (in focused terminal context by default)
5353
- `chat.new`: create a new chat thread preserving the active thread's branch/worktree state
54-
- `chat.newLocal`: create a new local chat thread for the active project (no worktree context)
54+
- `chat.newLocal`: create a new chat thread for the active project in a new environment (local/worktree determined by app settings (default `local`))
5555
- `editor.openFavorite`: open current project/worktree in the last-used editor
5656
- `script.{id}.run`: run a project script by id (for example `script.test.run`)
5757

apps/desktop/src/main.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,28 @@ function handleCheckForUpdatesMenuClick(): void {
535535
if (!BrowserWindow.getAllWindows().length) {
536536
mainWindow = createWindow();
537537
}
538-
void checkForUpdates("menu");
538+
void checkForUpdatesFromMenu();
539+
}
540+
541+
async function checkForUpdatesFromMenu(): Promise<void> {
542+
await checkForUpdates("menu");
543+
544+
if (updateState.status === "up-to-date") {
545+
void dialog.showMessageBox({
546+
type: "info",
547+
title: "You're up to date!",
548+
message: `T3 Code ${updateState.currentVersion} is currently the newest version available.`,
549+
buttons: ["OK"],
550+
});
551+
} else if (updateState.status === "error") {
552+
void dialog.showMessageBox({
553+
type: "warning",
554+
title: "Update check failed",
555+
message: "Could not check for updates.",
556+
detail: updateState.message ?? "An unknown error occurred. Please try again later.",
557+
buttons: ["OK"],
558+
});
559+
}
539560
}
540561

541562
function configureApplicationMenu(): void {

apps/marketing/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
},
1212
"dependencies": {
1313
"@t3tools/shared": "workspace:*",
14-
"astro": "^5.7.13"
14+
"astro": "^6.0.4"
1515
},
1616
"devDependencies": {
17-
"@astrojs/check": "^0.9.4",
17+
"@astrojs/check": "^0.9.7",
1818
"typescript": "catalog:"
1919
}
2020
}

apps/server/integration/fixtures/providerRuntime.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const codexTurnToolFixture = [
7373
turnId: TURN_ID,
7474
payload: {
7575
itemType: "command_execution",
76-
title: "Command run",
76+
title: "Ran command",
7777
detail: "echo integration",
7878
},
7979
},
@@ -85,7 +85,7 @@ export const codexTurnToolFixture = [
8585
payload: {
8686
itemType: "command_execution",
8787
status: "completed",
88-
title: "Command run",
88+
title: "Ran command",
8989
detail: "echo integration",
9090
},
9191
},

apps/server/src/open.test.ts

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
import fs from "node:fs";
2-
import os from "node:os";
3-
import path from "node:path";
4-
5-
import { assert, describe, it } from "@effect/vitest";
1+
import * as NodeServices from "@effect/platform-node/NodeServices";
2+
import { assert, it } from "@effect/vitest";
3+
import { assertSuccess } from "@effect/vitest/utils";
4+
import { FileSystem, Path, Effect } from "effect";
65

76
import {
87
isCommandAvailable,
98
launchDetached,
109
resolveAvailableEditors,
1110
resolveEditorLaunch,
1211
} from "./open";
13-
import { Effect } from "effect";
14-
import { assertSuccess } from "@effect/vitest/utils";
1512

16-
describe("resolveEditorLaunch", () => {
13+
it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {
1714
it.effect("returns commands for command-based editors", () =>
1815
Effect.gen(function* () {
16+
const antigravityLaunch = yield* resolveEditorLaunch(
17+
{ cwd: "/tmp/workspace", editor: "antigravity" },
18+
"darwin",
19+
);
20+
assert.deepEqual(antigravityLaunch, {
21+
command: "agy",
22+
args: ["/tmp/workspace"],
23+
});
24+
1925
const cursorLaunch = yield* resolveEditorLaunch(
2026
{ cwd: "/tmp/workspace", editor: "cursor" },
2127
"darwin",
@@ -117,7 +123,7 @@ describe("resolveEditorLaunch", () => {
117123
);
118124
});
119125

120-
describe("launchDetached", () => {
126+
it.layer(NodeServices.layer)("launchDetached", (it) => {
121127
it.effect("resolves when command can be spawned", () =>
122128
Effect.gen(function* () {
123129
const result = yield* launchDetached({
@@ -139,26 +145,20 @@ describe("launchDetached", () => {
139145
);
140146
});
141147

142-
describe("isCommandAvailable", () => {
143-
function withTempDir(run: (dir: string) => void): void {
144-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-open-"));
145-
try {
146-
run(dir);
147-
} finally {
148-
fs.rmSync(dir, { recursive: true, force: true });
149-
}
150-
}
151-
152-
it("resolves win32 commands with PATHEXT", () => {
153-
withTempDir((dir) => {
154-
fs.writeFileSync(path.join(dir, "code.CMD"), "@echo off\r\n", "utf8");
148+
it.layer(NodeServices.layer)("isCommandAvailable", (it) => {
149+
it.effect("resolves win32 commands with PATHEXT", () =>
150+
Effect.gen(function* () {
151+
const fs = yield* FileSystem.FileSystem;
152+
const path = yield* Path.Path;
153+
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });
154+
yield* fs.writeFileString(path.join(dir, "code.CMD"), "@echo off\r\n");
155155
const env = {
156156
PATH: dir,
157157
PATHEXT: ".COM;.EXE;.BAT;.CMD",
158158
} satisfies NodeJS.ProcessEnv;
159159
assert.equal(isCommandAvailable("code", { platform: "win32", env }), true);
160-
});
161-
});
160+
}),
161+
);
162162

163163
it("returns false when a command is not on PATH", () => {
164164
const env = {
@@ -168,55 +168,65 @@ describe("isCommandAvailable", () => {
168168
assert.equal(isCommandAvailable("definitely-not-installed", { platform: "win32", env }), false);
169169
});
170170

171-
it("does not treat bare files without executable extension as available on win32", () => {
172-
withTempDir((dir) => {
173-
fs.writeFileSync(path.join(dir, "npm"), "echo nope\r\n", "utf8");
171+
it.effect("does not treat bare files without executable extension as available on win32", () =>
172+
Effect.gen(function* () {
173+
const fs = yield* FileSystem.FileSystem;
174+
const path = yield* Path.Path;
175+
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });
176+
yield* fs.writeFileString(path.join(dir, "npm"), "echo nope\r\n");
174177
const env = {
175178
PATH: dir,
176179
PATHEXT: ".COM;.EXE;.BAT;.CMD",
177180
} satisfies NodeJS.ProcessEnv;
178181
assert.equal(isCommandAvailable("npm", { platform: "win32", env }), false);
179-
});
180-
});
182+
}),
183+
);
181184

182-
it("appends PATHEXT for commands with non-executable extensions on win32", () => {
183-
withTempDir((dir) => {
184-
fs.writeFileSync(path.join(dir, "my.tool.CMD"), "@echo off\r\n", "utf8");
185+
it.effect("appends PATHEXT for commands with non-executable extensions on win32", () =>
186+
Effect.gen(function* () {
187+
const fs = yield* FileSystem.FileSystem;
188+
const path = yield* Path.Path;
189+
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });
190+
yield* fs.writeFileString(path.join(dir, "my.tool.CMD"), "@echo off\r\n");
185191
const env = {
186192
PATH: dir,
187193
PATHEXT: ".COM;.EXE;.BAT;.CMD",
188194
} satisfies NodeJS.ProcessEnv;
189195
assert.equal(isCommandAvailable("my.tool", { platform: "win32", env }), true);
190-
});
191-
});
196+
}),
197+
);
192198

193-
it("uses platform-specific PATH delimiter for platform overrides", () => {
194-
withTempDir((firstDir) => {
195-
withTempDir((secondDir) => {
196-
fs.writeFileSync(path.join(secondDir, "code.CMD"), "@echo off\r\n", "utf8");
197-
const env = {
198-
PATH: `${firstDir};${secondDir}`,
199-
PATHEXT: ".COM;.EXE;.BAT;.CMD",
200-
} satisfies NodeJS.ProcessEnv;
201-
assert.equal(isCommandAvailable("code", { platform: "win32", env }), true);
202-
});
203-
});
204-
});
199+
it.effect("uses platform-specific PATH delimiter for platform overrides", () =>
200+
Effect.gen(function* () {
201+
const fs = yield* FileSystem.FileSystem;
202+
const path = yield* Path.Path;
203+
const firstDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });
204+
const secondDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-open-test-" });
205+
yield* fs.writeFileString(path.join(firstDir, "code.CMD"), "@echo off\r\n");
206+
yield* fs.writeFileString(path.join(secondDir, "code.CMD"), "MZ");
207+
const env = {
208+
PATH: `${firstDir};${secondDir}`,
209+
PATHEXT: ".COM;.EXE;.BAT;.CMD",
210+
} satisfies NodeJS.ProcessEnv;
211+
assert.equal(isCommandAvailable("code", { platform: "win32", env }), true);
212+
}),
213+
);
205214
});
206215

207-
describe("resolveAvailableEditors", () => {
208-
it("returns only editors whose launch commands are available", () => {
209-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "t3-editors-"));
210-
try {
211-
fs.writeFileSync(path.join(dir, "cursor.CMD"), "@echo off\r\n", "utf8");
212-
fs.writeFileSync(path.join(dir, "explorer.EXE"), "MZ", "utf8");
216+
it.layer(NodeServices.layer)("resolveAvailableEditors", (it) => {
217+
it.effect("returns installed editors for command launches", () =>
218+
Effect.gen(function* () {
219+
const fs = yield* FileSystem.FileSystem;
220+
const path = yield* Path.Path;
221+
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-editors-" });
222+
223+
yield* fs.writeFileString(path.join(dir, "cursor.CMD"), "@echo off\r\n");
224+
yield* fs.writeFileString(path.join(dir, "explorer.CMD"), "MZ");
213225
const editors = resolveAvailableEditors("win32", {
214226
PATH: dir,
215227
PATHEXT: ".COM;.EXE;.BAT;.CMD",
216228
});
217229
assert.deepEqual(editors, ["cursor", "file-manager"]);
218-
} finally {
219-
fs.rmSync(dir, { recursive: true, force: true });
220-
}
221-
});
230+
}),
231+
);
222232
});

apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MessageId,
77
type OrchestrationEvent,
88
CheckpointRef,
9+
isToolLifecycleItemType,
910
ThreadId,
1011
TurnId,
1112
type OrchestrationThreadActivity,
@@ -184,18 +185,6 @@ function requestKindFromCanonicalRequestType(
184185
}
185186
}
186187

187-
function isToolLifecycleItemType(itemType: string): boolean {
188-
return (
189-
itemType === "command_execution" ||
190-
itemType === "file_change" ||
191-
itemType === "mcp_tool_call" ||
192-
itemType === "dynamic_tool_call" ||
193-
itemType === "collab_agent_tool_call" ||
194-
itemType === "web_search" ||
195-
itemType === "image_view"
196-
);
197-
}
198-
199188
function runtimeEventToActivities(
200189
event: ProviderRuntimeEvent,
201190
): ReadonlyArray<OrchestrationThreadActivity> {

apps/server/src/provider/Layers/CodexAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ function itemTitle(itemType: CanonicalItemType): string | undefined {
164164
case "plan":
165165
return "Plan";
166166
case "command_execution":
167-
return "Command run";
167+
return "Ran command";
168168
case "file_change":
169169
return "File change";
170170
case "mcp_tool_call":

apps/server/src/provider/Layers/ProviderService.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => {
703703
threadId: session.threadId,
704704
turnId: asTurnId("turn-1"),
705705
toolKind: "command",
706-
title: "Command run",
706+
title: "Ran command",
707707
});
708708
fanout.codex.emit({
709709
type: "tool.completed",
@@ -713,7 +713,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => {
713713
threadId: session.threadId,
714714
turnId: asTurnId("turn-1"),
715715
toolKind: "command",
716-
title: "Command run",
716+
title: "Ran command",
717717
});
718718
fanout.codex.emit({
719719
type: "turn.completed",
@@ -768,7 +768,7 @@ fanout.layer("ProviderServiceLive fanout", (it) => {
768768
threadId: session.threadId,
769769
turnId: asTurnId("turn-1"),
770770
toolKind: "command",
771-
title: "Command run",
771+
title: "Ran command",
772772
detail: "echo one",
773773
},
774774
{

apps/web/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,20 @@
4242
},
4343
"devDependencies": {
4444
"@effect/language-service": "catalog:",
45+
"@rolldown/plugin-babel": "^0.2.0",
4546
"@tailwindcss/vite": "^4.0.0",
4647
"@tanstack/router-plugin": "^1.161.0",
48+
"@types/babel__core": "^7.20.5",
4749
"@types/react": "^19.0.0",
4850
"@types/react-dom": "^19.0.0",
49-
"@vitejs/plugin-react": "^5.1.4",
51+
"@vitejs/plugin-react": "^6.0.0",
5052
"@vitest/browser-playwright": "^4.0.18",
5153
"babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112",
5254
"msw": "^2.12.10",
5355
"playwright": "^1.58.2",
5456
"tailwindcss": "^4.0.0",
5557
"typescript": "catalog:",
56-
"vite": "^8.0.0-beta.12",
58+
"vite": "^8.0.0",
5759
"vitest": "catalog:",
5860
"vitest-browser-react": "^2.0.5"
5961
}

apps/web/src/appSettings.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from "vitest";
22

33
import {
4+
DEFAULT_TIMESTAMP_FORMAT,
45
getAppModelOptions,
56
normalizeCustomModelSlugs,
67
resolveAppModelSelection,
@@ -57,3 +58,9 @@ describe("resolveAppModelSelection", () => {
5758
expect(resolveAppModelSelection("codex", [], "")).toBe("gpt-5.4");
5859
});
5960
});
61+
62+
describe("timestamp format defaults", () => {
63+
it("defaults timestamp format to locale", () => {
64+
expect(DEFAULT_TIMESTAMP_FORMAT).toBe("locale");
65+
});
66+
});

0 commit comments

Comments
 (0)