Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
aa0b3e2
perf(web): the pull request timeline mounts only the events near the …
juliusmarminge Oct 31, 2026
11df0cb
perf(web): pull request comments reuse their markdown parse (#27808)
juliusmarminge Oct 31, 2026
6d46336
fix(observability): redact credential URL params in spans and type th…
juliusmarminge Oct 31, 2026
d6d2433
fix(web): back-to-back messages stack tight with joined corners (#27978)
maria-rcks Oct 31, 2026
961026c
feat(web): context meter fills up and shows tokens against the limit …
t3dotgg Oct 31, 2026
bf8e132
fix(server): preview discovery stops sending HTTP and TLS to agent-st…
t3dotgg Oct 31, 2026
b823dcf
ci(release): release version bump no longer fails on the pre-commit h…
t3dotgg Oct 31, 2026
50c1c5d
feat: side chats beside the main thread (#26901)
t3dotgg Oct 31, 2026
e5909e5
fix(desktop): name the running app in Windows installer prompts (#27886)
Yash-Singh1 Oct 31, 2026
1aa46d0
fix(connect): T3 Connect sockets no longer drop every hour (#27998)
t3dotgg Oct 31, 2026
cadf7d5
feat(cli): t3 up asks how your other devices reach this machine (#27996)
t3dotgg Oct 31, 2026
e00f98f
fix(web): stacked composer notices line up their details buttons (#22…
Zeus-Deus Oct 31, 2026
a0aefbe
fix(shared): give attached threads a canonical read call (#23784)
NikitaMGrimm Oct 31, 2026
f4a82ca
fix(mobile): models stay visible in the picker with Android animation…
shivamhwp Oct 31, 2026
f204389
fix(web): match sidebar footer bottom padding to its horizontal inset…
JakubMartyniak Oct 31, 2026
3e2dff4
fix(grok): Grok updates actually install on npm 12 (#24246)
ishaanko Oct 31, 2026
c0d279f
fix(azure-devops): file comments name the path the diff shows (#23468)
only21mil Oct 31, 2026
7cdbb8e
fix(web): show the worktree picker in the composer strip at default w…
hwanseoc Oct 31, 2026
3989f5a
fix(gitlab): keep scheduled pipelines pending (#25195)
maria-rcks Oct 31, 2026
fa81fac
fix(android): preserve styled text in incoming shares (#22359)
only21mil Oct 31, 2026
45d29b4
fix(web): a refused review shows GitHub's reason (#22382)
tiliakoos Oct 31, 2026
f2cd7a1
fix(web): show complete tooltip labels when opening (#25199)
MatthewFeroz Oct 31, 2026
b47d71d
fix(mobile): review's empty state stays below the header on iOS (#22420)
Claudesaul Oct 31, 2026
1fbbf03
fix(mobile): strip image and link punctuation from subagent previews …
only21mil Oct 31, 2026
3dbc157
docs(desktop): explain recovery after an Alpha update (#25424)
Dodothereal Oct 31, 2026
083c903
fix(web): right panel's tab bar no longer slides under the title bar …
Claudesaul Oct 31, 2026
ceb06de
fix(mobile): keep source text bold for numeric font weights (#23688)
only21mil Oct 31, 2026
ecbb208
fix(mobile): review guide buttons stay on one line (#24764)
PixPMusic Oct 31, 2026
cd7791d
fix(web): expired terminal context toasts say "them" for several cont…
MarcoDotIO Oct 31, 2026
6eef681
refactor(server): share a path check between file readers (#28041)
t3dotgg Oct 31, 2026
09ac556
fix(web): popovers fade out in place instead of jumping after a pick …
flamboh Oct 31, 2026
5e0d0fd
fix(server): truncated file previews no longer end in a replacement c…
MarcoDotIO Oct 31, 2026
8f258f0
fix(usage): use each account accent colour in limit bars (#24630)
dfakkeldy Oct 31, 2026
b65f868
fix(mobile): leaving the iOS app for a few seconds no longer drops th…
anaclumos Oct 31, 2026
640c614
fix(cli): t3 triage issues get the via-triage label for every reporte…
only21mil Oct 31, 2026
5882ccd
fix(mobile): Android floating tab bar fills the row when compose is h…
voida6 Oct 31, 2026
399d96d
fix(mobile): review file navigation respects reduced motion (#23348)
unmanbearpig Oct 31, 2026
c17a469
fix(mobile): keep PRs readable when image URLs are malformed (#23466)
only21mil Oct 31, 2026
5d46778
fix(mobile): PR tab switches no longer leave Liquid Glass behind (#26…
camchis Oct 31, 2026
9cee35a
fix(web): accept comma as decimal in usage price entry (#21939)
MikaKrul Oct 31, 2026
1a93920
fix(mobile): opening another stack layer no longer shows the previous…
only21mil Oct 31, 2026
a83ab55
fix(cursor): grep count results say "1 match", not "1 matches" (#26065)
MarcoDotIO Oct 31, 2026
300baf8
fix(server): the Codex plan badge updates after a ChatGPT downgrade (…
only21mil Oct 31, 2026
44e7318
fix(mobile): reject unknown app variants (#23681)
only21mil Oct 31, 2026
f021c21
fix(server): show Codex tool-output images inline (#23284)
achose369 Oct 31, 2026
a6e2236
fix(server): Antigravity failed logins due to missing skills folders …
medy17 Oct 31, 2026
82503cc
fix(acp): fail requests when stdout closes before the agent exits (#2…
only21mil Oct 31, 2026
044775d
fix(server): keep relay catch-up requests working after startup (#25201)
maria-rcks Oct 31, 2026
ff2f0ee
fix(azure-devops): looking up a project/repository finds the reposito…
MarcoDotIO Oct 31, 2026
61b535e
fix(desktop): handle macOS back and forward gestures (#25188)
maria-rcks Oct 31, 2026
146dd04
perf(acp): long agent terminal output no longer ties up the server (#…
only21mil Oct 31, 2026
2296a4a
fix(bitbucket): page through pull requests that share a millisecond (…
only21mil Oct 31, 2026
9f52b7d
fix(usage): use the freshest reported pooled plan (#25214)
maria-rcks Oct 31, 2026
63b0e3d
fix(server): do not freshly arm days-old usage-limit recoveries (#21735)
fixfon Oct 31, 2026
ec1fa9a
fix(cli): t3 uninstall on Windows removes the downloaded runtime (#24…
only21mil Oct 31, 2026
c94ff9b
fix(desktop): data: popups no longer replace the preview tab (#24866)
only21mil Oct 31, 2026
1d4c5e0
fix(web): show the correct Tailscale URL when switching to port 443 (…
vprashar2929 Oct 31, 2026
2972499
fix(relay): mark a tunnel released only after deletion succeeds (#22337)
only21mil Oct 31, 2026
25dde99
fix(claude): failed advisor calls no longer show as completed (#24254)
MarcoDotIO Oct 31, 2026
52df13e
fix(mobile): preserve notification responses when navigation fails (#…
only21mil Oct 31, 2026
1c8ada5
fix(web): composer notices no longer flicker next to a floating previ…
Brechard Oct 31, 2026
5927111
fix(server): Codex permission requests for glob paths no longer show …
MarcoDotIO Oct 31, 2026
0346d64
fix(server): every commit hook reports finishing with its real exit c…
MarcoDotIO Oct 31, 2026
ec9d994
fix(server): changing Claude effort no longer re-sends the model (#22…
f4llenz Oct 31, 2026
bfb44fb
feat(web): copy the app version from General settings (#24788)
UtkarshUsername Oct 31, 2026
f1e2e5d
perf(acp): MCP calls over ACP no longer wait behind a long call (#23834)
only21mil Oct 31, 2026
15b58ff
fix(server): omit Windows host-only adapters from LAN routes (#25217)
maria-rcks Oct 31, 2026
8f0d66d
fix(web): servers still restarting after an app update no longer offe…
tris203 Oct 31, 2026
7cd9a73
fix(web): thread alerts missed during a disconnect fire on reconnect …
only21mil Oct 31, 2026
f7e5565
fix(server): Forgejo checks keep the final status when it shares a se…
fixfon Oct 31, 2026
e1e6008
fix(web): collapsing a changed-files folder no longer leaves a one-fr…
Yuruzuu Oct 31, 2026
62efa4b
perf(acp): stop re-saving the whole terminal log on every output chun…
only21mil Oct 31, 2026
19a773f
fix(mobile): hardware Enter keeps the Android terminal focused (#24928)
only21mil Oct 31, 2026
43b56b1
fix(device): forward numeric keypad keys to iOS simulators (#23339)
only21mil Oct 31, 2026
80fd67e
fix(provider-cursor): Cursor keeps working when a team blocks /v1/me …
only21mil Oct 31, 2026
4e08e9d
fix(server): imported Codex threads keep their saved Codex names (#24…
only21mil Oct 31, 2026
9f01895
fix(opencode): show a readable message when a provider error is <none…
ricardo-rp Oct 31, 2026
8003dd8
fix(web): Shift+Tab moves a nested composer list item back out (#27016)
tsmith165 Oct 31, 2026
ac49a62
fix(install): verify archives in homes with backslashes or newlines (…
maria-rcks Oct 31, 2026
23f9c2c
perf(server): skip the per-listing SSH probe once a host reports Andr…
only21mil Oct 31, 2026
9bf3d55
fix(server): wake no-checks PR watches at the configured deadline (#2…
only21mil Oct 31, 2026
e20b1d9
fix(desktop): keep Hyprland SnapShot metadata when a CLI title spinne…
saphid Oct 31, 2026
7671644
fix(web): restore No project name and icon from settings (#26798)
shivamhwp Oct 31, 2026
7fe6934
fix(mobile): new thread project list no longer shifts when the No pro…
jdlar18 Oct 31, 2026
66eb776
fix(server): preserve merge ancestry on selected-file commits (#24744)
saphid Oct 31, 2026
d7d5d7c
fix(server): cleanup keeps worktrees waiting for a usage-limit reset …
only21mil Oct 31, 2026
385b1b1
fix(server): Source Control names a CLI built for another CPU instead…
tiliakoos Oct 31, 2026
251834c
fix(claude): retain measured usage after a steered query exits (#25200)
maria-rcks Oct 31, 2026
94a53a2
fix(acp): Copilot threads get T3's tools (#26968)
Radu028 Oct 31, 2026
c96c1b6
feat(web): pick the model and its effort from one control (#28057)
t3dotgg Oct 31, 2026
d544d78
fix(server): Pi transcripts carry their home path (#28061)
maria-rcks Oct 31, 2026
cde3620
fix(web): forward native Copy and Cut from server browser tabs (#26633)
shivamhwp Oct 31, 2026
b010581
fix(usage): combine duplicated skipped-record warnings (#23411)
PixPMusic Oct 31, 2026
4146e51
fix(gitlab): a failed child pipeline no longer shows as a passing che…
only21mil Oct 31, 2026
85bd3f2
fix(web): hide Ultrathink from the Claude effort picker (#28044)
t3dotgg Oct 31, 2026
ffaa1db
feat: pin favorite projects and archive old ones (#26154)
t3dotgg Oct 31, 2026
a24ec90
fix(web): machine label no longer shifts between one and many machine…
t3dotgg Oct 31, 2026
793bbf4
fix(web): long titles no longer overflow the pull request list toolti…
t3dotgg Oct 31, 2026
2359643
fix(web): model picker stays open until dismissed, with a Show more r…
t3dotgg Oct 31, 2026
eef35b0
fix(clients): stop loading thread families when opening chats (#26762)
Yash-Singh1 Oct 31, 2026
9c0fb54
chore(release): prepare v0.1.6
t3-code-bot[bot] Oct 31, 2026
79f1fad
fix(server): bump Antigravity quota probe UA to hub/2.24.0 so Gemini …
LVC-Chakresh Oct 31, 2026
b970bcb
fix(opencode): use Big Pickle for default text generation (#22442)
UtkarshUsername Oct 31, 2026
44d91c5
test(scripts): identify the Windows native probe by executable (#21774)
UtkarshUsername Oct 31, 2026
15fe283
fix(web): effort picker shows Default or the checkmark, flush right (…
shivamhwp Oct 31, 2026
98d4077
Merge upstream/main 15fe283272 into coder-only
henrychu04 Oct 31, 2026
94bb90f
Merge coder-only into the upstream 15fe283272 sync
henrychu04 Oct 31, 2026
6d8c528
Mock the environment reads upstream's project group update makes
henrychu04 Oct 31, 2026
30af146
Record the 15fe283272 sync base and its checklist lessons
henrychu04 Oct 31, 2026
7316ea2
Merge coder-only into the upstream 15fe283272 sync again
henrychu04 Oct 31, 2026
8fff5e1
Merge coder-only into the upstream 15fe283272 sync for #296
henrychu04 Oct 31, 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
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "t3",
"version": "0.1.5",
"version": "0.1.6",
"license": "MIT",
"type": "module",
"scripts": {
Expand Down
41 changes: 40 additions & 1 deletion apps/server/scripts/acp-mock-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ const failSetModeConfigOption = process.env.T3_ACP_FAIL_SET_MODE_CONFIG_OPTION =
const emitEmptyDefaultAgentOption = process.env.T3_ACP_EMIT_EMPTY_DEFAULT_AGENT_OPTION === "1";
const promptResponseText = process.env.T3_ACP_PROMPT_RESPONSE_TEXT;
const promptDelayMs = Number(process.env.T3_ACP_PROMPT_DELAY_MS ?? "0");
const terminalOutputBytes = Number(process.env.T3_ACP_EMIT_TERMINAL_OUTPUT_BYTES ?? "0");
const supportsSessionLifecycle = process.env.T3_ACP_SESSION_LIFECYCLE === "1";
const supportsAcpMcp = process.env.T3_ACP_MCP_ACP === "1";
const supportsHttpMcp = process.env.T3_ACP_MCP_HTTP === "1";
const supportsV2Management = process.env.T3_ACP_V2_MANAGEMENT === "1";
const advertisedAuthMethodId = process.env.T3_ACP_AUTH_METHOD_ID?.trim();
const initializeAuthMethodId =
Expand Down Expand Up @@ -505,7 +507,14 @@ const program = Effect.gen(function* () {
session: {
...(supportsSessionLifecycle ? { fork: {}, additionalDirectories: {} } : {}),
...(supportsV2Management ? { delete: {} } : {}),
...(supportsAcpMcp ? { mcp: { acp: {} } } : {}),
...(supportsAcpMcp || supportsHttpMcp
? {
mcp: {
...(supportsAcpMcp ? { acp: {} } : {}),
...(supportsHttpMcp ? { http: {} } : {}),
},
}
: {}),
},
...(supportsV2Management ? { providers: {} } : {}),
},
Expand Down Expand Up @@ -1020,6 +1029,36 @@ const program = Effect.gen(function* () {
return yield* finishPrompt(requestedSessionId, "end_turn");
}

if (terminalOutputBytes > 0) {
const terminalId = "long-output";
yield* agent.client.sessionUpdate({
sessionId: requestedSessionId,
update: { sessionUpdate: "terminal_update", terminalId, command: "print long output" },
});
// 4 KiB chunks of 64-char numbered lines, like a long build log.
for (let line = 0; line * 64 < terminalOutputBytes; line += 64) {
let chunk = "";
for (let i = line + 1; i <= line + 64; i++) {
chunk += `${String(i).padStart(8, "0")} ${"x".repeat(54)}\n`;
}
yield* agent.client.sessionUpdate({
sessionId: requestedSessionId,
update: {
sessionUpdate: "terminal_output_chunk",
terminalId,
data: Buffer.from(chunk)
.subarray(0, terminalOutputBytes - line * 64)
.toString("base64"),
},
});
}
yield* agent.client.sessionUpdate({
sessionId: requestedSessionId,
update: { sessionUpdate: "terminal_update", terminalId, exitStatus: { exitCode: 0 } },
});
return yield* finishPrompt(requestedSessionId, "end_turn");
}

if (emitNotice) {
// A notice in fx 0.0.14's shape for a skipped skill, between two chunks of one message.
const chunk = (text: string) =>
Expand Down
45 changes: 45 additions & 0 deletions apps/server/src/assets/AssetAccess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,51 @@ describe("AssetAccess", () => {
}).pipe(Effect.provide(layerTest)),
);

it.effect.skipIf(!symlinksSupported)("does not issue URLs for server state files", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const config = yield* ServerConfig.ServerConfig;
const stateFile = path.join(config.secretsDir, "example.bin");
yield* fs.makeDirectory(config.secretsDir, { recursive: true });
yield* fs.writeFile(stateFile, new Uint8Array(32).fill(7));
yield* fs.writeFileString(config.dbPath, "SQLite format 3");
const root = yield* fs.makeTempDirectoryScoped({ prefix: "t3-media-state-" });
const alias = path.join(root, "alias");
yield* fs.symlink(stateFile, alias);
const threadId = ThreadId.make("thread-1");
for (const resource of [
{ _tag: "media-file", threadId, path: stateFile, disposition: "attachment" },
{ _tag: "media-file", threadId, path: alias, disposition: "attachment" },
{
_tag: "media-file",
threadId,
path: path.join(config.secretsDir, "..", "secrets", "example.bin"),
disposition: "attachment",
},
{ _tag: "media-file", threadId, path: config.dbPath },
{ _tag: "draft-workspace-file", cwd: config.stateDir, path: "statev2.sqlite" },
] as const) {
const error = yield* issueAssetUrl({ resource }).pipe(Effect.flip);
expect(error._tag).toBe("AssetWorkspaceAssetNotFoundError");
}

const icon = path.join(config.baseDir, "icon.png");
yield* fs.symlink(stateFile, icon);
yield* fs.writeFileString(path.join(config.baseDir, "page.html"), '<img src="icon.png">');
const page = yield* issueAssetUrl({
resource: { _tag: "draft-workspace-file", cwd: config.baseDir, path: "page.html" },
});
const pageToken = page.relativeUrl.slice(`${ASSET_ROUTE_PREFIX}/`.length).split("/")[0]!;
expect(yield* resolveAsset(pageToken, "icon.png")).toBeNull();
const faviconError = yield* issueAssetUrl({
resource: { _tag: "project-favicon", cwd: root },
projectFaviconPath: icon,
}).pipe(Effect.flip);
expect(faviconError._tag).toBe("AssetProjectFaviconNotFoundError");
}).pipe(Effect.provide(layerTest)),
);

it.effect.skipIf(!symlinksSupported)(
"binds media URLs to the canonical target and rejects symlink substitution",
() =>
Expand Down
9 changes: 7 additions & 2 deletions apps/server/src/assets/AssetAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
timingSafeEqualBase64Url,
} from "../auth/utils.ts";
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import { makeIsServerInternalPath } from "../auth/serverInternalPaths.ts";
import { parseAttachmentFileExtension, resolveAttachmentPathById } from "../attachmentStore.ts";
import * as ServerConfig from "../config.ts";
import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
Expand Down Expand Up @@ -274,7 +275,9 @@ const resolveCanonicalFile = Effect.fn("AssetAccess.resolveCanonicalFile")(funct
if (Option.isNone(canonicalFile)) return null;

const info = yield* ProjectFaviconResolver.optionOnNotFound(fileSystem.stat(canonicalFile.value));
return Option.isSome(info) && info.value.type === "File" ? canonicalFile.value : null;
if (Option.isNone(info) || info.value.type !== "File") return null;
const isServerInternalPath = yield* makeIsServerInternalPath;
return (yield* isServerInternalPath(canonicalFile.value)) ? null : canonicalFile.value;
});

const resolveCanonicalWorkspaceFile = Effect.fn("AssetAccess.resolveCanonicalWorkspaceFile")(
Expand Down Expand Up @@ -302,7 +305,9 @@ const resolveCanonicalWorkspaceFile = Effect.fn("AssetAccess.resolveCanonicalWor
const info = yield* ProjectFaviconResolver.optionOnNotFound(
fileSystem.stat(canonicalFile.value),
);
return Option.isSome(info) && info.value.type === "File" ? canonicalFile.value : null;
if (Option.isNone(info) || info.value.type !== "File") return null;
const isServerInternalPath = yield* makeIsServerInternalPath;
return (yield* isServerInternalPath(canonicalFile.value)) ? null : canonicalFile.value;
},
);

Expand Down
29 changes: 29 additions & 0 deletions apps/server/src/auth/serverInternalPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// @effect-diagnostics nodeBuiltinImport:off - Effect's realPath keeps the requested letter case.
import * as NodeFSP from "node:fs/promises";

import * as Effect from "effect/Effect";
import * as Path from "effect/Path";

import * as ServerConfig from "../config.ts";
import { isWithinRoot } from "../workspace/KnownWorkspaces.ts";

/** Builds a check for the server's own state files, which client file reads do not serve. */
export const makeIsServerInternalPath = Effect.gen(function* () {
const config = yield* ServerConfig.ServerConfig;
const path = yield* Path.Path;
// Native realpath returns the on-disk letter case.
const realPath = (value: string) =>
Effect.promise(() => NodeFSP.realpath(value).catch(() => path.resolve(value)));
return Effect.fn("ServerInternalPaths.isServerInternalPath")(function* (filePath: string) {
const [file, stateDir, secretsDir] = yield* Effect.all(
[realPath(filePath), realPath(config.stateDir), realPath(config.secretsDir)],
{ concurrency: "unbounded" },
);
return (
isWithinRoot(path, file, secretsDir) ||
(path.dirname(file) === stateDir && path.basename(file).includes(".sqlite")) ||
// macOS does not resolve `/dev/fd/N` to the open file's path.
isWithinRoot(path, file, "/dev/fd")
);
});
});
2 changes: 2 additions & 0 deletions apps/server/src/coderEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const CODER_ENVIRONMENT_CAPABILITIES = {
fileAttachments: { maxUploadBytes: PROVIDER_SEND_TURN_MAX_FILE_BYTES },
pullRequests: true,
pullRequestChecks: true,
sideChats: true,
inlineMessageContext: true,
requiredWorktreeBootstrap: true,
threadSettlement: true,
Expand Down Expand Up @@ -91,6 +92,7 @@ export const CODER_ENVIRONMENT_CAPABILITIES = {
localServerStop: true,
environmentIcon: true,
projectCloneTracking: true,
projectOrganization: true,
threadProjectFromScratch: true,
// Links are the gateway's workspace link rules; calls between workspaces go through its hub.
peerLinks: true,
Expand Down
Loading