Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
385 commits
Select commit Hold shift + click to select a range
34766e6
perf(cli): thread show reads only the end of long threads (#24087)
only21mil Oct 30, 2026
c84acfa
fix(web): keep later navigation when thread archival completes (#24109)
only21mil Oct 30, 2026
e9dd8a6
fix(clients): offer Commit when the branch is behind upstream (#24117)
only21mil Oct 30, 2026
d1777ea
fix(claude): stream live command output on Linux when TMPDIR is set (…
only21mil Oct 30, 2026
c924644
fix(web): long guide Next buttons wrap instead of overflowing (#24189)
PixPMusic Oct 30, 2026
e1d85af
fix(desktop): follow live system light/dark changes on Linux (#24193)
eboye Oct 30, 2026
eca102b
fix(server): browser load errors with digits keep their full name (#2…
MarcoDotIO Oct 30, 2026
6275f5c
docs: keybindings guide names the real when key and the stopped-threa…
MarcoDotIO Oct 30, 2026
88fec6f
docs: thread migration guide names the real --base-dir flag (#24250)
MarcoDotIO Oct 30, 2026
06b62fb
fix(desktop): nightly update notes no longer list the Full Changelog …
davisonio Oct 30, 2026
e0464a7
fix(server): accept explicit false in Claude managed policy (#24402)
zaydiscold Oct 30, 2026
771afda
fix(build): preserve directory desktop artifacts and framework symlin…
zaydiscold Oct 30, 2026
e83021c
fix(desktop): LAN pairing links follow the current network address (#…
only21mil Oct 30, 2026
4ce54d4
fix(server): turns no longer start on a provider instance being repla…
only21mil Oct 30, 2026
64f3042
fix(web): closing an invalid When editor no longer leaves Save disabl…
only21mil Oct 30, 2026
f5bc64b
fix(web): deleting a thread no longer offers to remove a shared workt…
only21mil Oct 30, 2026
e5871b6
fix(web): Reconnect on a switched-off environment switches it back on…
Jacksondr5 Oct 30, 2026
8a80ea1
fix(cursor): save SDK credentials with undefined optional fields (#24…
Onkarj012 Oct 30, 2026
e6fccee
fix(server): cache absent legacy transcripts for V2 threads (#24707)
ScottN-PV Oct 30, 2026
b6f066b
fix(server): editing a file no longer forces a full search-index resc…
only21mil Oct 30, 2026
9e97921
fix(clients): a route added after a reorder no longer disappears (#24…
only21mil Oct 30, 2026
6eaf397
fix(server): skip provider CLI update when already on latest (#24763)
samkelly880 Oct 30, 2026
8ae3dfc
fix(providers): a failed provider check no longer drops later setting…
rishabhx29 Oct 30, 2026
3f75a0e
fix(acp): stop showing ACP v2 prompt echoes as a second user message …
ScottN-PV Oct 30, 2026
22c7039
fix(server): removing a failed clone no longer leaves a stuck banner …
dw3105 Oct 30, 2026
87c33c8
fix(web): Limits ChatGPT usage card names the connected accounts (#24…
only21mil Oct 30, 2026
49f51e1
fix(web): allow adding the displayed provider accent color (#25014)
tsukaska Oct 30, 2026
97dbfd7
fix(web): preserve terminal selections across unchanged redraws (#25030)
maria-rcks Oct 30, 2026
bb7ed73
fix(bitbucket): honor the repository in pull request URLs (#25052)
maria-rcks Oct 30, 2026
f8278d8
fix(web): keep typed Settings text when Esc closes Settings (#25131)
AatmanAJ Oct 30, 2026
3a6b018
fix(mobile): keep split-view inspectors registered across renders (#2…
maria-rcks Oct 30, 2026
c3d8c4b
fix(mcp): preserve actionable device screenshot errors (#25216)
maria-rcks Oct 30, 2026
54d0a16
fix(server): reject Codex turns owned by an earlier run (#25224)
maria-rcks Oct 30, 2026
1cfd23e
fix(web): keep palette project lookups in their environment (#25236)
maria-rcks Oct 30, 2026
40b4353
fix(server): HEAD webhook probes no longer start agent runs (#25291)
only21mil Oct 30, 2026
f8df431
fix(antigravity): binary file reads return an error, not garbled text…
only21mil Oct 30, 2026
9474bcb
fix(web): preserve trailing whitespace in review diffs (#25303)
only21mil Oct 30, 2026
6919aa4
fix(codex): keep late subagent interrupts from freezing other threads…
only21mil Oct 30, 2026
68a1956
fix(server): name the missing Antigravity ACP runtime (#25435)
Dodothereal Oct 30, 2026
1fe0b4f
fix(server): clear unreachable preview downloads after a server resta…
only21mil Oct 30, 2026
f048a9b
fix(web): archiving or deleting a thread expires its earlier Undos (#…
only21mil Oct 30, 2026
3121c19
fix(usage): count compressed Codex rollouts (#25453)
maria-rcks Oct 30, 2026
812cb68
fix(provider-acp-registry): install agents with large archive listing…
kareemsetl Oct 30, 2026
286bc5e
fix(server): editing or removing a hand-written shortcut in Settings …
MarcoDotIO Oct 30, 2026
076aacc
fix(acp): cancelled ACP tool calls no longer stay running and end as …
MarcoDotIO Oct 30, 2026
7f2ff41
fix: prevent subagents inheriting parent settlement protections (#26035)
agentic-jarmo Oct 30, 2026
e428a2c
fix(web,mobile): pull request text breaks lines when providers do (#2…
CodyRay Oct 30, 2026
1758072
fix(web): byte sizes just under the next unit no longer read "1024 KB…
MarcoDotIO Oct 30, 2026
91c7c47
fix(server): truncated webhook delivery bodies no longer end in a rep…
MarcoDotIO Oct 30, 2026
f56a477
fix(server): thread deletion removes files attached to question answe…
SouthCarpet Oct 30, 2026
32ea1a1
fix(mobile): keep the composer text box off the model button on RTL p…
S3333F Oct 30, 2026
513bfbb
fix(scripts): check:changed compares forks against upstream, not a st…
tris203 Oct 30, 2026
9ad6c7e
fix(web,mobile): long links in secret requests wrap and can be copied…
MatthewFeroz Oct 30, 2026
2246386
fix(web): clicking a secret request card focuses its field again (#26…
MatthewFeroz Oct 30, 2026
d8430c0
fix(opencode): read the selected Go key from OpenCode 2's credential …
nolight132 Oct 30, 2026
1c7cef2
fix(web): onboarding pairing hint no longer suggests npx t3 serve --t…
wezn Oct 30, 2026
4dd2f73
fix(web): Load balancing and PR sharing name the route in use (#26441)
hawaii-h1 Oct 30, 2026
ad3ea30
docs: clarify Windows T3 Connect host lifetime (#26480)
TheAngryPit Oct 30, 2026
6e72f54
fix(prs): classify generated files and evals correctly (#26563)
jakeleventhal Oct 30, 2026
26af674
fix(forgejo): publishing a repository with no commits no longer fails…
Mnigos Oct 30, 2026
35231d7
fix(web): stop timeline details icon flickering on touch devices (#26…
shivamhwp Oct 30, 2026
32ce9db
fix(web): remote machine icon no longer forces a third sidebar line (…
ahalekelly Oct 30, 2026
e13c0a9
fix(server): keep provider update progress on the latest frame (#26823)
bitmvk Oct 30, 2026
eb5612d
fix(web): prevent long links from creating per-character elements (#2…
saimircelaj0 Oct 30, 2026
c61a51d
fix(server): recover PR cache from corrupt scope revisions (#26873)
markus41 Oct 30, 2026
cfe28f0
fix(web): model picker opens at its final height instead of shrinking…
flamboh Oct 30, 2026
2274ff9
fix(web): Compact chip fits on narrow composers (#27033)
flamboh Oct 30, 2026
1a7ddbe
fix(shared): preserve JSONC properties after CR comments (#27069)
Lucenx9 Oct 30, 2026
1b9437f
fix(web): long pending-question headers truncate instead of overflowi…
toni92-beep Oct 30, 2026
7ec1894
fix(desktop): control sockets work with long temporary paths (#27125)
Lucenx9 Oct 30, 2026
609dd0f
fix(web): plain text code blocks match the text around them (#27144)
profboba Oct 30, 2026
7e83902
fix(desktop): Vivaldi cookie import finds its Linux keyring key (#27148)
Rhinomcd Oct 30, 2026
77f08e6
fix(web): app shortcuts wait while a confirmation dialog is open (#27…
serge-the-hedge Oct 30, 2026
4fb9e5d
fix(server): dev database copies drop links to other environments (#2…
maria-rcks Oct 30, 2026
9ccc715
chore(web): upgrade Base UI to 1.9 (#27796)
juliusmarminge Oct 30, 2026
9588f0c
fix(server): a file picker closes when its page is replaced (#22062)
fcgith Oct 30, 2026
12d1e4c
fix(web): keep truncated Markdown task previews read-only (#22339)
only21mil Oct 30, 2026
e9485df
perf(composer): reuse persisted image bytes when attachments change (…
only21mil Oct 30, 2026
e2d9d9a
fix(server): provider logs of finished threads obey retention limits …
only21mil Oct 30, 2026
469afd8
fix(chat): commands that exit 0 no longer show as failed (#23351)
only21mil Oct 30, 2026
3a873e8
fix(web): disable conflict checkout without required permissions (#23…
only21mil Oct 30, 2026
714f849
fix(server): t3 auth --json and --token-only print only the result (#…
MarcoDotIO Oct 30, 2026
da94a13
fix(web): tab to equations only when they overflow (#23954)
only21mil Oct 30, 2026
7c90d51
fix(web): composer keeps the picked effort when it falls back to anot…
mkantautas Oct 30, 2026
9bd5e12
fix(web): restored Device view waits for device state before setup (#…
only21mil Oct 30, 2026
91a9196
perf(server): idle Grok sessions on Linux stop polling /proc every 25…
only21mil Oct 30, 2026
e496db3
fix(server): a task cancelled as its usage limit lands no longer resu…
spoukyii Oct 30, 2026
7917ac8
fix(web): Resume keeps its thread's model when you switch threads (#2…
only21mil Oct 30, 2026
c6ef3b4
fix(server): empty MCP status filters no longer hide threads (#25306)
only21mil Oct 30, 2026
bef663a
fix(device): recover cached agent daemons after exit (#23441)
yashranaway Oct 30, 2026
9ea95c1
fix(gitlab): keep pull request actions on the selected host (#23695)
parmartejass Oct 30, 2026
2ce3185
fix(codex): say why the Codex app-server exited (#23938)
mkantautas Oct 30, 2026
78d0668
fix(mobile): mark the thread worktree's branch as current (#23436)
only21mil Oct 30, 2026
52b0ab2
fix(preview): reserve capacity before opening browser tabs (#25028)
maria-rcks Oct 30, 2026
efe0d16
fix(mobile): preserve Android Usage across layout changes (#25231)
maria-rcks Oct 30, 2026
e555903
fix(mobile): keep refresh errors out of the load-more footer (#23594)
only21mil Oct 30, 2026
b9eb552
fix(server): release retired provider scopes (#24777)
maxwellyoung Oct 30, 2026
1854e14
fix(server): recognize a Claude proxy set in Claude's settings.json (…
EhsanulHaqueSiam Oct 30, 2026
1820fbe
fix(server): stop event subscribers before closing the database (#27831)
maria-rcks Oct 30, 2026
930df19
fix(mobile): release hidden sidebar PR summary demand (#25337)
saphid Oct 30, 2026
81899ce
feat(server): read Bitbucket and GitCafe credentials from a secret pr…
yordis Oct 30, 2026
cb9629e
perf(web): thread switches skip re-parsing, sidebar scrolling, and co…
juliusmarminge Oct 30, 2026
78f1ba8
fix(web): preserve PDF scroll and zoom across autoreloads (#25555)
edemaine Oct 30, 2026
96c21aa
feat(marketing): homepage quotes now come from well-known devs (#27251)
t3dotgg Oct 30, 2026
d2d1e4f
fix(updates): version checks respect your configured npm registry (#2…
t3dotgg Oct 30, 2026
be5a887
feat: add assembly file highlighting and language selection (#27899)
ImLunaHey Oct 31, 2026
d92c437
feat(web): ask for a GitHub star once after 5 prompts (#27832)
t3dotgg Oct 31, 2026
f3bbd0f
fix(web): video full-screen buttons work in HTML renders (#21378)
punkhop Oct 31, 2026
2a180c7
fix(desktop): compare custom renderer scheme and authority explicitly…
only21mil Oct 31, 2026
a2e4524
fix(web): preview address bar no longer ligates "://" (#21579)
sairajchouhan Oct 31, 2026
0d51b14
fix(server): read the smallest iOS simulator text size as small (#22114)
only21mil Oct 31, 2026
ee2800e
fix(mobile): relative-time labels keep counting on archive and pull r…
only21mil Oct 31, 2026
c856e00
fix(desktop): stop the macOS Settings tracker when T3 exits (#22497)
only21mil Oct 31, 2026
159a914
fix(claude): context meter shows where Claude actually auto-compacts …
GitJarno Oct 31, 2026
5153300
fix(desktop): browser tabs no longer report loopback access as denied…
afrojun Oct 31, 2026
5b9c1ae
fix(desktop): explain Hyprland SnapShot activation timeouts (#23426)
only21mil Oct 31, 2026
76c25c7
fix(forgejo): reviewer picker lists each assignee once on Forgejo and…
0pilatos0 Oct 31, 2026
e2a6267
fix(server): release drained terminal output while queue stays busy (…
parmartejass Oct 31, 2026
5b0cf58
fix(acp-registry): installing one agent no longer blocks other agents…
only21mil Oct 31, 2026
e9a11d4
fix(mobile): name iOS environment buttons for VoiceOver (#23740)
only21mil Oct 31, 2026
95ca05d
fix(mobile): separate expanded thoughts from the next reply (#25300)
shivamhwp Oct 31, 2026
447d71d
fix(web): chat stays centered while the thread details card fits besi…
koushikxd Oct 31, 2026
191cd7e
fix(web): match HTML render backgrounds to the dark chat theme (#25948)
mydd7 Oct 31, 2026
e123ac6
fix(web): a failed review guide keeps Try again reachable (#26073)
NotAnNvidiaFan Oct 31, 2026
01b026f
fix(mobile): scale compose buttons with text size (#26100)
none23 Oct 31, 2026
a05e8fd
fix(web): Add Environment dialog reopens on Remote link instead of a …
Mnigos Oct 31, 2026
de6d4bf
fix(web): keep retention controls stable while editing days (#26335)
0bkevin Oct 31, 2026
6e1bec5
fix(web): prevent permissions label from shrinking on click (#26470)
UtkarshUsername Oct 31, 2026
8df96fb
fix(web): add tooltips to boring sidebar actions (#26477)
UtkarshUsername Oct 31, 2026
49f6cba
fix(server): searching delegate models no longer drops words found in…
MinitJain Oct 31, 2026
2d0b219
fix(server): allow requested browser verification (#26503)
Adamulek123 Oct 31, 2026
f736543
fix(client-runtime): idle clients stop tracing unchanged platform reg…
shivamhwp Oct 31, 2026
7d7db0c
fix(web): remove gaps between permission options (#26514)
Adamulek123 Oct 31, 2026
00b2d0e
fix(web): the prompt box follows the interface font size (#26559)
dw3105 Oct 31, 2026
9c30a1a
perf(server): linked calls reuse sessions while a tool waits (#26567)
Adamulek123 Oct 31, 2026
461df46
fix(web): file context menu actions respond to pointer clicks (#26579)
dbolser Oct 31, 2026
e4a067b
fix(acp): reject models unavailable to the active session (#26623)
trahane Oct 31, 2026
3e42e66
fix(mobile): reconnecting threads no longer open on "Failed to connec…
liamwarfield Oct 31, 2026
ea3767b
fix(web): scheduled task editor shows the destination thread (#26658)
shivamhwp Oct 31, 2026
6557665
fix(provider): agents wait on a deploy instead of ending the turn (#2…
joshrobertson8 Oct 31, 2026
65965f7
fix(server): launched threads step down to a runtime mode their provi…
henrychu04 Oct 31, 2026
6366aba
fix(server): deleted worktree threads recover from a detached checkou…
shivamhwp Oct 31, 2026
1a3f145
fix(server): show the database error instead of "Provider turn failed…
georgenijo Oct 31, 2026
1d690b4
fix(mobile): thread rows show when a title is regenerating (#26835)
saphid Oct 31, 2026
fd08b17
fix(web): punctuation after a file chip stays on the chip's line (#26…
kendyl93 Oct 31, 2026
f2ae390
fix(mobile): give Android reply actions 48dp touch targets (#27020)
Dwite Oct 31, 2026
f98ff73
perf(server): skip unused projections in thread reads (#27039)
Adamulek123 Oct 31, 2026
bed62e6
fix(server): ChatGPT-connected Codex shows reasoning for models newer…
t3dotgg Oct 31, 2026
53a986e
fix(server): keep connection tickets out of request traces (#27904)
maria-rcks Oct 31, 2026
2b15ba0
fix(web): model settings show "No reasoning" instead of labeling ever…
t3dotgg Oct 31, 2026
1da0492
fix(server): hide rolling resets for untouched Antigravity quotas (#2…
SouthCarpet Oct 31, 2026
59bb4e8
fix(mobile): disabled iOS header controls now show as disabled (#24088)
only21mil Oct 31, 2026
80a2351
fix(web,mobile): Luau files are syntax highlighted in diffs (#25865)
Yuruzuu Oct 31, 2026
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
7375716
merge upstream/main into fork
adampeterhiggins Oct 31, 2026
7bab9ac
fix(web): preserve fork behavior across upstream UI changes
adampeterhiggins Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 10 additions & 4 deletions .github/triage/PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,16 @@ of `main` for that work, never the tag-pinned diagnosis clone.
- Show the user the complete final issue text and get an explicit yes before
posting. Never post without it.
- Note at the end of the issue which model and agent produced it.
- If `gh` is not authenticated, offer `gh auth login`, or build a prefilled
https://github.com/pingdotgg/t3code/issues/new URL with title and body query
parameters; print the URL, and open it in their browser only after they
approve.
- Post with `gh issue create --repo pingdotgg/t3code --label via-triage` only
if `gh api repos/pingdotgg/t3code --jq .permissions.triage` prints `true`.
- Otherwise, or if `gh` is not authenticated, build a prefilled
https://github.com/pingdotgg/t3code/issues/new?template=via-triage.yml URL.
The form applies the label whoever submits it. Add `title` plus one
URL-encoded query parameter per form field id: `what-happened`, `diagnosis`,
`steps`, `version`, `environment`, `evidence`, `related`, `workaround` and
`agent`. Keep the full encoded URL below 6,000 characters by shortening any
fields as needed. Give the user any omitted text to paste into the form before
submitting. Print the URL, and open it in their browser only after they approve.
- If the user pasted screenshots, remind them to drag the images into the issue
after it is created; they cannot be attached from here.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mobile-eas-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ jobs:
git config user.name "${APP_SLUG}[bot]"
git config user.email "${user_id}+${APP_SLUG}[bot]@users.noreply.github.com"
git add apps/mobile/app.config.ts
git commit \
git commit --no-verify \
-m "chore(mobile): bump app version to $RELEASE_VERSION" \
-m "Co-authored-by: codex <codex@users.noreply.github.com>"
git push origin "HEAD:refs/heads/${GITHUB_REF_NAME}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,7 @@ jobs:
git config user.email "${{ steps.app_bot.outputs.email }}"

git add apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json pnpm-lock.yaml
git commit -m "chore(release): prepare $RELEASE_TAG"
git commit --no-verify -m "chore(release): prepare $RELEASE_TAG"
git push origin HEAD:main

announce_discord:
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The most common defect in this repo is a change that works on the path you teste
An empty database is a bad test. Seed your worktree's `.t3` with a copy of real data instead of pointing at live state:

- The first `vp run dev` in a worktree does this for you when its database is missing and the source is at most 500 MiB. Larger sources warn and dev starts with an empty database. `--no-seed` or `T3CODE_DEV_SEED=0` skips seeding. Manual migration still accepts larger sources and may use substantial disk space before pruning.
- Run `vp run migrate-dev-db` with your dev server stopped to refresh. It rebuilds `<worktree>/.t3/userdata/statev2.sqlite` from a read-only snapshot of `~/.t3/userdata/statev2.sqlite`, the developer's real data. It keeps recent projects and their stopped threads, and drops scheduled tasks, pending work, and auth sessions, so your dev server never runs the developer's agents. Raise `--projects` and `--threads-per-project` for more data.
- Run `vp run migrate-dev-db` with your dev server stopped to refresh. It rebuilds `<worktree>/.t3/userdata/statev2.sqlite` from a read-only snapshot of `~/.t3/userdata/statev2.sqlite`, the developer's real data. It keeps recent projects and their stopped threads, and drops scheduled tasks, pending work, auth sessions, and links to other environments, so your dev server never runs the developer's agents or reaches their other machines. Raise `--projects` and `--threads-per-project` for more data.
- Refresh `statev2.sqlite`, not `state.sqlite`. The server copies the V1 `state.sqlite` only when `statev2.sqlite` is missing.
- Bring `secrets` and `settings.json` only if the flow under test needs them.
- Copy in, never symlink. Data flows one way: into your sandbox, never back out.
Expand Down
32 changes: 32 additions & 0 deletions apps/desktop/resources/installer.nsh
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
!include "StrFunc.nsh"
!ifdef BUILD_UNINSTALLER
${UnStrRep}
!define T3StrRep '${UnStrRep}'
!else
${StrRep}
!define T3StrRep '${StrRep}'
!endif

!macro customAppRunningMessages
Push $0
Push $1
${If} $IsPowerShellAvailable == 0
System::Call 'kernel32::SetEnvironmentVariable(t "T3CODE_INSTALL_DIR", t "$INSTDIR")'
nsExec::ExecToStack `"$PowerShellPath" -NoProfile -NonInteractive -C "Get-CimInstance -ClassName Win32_Process | Where-Object { $$_.ExecutablePath -and [IO.Path]::GetDirectoryName($$_.ExecutablePath) -eq $$env:T3CODE_INSTALL_DIR } | ForEach-Object { [IO.Path]::GetFileNameWithoutExtension($$_.ExecutablePath) } | Where-Object { $$_ -in 'T3 Code (Alpha)', 'T3 Code (Beta)', 'T3 Code (Nightly)' } | Select-Object -First 1 | ForEach-Object { [Console]::Write($$_) }"`
Pop $0
Pop $1
System::Call 'kernel32::SetEnvironmentVariable(t "T3CODE_INSTALL_DIR", p 0)'
${If} $0 == 0
${If} $1 == "T3 Code (Alpha)"
${OrIf} $1 == "T3 Code (Beta)"
${OrIf} $1 == "T3 Code (Nightly)"
${T3StrRep} $appRunningMessage $appRunningMessage "${PRODUCT_NAME}" $1
${T3StrRep} $appClosingMessage $appClosingMessage "${PRODUCT_NAME}" $1
${T3StrRep} $appCannotBeClosedMessage $appCannotBeClosedMessage "${PRODUCT_NAME}" $1
${EndIf}
${EndIf}
${EndIf}
Pop $1
Pop $0
!macroend

!macro customInstall
!ifndef DO_NOT_CREATE_START_MENU_SHORTCUT
${IfNot} ${FileExists} "$newStartMenuLink"
Expand Down
189 changes: 189 additions & 0 deletions apps/desktop/src/app/ClerkTokenStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// @effect-diagnostics nodeBuiltinImport:off -- These tests assert real POSIX modes on a temporary directory.
import * as NodeFS from "node:fs";
import * as NodeOS from "node:os";
import * as NodePath from "node:path";
import { assert, describe, it } from "@effect/vitest";
import { vi } from "vite-plus/test";

vi.mock("node:fs", { spy: true });

import { openClerkTokenStorage, type ClerkTokenStorage } from "./ClerkTokenStore.ts";

// oxlint-disable-next-line t3code/no-global-process-runtime -- Unix mode bits are the assertion; Windows reports them through ACLs.
const describePosix = describe.skipIf(process.platform === "win32");

const modeOf = (path: string): number => NodeFS.statSync(path).mode & 0o777;

function writeLoose(path: string, contents: string): void {
NodeFS.writeFileSync(path, contents, { mode: 0o666 });
NodeFS.chmodSync(path, 0o666);
}

async function withStateDir(run: (stateDir: string) => Promise<void> | void): Promise<void> {
const stateDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-clerk-tokens-"));
try {
await run(stateDir);
} finally {
NodeFS.rmSync(stateDir, { recursive: true, force: true });
}
}

const idleStorage = (): ClerkTokenStorage => ({
getItem: async () => null,
setItem: async () => undefined,
removeItem: () => undefined,
});

describePosix("ClerkTokenStore", () => {
it("keeps clerk tokens at 0600 inside a 0700 directory", async () => {
await withStateDir(async (stateDir) => {
NodeFS.chmodSync(stateDir, 0o755);
const directory = NodePath.join(stateDir, "clerk");
const legacyPath = NodePath.join(stateDir, "clerk-tokens.json");
const tokenPath = NodePath.join(directory, "clerk-tokens.json");
NodeFS.mkdirSync(directory, { mode: 0o755 });
NodeFS.chmodSync(directory, 0o755);
writeLoose(legacyPath, '{"__clerk_client_jwt":"enc:existing"}\n');

const opened: string[] = [];
const storage = openClerkTokenStorage(stateDir, (cwd) => {
opened.push(cwd);
return {
getItem: async () => {
writeLoose(tokenPath, "from-get");
return "from-get";
},
setItem: (_key, value) => {
writeLoose(tokenPath, value);
},
removeItem: () => {
NodeFS.rmSync(tokenPath, { force: true });
},
};
});

assert.deepEqual(opened, [directory]);
assert.equal(modeOf(stateDir), 0o755);
assert.equal(modeOf(directory), 0o700);
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.existsSync(legacyPath), false);
assert.equal(
NodeFS.readFileSync(tokenPath, "utf8"),
'{"__clerk_client_jwt":"enc:existing"}\n',
);

assert.equal(await storage.getItem("client"), "from-get");
assert.equal(modeOf(tokenPath), 0o600);
await storage.setItem("client", "from-set");
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "from-set");
storage.removeItem("client");
assert.equal(NodeFS.existsSync(tokenPath), false);

writeLoose(tokenPath, "kept");
writeLoose(legacyPath, "stale");
NodeFS.chmodSync(directory, 0o755);
openClerkTokenStorage(stateDir, () => idleStorage());
assert.equal(modeOf(directory), 0o700);
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.existsSync(legacyPath), false);
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "kept");
});
});

it("removes the legacy token file when the private copy already exists", async () => {
await withStateDir((stateDir) => {
const directory = NodePath.join(stateDir, "clerk");
const legacyPath = NodePath.join(stateDir, "clerk-tokens.json");
const tokenPath = NodePath.join(directory, "clerk-tokens.json");
NodeFS.mkdirSync(directory, { mode: 0o755 });
writeLoose(legacyPath, "stale-credential");
writeLoose(tokenPath, "kept-credential");

openClerkTokenStorage(stateDir, () => idleStorage());

assert.equal(NodeFS.existsSync(legacyPath), false);
assert.equal(modeOf(directory), 0o700);
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "kept-credential");
});
});

it("resolves a relative state directory before opening storage", async () => {
await withStateDir(async (stateDir) => {
const relativeStateDir = NodePath.relative(process.cwd(), stateDir);
const opened: string[] = [];
openClerkTokenStorage(relativeStateDir, (directory) => {
opened.push(directory);
return idleStorage();
});

const directory = NodePath.join(stateDir, "clerk");
assert.equal(NodePath.isAbsolute(relativeStateDir), false);
assert.deepEqual(opened, [directory]);
assert.equal(modeOf(directory), 0o700);
});
});

it("migrates relative state paths from Electron userData, not the working directory", async () => {
await withStateDir((stateDir) => {
const relativeStateDir = NodePath.relative(process.cwd(), stateDir);
const userData = NodePath.join(
stateDir,
"electron",
...relativeStateDir.split(NodePath.sep).map(() => "profile"),
);
const legacyStateDir = NodePath.join(userData, relativeStateDir);
const legacyPath = NodePath.join(legacyStateDir, "clerk-tokens.json");
NodeFS.mkdirSync(legacyStateDir, { recursive: true });
writeLoose(legacyPath, "existing-session");
const unrelatedPath = NodePath.join(stateDir, "clerk-tokens.json");
writeLoose(unrelatedPath, "not-the-electron-session");

openClerkTokenStorage(relativeStateDir, idleStorage, userData);

const tokenPath = NodePath.join(stateDir, "clerk", "clerk-tokens.json");
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "existing-session");
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.existsSync(legacyPath), false);
assert.equal(NodeFS.readFileSync(unrelatedPath, "utf8"), "not-the-electron-session");
});
});

it("migrates tokens when the legacy directory is on another filesystem", async () => {
await withStateDir((stateDir) => {
const legacyPath = NodePath.join(stateDir, "clerk-tokens.json");
writeLoose(legacyPath, "existing-session");
using _rename = vi.spyOn(NodeFS, "renameSync").mockImplementation(() => {
throw Object.assign(new Error("cross-device link"), { code: "EXDEV" });
});

openClerkTokenStorage(stateDir, idleStorage);

const tokenPath = NodePath.join(stateDir, "clerk", "clerk-tokens.json");
assert.equal(NodeFS.readFileSync(tokenPath, "utf8"), "existing-session");
assert.equal(modeOf(tokenPath), 0o600);
assert.equal(NodeFS.existsSync(legacyPath), false);
});
});

it("does not follow a symlinked clerk directory or token file", async () => {
await withStateDir(async (stateDir) => {
const outsideDirectory = NodePath.join(stateDir, "outside-dir");
NodeFS.mkdirSync(outsideDirectory, { mode: 0o755 });
NodeFS.chmodSync(outsideDirectory, 0o755);
NodeFS.symlinkSync(outsideDirectory, NodePath.join(stateDir, "clerk"));

assert.throws(() => openClerkTokenStorage(stateDir, idleStorage), /must be a real directory/);
assert.equal(modeOf(outsideDirectory), 0o755);
NodeFS.unlinkSync(NodePath.join(stateDir, "clerk"));

const outsideFile = NodePath.join(stateDir, "outside.json");
NodeFS.writeFileSync(outsideFile, "secret", { mode: 0o644 });
NodeFS.chmodSync(outsideFile, 0o644);
NodeFS.symlinkSync(outsideFile, NodePath.join(stateDir, "clerk-tokens.json"));
assert.throws(() => openClerkTokenStorage(stateDir, idleStorage), /must be a regular file/);
assert.equal(modeOf(outsideFile), 0o644);
});
});
});
117 changes: 117 additions & 0 deletions apps/desktop/src/app/ClerkTokenStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// @effect-diagnostics nodeBuiltinImport:off -- The Clerk bridge must lock the token file down before startup can yield to Electron's ready event.
import * as NodeFS from "node:fs";
import * as NodePath from "node:path";

// electron-store chmods every write to 0666; the private directory protects tokens during writes.
const CLERK_TOKEN_DIRECTORY_MODE = 0o700;
const CLERK_TOKEN_FILE_MODE = 0o600;
const CLERK_TOKEN_DIRECTORY_NAME = "clerk";
const CLERK_TOKEN_FILE_NAME = "clerk-tokens.json";

type Awaitable<T> = T | Promise<T>;

export interface ClerkTokenStorage {
readonly getItem: (key: string) => Awaitable<string | null>;
readonly setItem: (key: string, value: string) => Awaitable<void>;
readonly removeItem: (key: string) => Awaitable<void>;
}

function isErrno(error: unknown, code: string): boolean {
return typeof error === "object" && error !== null && "code" in error && error.code === code;
}

function lstatIfExists(path: string): NodeFS.Stats | undefined {
try {
return NodeFS.lstatSync(path);
} catch (error) {
if (isErrno(error, "ENOENT")) return undefined;
throw error;
}
}

function assertRegularFile(path: string, stat: NodeFS.Stats): void {
if (stat.isSymbolicLink() || !stat.isFile()) {
throw new Error(`Clerk token file "${path}" must be a regular file.`);
}
}

function restrictClerkTokenFile(path: string): void {
const stat = lstatIfExists(path);
if (stat === undefined) return;
assertRegularFile(path, stat);
if ((stat.mode & 0o777) === CLERK_TOKEN_FILE_MODE) return;
NodeFS.chmodSync(path, CLERK_TOKEN_FILE_MODE);
}

function migrateLegacyClerkTokenFile(stateDir: string, directory: string): void {
const legacyPath = NodePath.join(stateDir, CLERK_TOKEN_FILE_NAME);
const tokenPath = NodePath.join(directory, CLERK_TOKEN_FILE_NAME);
const legacy = lstatIfExists(legacyPath);
if (legacy !== undefined) {
assertRegularFile(legacyPath, legacy);
NodeFS.chmodSync(legacyPath, CLERK_TOKEN_FILE_MODE);
}

const destination = lstatIfExists(tokenPath);
if (destination === undefined) {
if (legacy === undefined) return;
try {
NodeFS.copyFileSync(legacyPath, tokenPath, NodeFS.constants.COPYFILE_EXCL);
NodeFS.chmodSync(tokenPath, CLERK_TOKEN_FILE_MODE);
NodeFS.unlinkSync(legacyPath);
} catch (error) {
if (!isErrno(error, "ENOENT")) throw error;
}
return;
}

assertRegularFile(tokenPath, destination);
NodeFS.chmodSync(tokenPath, CLERK_TOKEN_FILE_MODE);
if (legacy === undefined) return;
try {
NodeFS.unlinkSync(legacyPath);
} catch (error) {
if (!isErrno(error, "ENOENT")) throw error;
}
}

function restrictAfter<T>(run: () => Awaitable<T>, tighten: () => void): Awaitable<T> {
let result: Awaitable<T>;
try {
result = run();
} catch (error) {
tighten();
throw error;
}
if (result instanceof Promise) return result.finally(tighten);
tighten();
return result;
}

/** Opens Clerk token storage in a private directory and keeps the file at `0600`. */
export function openClerkTokenStorage(
stateDir: string,
createStorage: (directory: string) => ClerkTokenStorage,
userDataPath = ".",
): ClerkTokenStorage {
const absoluteStateDir = NodePath.resolve(stateDir);
const directory = NodePath.join(absoluteStateDir, CLERK_TOKEN_DIRECTORY_NAME);
NodeFS.mkdirSync(directory, { recursive: true, mode: CLERK_TOKEN_DIRECTORY_MODE });
const stat = NodeFS.lstatSync(directory);
if (stat.isSymbolicLink() || !stat.isDirectory()) {
throw new Error(`Clerk token directory "${directory}" must be a real directory.`);
}
NodeFS.chmodSync(directory, CLERK_TOKEN_DIRECTORY_MODE);
// electron-store rebases relative paths under Electron userData, not the working directory.
migrateLegacyClerkTokenFile(NodePath.resolve(userDataPath, stateDir), directory);
const tokenPath = NodePath.join(directory, CLERK_TOKEN_FILE_NAME);
restrictClerkTokenFile(tokenPath);

const storage = createStorage(directory);
const tighten = () => restrictClerkTokenFile(tokenPath);
return {
getItem: (key) => restrictAfter(() => storage.getItem(key), tighten),
setItem: (key, value) => restrictAfter(() => storage.setItem(key, value), tighten),
removeItem: (key) => restrictAfter(() => storage.removeItem(key), tighten),
};
}
Loading