Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
214 commits
Select commit Hold shift + click to select a range
b6c326d
fix(server): background compactions no longer flip threads to Working…
juliusmarminge Oct 25, 2026
dd2842f
fix(web): a stale queued-edit save no longer clears a newer edit (#25…
juliusmarminge Oct 25, 2026
2649693
fix(client): a thread opened before its shell loads still counts as o…
juliusmarminge Oct 25, 2026
3ddfcfc
docs: open and track PRs through GitCafe when origin is git.cafe (#25…
juliusmarminge Oct 25, 2026
c298a40
perf(web): diff file boundaries observe visibility once instead of on…
juliusmarminge Oct 25, 2026
796fc0a
fix(mobile): center toast actions beside wrapped messages (#24986)
PixPMusic Oct 25, 2026
481b9b7
fix(server): compact superseded turn-item snapshots (#20990)
JustYannicc Oct 25, 2026
3e9d97a
feat(server): count how guest sharing gets used (#24996)
t3dotgg Oct 25, 2026
f898557
chore(lint): enforce unnecessaryPipeChain (#25077)
juliusmarminge Oct 25, 2026
1a13127
test(web): Arabic arrow-key test no longer drops its first key press …
t3dotgg Oct 25, 2026
44df336
fix(mobile): upgrade react-native-keyboard-controller to 1.22.8 for t…
juliusmarminge Oct 26, 2026
2ee1529
fix(infra): keep task cache cleanup ahead of uploads (#25143)
juliusmarminge Oct 26, 2026
0aeb620
chore(lint): enforce mapSomeToAsSome (#25078)
juliusmarminge Oct 26, 2026
05ef5ff
chore(lint): enforce flatMapIgnoredParamToAndThen (#25089)
juliusmarminge Oct 26, 2026
8bdf378
chore(lint): enforce provideLayerSucceedToProvideService (#25090)
juliusmarminge Oct 26, 2026
10d800c
chore(lint): enforce unicorn/require-module-specifiers (#25102)
juliusmarminge Oct 26, 2026
15c6e6b
chore(lint): enforce unicorn/no-useless-fallback-in-spread (#25103)
juliusmarminge Oct 26, 2026
b0f5d44
chore(lint): enforce eslint/no-unneeded-ternary (#25105)
juliusmarminge Oct 26, 2026
2eccc57
chore(lint): enforce eslint/no-unsafe-optional-chaining (#25106)
juliusmarminge Oct 26, 2026
f995643
fix(web): preview favicons resolve their project without ChatView syn…
juliusmarminge Oct 26, 2026
4db2ffc
refactor(mobile): branch picker owns its search and checkout guard (#…
juliusmarminge Oct 26, 2026
65822d8
refactor(web): composer editor stops mirroring props into refs with e…
juliusmarminge Oct 26, 2026
03df289
fix(client): usage-limit Snooze disables at reset even weeks away, vi…
juliusmarminge Oct 26, 2026
738fd08
fix(infra): task cache cleanup test provides its service directly (#2…
juliusmarminge Oct 26, 2026
c58beff
chore(lint): a new React effect states its reason instead of raising …
juliusmarminge Oct 26, 2026
0ad399c
test(web): the diff boundary observer counter lives in its fixture (#…
juliusmarminge Oct 26, 2026
07066c0
fix(client): a queued-edit save keeps its claim until it finishes (#2…
juliusmarminge Oct 26, 2026
5fee09a
refactor(web): terminal focus requests name the terminal they focus (…
juliusmarminge Oct 26, 2026
f4e1407
fix(web): pull request checks stay current after a checks refresh (#2…
juliusmarminge Oct 26, 2026
0b42aa9
chore(devices): bump agent-device to 0.21.31 (#25065)
juliusmarminge Oct 26, 2026
0bf4d16
feat(mobile): composer placeholder reads "Ask anything" (#25176)
flamboh Oct 26, 2026
c8c6deb
refactor(mobile): shared AppState, reduce-motion and back-button help…
juliusmarminge Oct 26, 2026
e14d08c
fix(web): settings panels derive state instead of syncing it with eff…
juliusmarminge Oct 26, 2026
5786e12
refactor(mobile): terminal lifecycle runs in a session store, not scr…
juliusmarminge Oct 26, 2026
9f7b8ad
refactor(web): branch toolbar and dialogs stop syncing through effect…
juliusmarminge Oct 26, 2026
f16ae12
refactor(web): thread find state lives in one store instead of event …
juliusmarminge Oct 26, 2026
9296c20
refactor(web): browser panel tabs follow preview sessions without Cha…
juliusmarminge Oct 26, 2026
11bd26e
chore(lint): enforce effectSucceedWithVoid (#25074)
juliusmarminge Oct 26, 2026
9c02109
chore(lint): enforce preferSucceedSomeOrNone (#25080)
juliusmarminge Oct 26, 2026
312a12d
chore(lint): enforce unnecessaryFailYieldableError (#25083)
juliusmarminge Oct 26, 2026
9ae08d5
chore(lint): enforce catchToOrElseSucceed (#25084)
juliusmarminge Oct 26, 2026
16573c4
chore(lint): enforce allOfMapToForEach (#25085)
juliusmarminge Oct 26, 2026
ef005d6
chore(lint): enforce unnecessaryEffectGen (#25086)
juliusmarminge Oct 26, 2026
f4ba6ae
chore(lint): enforce catchToIgnore (#25087)
juliusmarminge Oct 26, 2026
f5ad87d
chore(lint): enforce optionMatchToFromOption (#25088)
juliusmarminge Oct 26, 2026
a0f3606
chore(lint): enforce redundantMapError (#25091)
juliusmarminge Oct 26, 2026
269deb8
chore(lint): enforce raceFirstWithSleepToTimeout (#25092)
juliusmarminge Oct 26, 2026
686e394
chore(lint): enforce catchUnfailableEffect (#25093)
juliusmarminge Oct 26, 2026
a8b692b
chore(lint): enforce preferTypedSchemaDecoder (#25094)
juliusmarminge Oct 26, 2026
c4dbf19
chore(lint): enforce schemaStructWithTag (#25095)
juliusmarminge Oct 26, 2026
a0b246d
chore(lint): enforce tryCatchInEffectGen (#25097)
juliusmarminge Oct 26, 2026
a320762
chore(lint): enforce eslint/no-unused-vars (#25100)
juliusmarminge Oct 26, 2026
6f21f01
chore(lint): enforce unicorn/no-useless-spread (#25104)
juliusmarminge Oct 26, 2026
3a1b3a7
chore(lint): enforce eslint/no-new (#25107)
juliusmarminge Oct 26, 2026
4e43779
fix(web): theme editor returns to its spot after the window shrinks a…
juliusmarminge Oct 26, 2026
06e80ac
refactor(mobile): cloud account transitions run in the shared account…
juliusmarminge Oct 26, 2026
7a95549
refactor(client): drop the VCS wake and dispatcher element scope opti…
juliusmarminge Oct 26, 2026
d9abfdd
feat(mobile): mobile:install --session installs into an agent-device …
juliusmarminge Oct 26, 2026
888a3d4
fix(server): make document attachments readable to agents (#21458)
maria-rcks Oct 26, 2026
e856833
fix(mobile): native client fingerprints match across macOS and Linux
juliusmarminge Oct 26, 2026
122c5f8
fix(web): keep Windows thread notifications clickable until handled (…
AKolenda Oct 26, 2026
3ca62a5
refactor(client): terminal sessions keep one driver per mode (#25186)
juliusmarminge Oct 26, 2026
b16728a
refactor(web): preview tab sync subscribes to the preview store inste…
juliusmarminge Oct 26, 2026
fe585bc
fix(server): use asSome for initial worktree measurement (#25206)
juliusmarminge Oct 26, 2026
3de45c6
feat(sidebar): working section is on by default (#25190)
maria-rcks Oct 26, 2026
d69810a
feat(web): preview office files, fonts, and mermaid diagrams (#21362)
maria-rcks Oct 26, 2026
73afb57
feat(web): virtualize large file previews (#21466)
maria-rcks Oct 26, 2026
9d17a02
feat(web): preview archives, sqlite databases, and decoded images (#2…
maria-rcks Oct 26, 2026
34ead56
feat(web): preview opendocument, epub, rich text, and notebooks (#21469)
maria-rcks Oct 26, 2026
6700834
chore(lint): enforce eslint/no-empty-pattern (#25101)
juliusmarminge Oct 26, 2026
d19ec7d
chore(lint): enforce typescript/no-extraneous-class (#25108)
juliusmarminge Oct 26, 2026
53896ae
chore(lint): enforce react/capitalized-calls (#25109)
juliusmarminge Oct 26, 2026
fd71ddc
fix(desktop): hidden preview tabs throttle instead of running at full…
t3dotgg Oct 26, 2026
285b19d
chore(lint): enforce react/static-components (#25110)
juliusmarminge Oct 26, 2026
7e20a5b
chore(lint): enforce react/no-unstable-nested-components (#25111)
juliusmarminge Oct 26, 2026
f44fea4
chore(lint): enforce react/no-array-index-key (#25112)
juliusmarminge Oct 26, 2026
908226f
chore(lint): enforce react/immutability (#25115)
juliusmarminge Oct 26, 2026
8a84d40
chore(lint): disable react/jsx-no-constructed-context-values (#25039)
juliusmarminge Oct 26, 2026
c5eb559
chore(lint): disable react/no-object-type-as-default-prop (#25040)
juliusmarminge Oct 26, 2026
56d13e2
chore(lint): disable unicorn/no-array-reverse (#25041)
juliusmarminge Oct 26, 2026
751a37f
chore(lint): disable unicorn/no-new-array (#25042)
juliusmarminge Oct 26, 2026
e5a5515
chore(lint): disable unicorn/prefer-array-find (#25043)
juliusmarminge Oct 26, 2026
78d89dd
chore(lint): disable unicorn/prefer-set-has (#25044)
juliusmarminge Oct 26, 2026
cf21b98
chore(lint): disable unicorn/prefer-string-starts-ends-with (#25045)
juliusmarminge Oct 26, 2026
f10a04b
chore(lint): disable unicorn/prefer-add-event-listener (#25047)
juliusmarminge Oct 26, 2026
d75fb04
chore(lint): disable typescript/no-this-alias (#25048)
juliusmarminge Oct 26, 2026
1cd44f0
chore(lint): disable eslint/no-control-regex (#25049)
juliusmarminge Oct 26, 2026
d1f7018
chore(lint): disable returnEffectInGen (#25050)
juliusmarminge Oct 26, 2026
80b48cc
refactor(mobile): thread detail and feed reset by key and share one f…
juliusmarminge Oct 26, 2026
f88b33a
fix(web): thread find exports the timeline fields type its context na…
juliusmarminge Oct 26, 2026
d487078
chore(lint): enforce react/memo-dependencies (#25117)
juliusmarminge Oct 26, 2026
f279070
fix(web): Escape and copy work in chat media and markdown widgets, wh…
juliusmarminge Oct 26, 2026
f512ff6
fix(web): composer banners, menus and pickers act in their handlers (…
juliusmarminge Oct 26, 2026
0a3b561
refactor(web): device panel reads hub state from atoms (#25265)
juliusmarminge Oct 26, 2026
c3888da
feat(web): New design brings back color where it means something (#25…
t3dotgg Oct 26, 2026
02f4ba7
fix(web): SSH password prompts wait for their dialog, and the app she…
juliusmarminge Oct 26, 2026
d593081
refactor(mobile): settings and usage screens drop sync effects (#25227)
juliusmarminge Oct 26, 2026
a2589f2
fix(mobile): review opens on Changes and keeps the section you picked…
juliusmarminge Oct 26, 2026
7510e5e
fix(mobile): Android dialogs raised before or during activity replace…
juliusmarminge Oct 26, 2026
1de6494
chore(lint): enforce react/purity (#25038)
juliusmarminge Oct 26, 2026
04a21bb
fix(web): app loads again after a chunk cycle blanked it at boot (#25…
t3dotgg Oct 26, 2026
8a4ad94
refactor(web): root notification and toast coordinators start with th…
juliusmarminge Oct 26, 2026
6a3a460
refactor(web): sidebar thread lists derive state instead of syncing i…
juliusmarminge Oct 26, 2026
beeb215
chore(lint): enforce react/refs (#25116)
juliusmarminge Oct 26, 2026
9d6a2e5
fix(web): new thread shortcut focuses palette search, not the Back bu…
t3dotgg Oct 26, 2026
85f17ea
chore(lint): enforce react/exhaustive-effect-dependencies (#25118)
juliusmarminge Oct 26, 2026
72d4dc9
fix(web): composer corners and context strip no longer show double ed…
t3dotgg Oct 26, 2026
f1b8758
fix(web): VS Code icon in thread details no longer has a box around i…
t3dotgg Oct 26, 2026
7df7cb9
fix(web): sent messages stand out on the black canvas in the new desi…
t3dotgg Oct 26, 2026
f40c7c6
feat(web): new design is on by default (#25324)
t3dotgg Oct 26, 2026
7f26969
chore(lint): enforce react/set-state-in-effect (#25119)
juliusmarminge Oct 26, 2026
70b0abc
fix(web): short branch names no longer get clipped in thread details …
t3dotgg Oct 26, 2026
c66f13c
refactor(mobile): stream and MCP webviews tie their work to the view'…
juliusmarminge Oct 26, 2026
aa2c80b
fix(web): stacked notices above the composer no longer cast a dark ba…
t3dotgg Oct 26, 2026
1549046
fix(web): thread undo notice sits above the bottom shelves instead of…
maria-rcks Oct 26, 2026
6f3f0d4
feat(web): "could not load" screen tells users how to debug it (#25317)
t3dotgg Oct 26, 2026
3250780
fix(web): usage tabs no longer jump when switching to Limits (#25336)
t3dotgg Oct 26, 2026
62245d8
fix(web): composer bottom toolbar keeps its background under update n…
t3dotgg Oct 26, 2026
80f411f
chore(lint): enforce catchConditionalRefailToCatchIf (#25081)
juliusmarminge Oct 26, 2026
931bd53
chore(lint): enforce flatMapConditionalToFilterOrFail (#25082)
juliusmarminge Oct 26, 2026
6618ccb
chore(lint): enforce runEffectInsideEffect (#25096)
juliusmarminge Oct 26, 2026
d859715
chore(lint): enforce abortControllerInEffect (#25098)
juliusmarminge Oct 26, 2026
2931461
chore(lint): enforce t3code/no-inline-schema-compile (#25113)
juliusmarminge Oct 26, 2026
d53b446
chore(lint): enforce oxc/no-accumulating-spread (#25114)
juliusmarminge Oct 26, 2026
695f5c9
chore(lint): enforce t3code/no-polling-loop-in-tests (#25120)
juliusmarminge Oct 26, 2026
4e493b1
docs: read GitHub Actions logs through cafe (#25343)
juliusmarminge Oct 26, 2026
4338377
feat(web): "failed to start" screen helps users recover and report th…
t3dotgg Oct 26, 2026
69f85ef
chore(task-cache): name native client downloads so agent-device can i…
juliusmarminge Oct 26, 2026
03851b8
fix(web): keep large thread counts from shrinking labels (#25355)
Yash-Singh1 Oct 26, 2026
cbd72e5
fix(task-cache): avoid cross-request admission waiters (#25353)
juliusmarminge Oct 26, 2026
3d5364f
fix(task-cache): name Android native client downloads .apk
juliusmarminge Oct 26, 2026
10629af
fix(server): select the active iPhone Duo display for screenshots (#2…
shivamhwp Oct 26, 2026
3ed0688
fix(server): remove one-child-per-model instruction (#25359)
Yash-Singh1 Oct 26, 2026
3e70f55
fix(dev): skip auto-seeding large databases (#25365)
Yash-Singh1 Oct 26, 2026
2763bbe
fix(server): preview snapshots preserve blurred backdrops (#24618)
juliusmarminge Oct 26, 2026
5a38e6b
fix(web): model picker no longer jumps when switching providers (#25358)
t3dotgg Oct 26, 2026
9eac774
test(mobile): run React hooks with the release compiler (#24274)
juliusmarminge Oct 26, 2026
bfc7177
fix(mobile): pause videos outside visible thread and PR content (#24268)
juliusmarminge Oct 26, 2026
572cf74
fix(server): retry stalled MCP registration for resumed Claude turns …
Yash-Singh1 Oct 26, 2026
1ecb1a0
fix(mobile): avoid crashes opening wrapped source links (#25156)
juliusmarminge Oct 26, 2026
fb8fc1f
refactor(web): disconnect and reconnect delays read a shared deadline…
juliusmarminge Oct 26, 2026
d171a88
feat(mobile): run the chrome e2e across poses on the device pool
juliusmarminge Oct 26, 2026
72dde22
chore(lint): enforce effectFnOpportunity (#25099)
juliusmarminge Oct 26, 2026
e1c750e
fix(web): composer body ref stops re-attaching on every render (#25409)
juliusmarminge Oct 26, 2026
0a20d7e
fix(web): switching threads no longer delays the next thread's visit …
juliusmarminge Oct 26, 2026
91f47a1
fix(mobile): mobile:install builds on a cache miss for pooled devices…
juliusmarminge Oct 26, 2026
cc93cc5
revert(web): composer body ref goes back to the #25079 ref callback (…
juliusmarminge Oct 26, 2026
184d915
feat(mobile): thread rows drop the branch and lead with the title (#2…
juliusmarminge Oct 26, 2026
a240408
refactor(web): file breadcrumb menus refetch on a new workspace chang…
juliusmarminge Oct 26, 2026
1316d48
fix(web): citations to older responses load their history instead of …
juliusmarminge Oct 26, 2026
6ea3414
refactor(client): composers share one workspace snapshot refresh per …
juliusmarminge Oct 26, 2026
6ffde5b
refactor(mobile): composer pastes and picks are cancelled by their dr…
juliusmarminge Oct 26, 2026
911091b
chore(mobile): e2e poses for current devices, Duo and landscape fixes
juliusmarminge Oct 26, 2026
3433ba1
fix(mobile): select the correct draft after adding a project (#24269)
juliusmarminge Oct 26, 2026
6a1c8d1
fix(mobile): mobile:install no longer requires --prepare
juliusmarminge Oct 26, 2026
3e47c1c
fix(mobile): e2e landscape poses rotate for real on tablets and folds
juliusmarminge Oct 26, 2026
cc0b86d
fix(server): goals stop the running turn, clear while working, no ech…
t3dotgg Oct 26, 2026
0712398
refactor(web): pull request list remembers each answer for the next s…
juliusmarminge Oct 26, 2026
3f3c1b9
refactor(web): HTML previews handle frame messages in one listener (#…
juliusmarminge Oct 26, 2026
2acc7b5
refactor(web): review guide runs are watched from their answers, not …
juliusmarminge Oct 26, 2026
9d7997a
refactor(web): resize drags, panel shells and the browser host own th…
juliusmarminge Oct 26, 2026
3cb16cf
fix(web): find shows its match after a thread switch, and the timelin…
juliusmarminge Oct 26, 2026
59a3f52
fix(mobile): a new thread's first send no longer sticks on Starting (…
juliusmarminge Oct 26, 2026
d5a791b
fix(client): drafts for answered questions are cleared even when thei…
juliusmarminge Oct 26, 2026
28df7a2
refactor(web): terminal output no longer re-renders the drawer, theme…
juliusmarminge Oct 26, 2026
f0dd86f
fix(web): stable callbacks refresh before layout effects read them (#…
juliusmarminge Oct 26, 2026
7d3208c
test(web): say why the useStableCallback test renders without a route…
juliusmarminge Oct 26, 2026
e5ea25d
fix(mobile): queued edits end when their run starts, wherever you are…
juliusmarminge Oct 26, 2026
148d2fd
refactor(web): root theme, toast and chrome sync start with the app (…
juliusmarminge Oct 26, 2026
a936f05
refactor(web): root theme, toast and chrome sync start with the app (…
juliusmarminge Oct 26, 2026
d6fb62a
fix(mobile): editing a queued message while a question is open no lon…
juliusmarminge Oct 26, 2026
f05c38f
fix(web): browser viewport resizes work without an open preview panel…
juliusmarminge Oct 26, 2026
d81757e
test(web): attachment preview omission tests no longer time out on CI…
juliusmarminge Oct 26, 2026
87ea18c
fix(mobile): describe when composer view commands can reject (#25516)
juliusmarminge Oct 26, 2026
7c6e85c
fix(web): browser history and titles show up again (#25491)
juliusmarminge Oct 26, 2026
3f94487
fix(web): switching back to a thread no longer restores its tab's old…
juliusmarminge Oct 26, 2026
e6ec114
refactor(web): pull request page reacts to turns and sweep Escape wit…
juliusmarminge Oct 26, 2026
b424c6e
fix(web): an armed pull request confirm no longer outlives its button…
juliusmarminge Oct 26, 2026
210b2e7
fix(mobile): keep expanding composer text above its toolbar (#25396)
saphid Oct 26, 2026
1c1050e
fix(server): release completed PR sync sweep data from the read cache…
juliusmarminge Oct 26, 2026
e762f09
fix(mobile): drop the unused useLayoutEffect import that fails lint o…
juliusmarminge Oct 26, 2026
374e57d
refactor(web): provider settings and Codex setup run as explicit flow…
juliusmarminge Oct 26, 2026
8e1bb03
test(web): attachment preview omission cases check the prepared docum…
juliusmarminge Oct 26, 2026
c4d5950
refactor(mobile): pull request screens use a file-jump store and hand…
juliusmarminge Oct 26, 2026
4e42eaf
fix(mobile): label the pull request toast action View (#25581)
PixPMusic Oct 26, 2026
220eb73
refactor(mobile): media previews share file leases and close on blur …
juliusmarminge Oct 26, 2026
b6994c5
fix(shared): pin T3 Connect cloudflared to 2026.10.4, which drops the…
Project516 Oct 26, 2026
278588a
fix(web): skill chips in chat open the same details popover as the co…
flamboh Oct 26, 2026
dd5b540
refactor(web): chat route shortcuts register once instead of rebuildi…
juliusmarminge Oct 26, 2026
fe507c5
fix(clients): queued messages can't be edited while a question is ope…
juliusmarminge Oct 26, 2026
0899305
docs: add an FAQ that answers what people ask every day (#25451)
t3dotgg Oct 26, 2026
560ddb1
feat(web): warn before adding a second Claude or Codex account (#25475)
t3dotgg Oct 26, 2026
0790e38
fix(server): expire new-model badges after seven days (#25609)
Yash-Singh1 Oct 26, 2026
451bb45
refactor(web): running timers share one clock instead of an interval …
juliusmarminge Oct 26, 2026
4034b5f
fix(web): simplify the created pull request success row (#25613)
Yash-Singh1 Oct 26, 2026
5effca1
fix(web): thread jump hints wait for their delay on every hold (#25636)
juliusmarminge Oct 26, 2026
a54ba53
fix(mobile): open threads from the local cache instead of "Thread una…
juliusmarminge Oct 26, 2026
e2d3ded
fix(infra): task-cache telemetry no longer strands an event queued du…
juliusmarminge Oct 26, 2026
0c901ed
refactor(web): video previews attach listeners on their element and s…
juliusmarminge Oct 26, 2026
3de3eee
refactor(mobile): voice input session owns recording, metering and ke…
juliusmarminge Oct 26, 2026
6838e31
fix(web): screen readers can wrap to the first and last message of a …
shivamhwp Oct 26, 2026
024e593
fix(client-runtime): a slow host no longer shows settings as read-onl…
yordis Oct 26, 2026
a4fbea8
refactor(mobile): thread rows share one clock and cache SVGs instead …
juliusmarminge Oct 26, 2026
0604547
fix(server): a thread whose worktree was deleted recovers on the next…
yordis Oct 26, 2026
977a188
refactor(mobile): pull request list records answers where they land (…
juliusmarminge Oct 26, 2026
abf8f43
fix(mobile): keep floating status within the composer width (#25671)
PixPMusic Oct 26, 2026
9db433f
refactor(web): settings group by concern, with General split into foc…
juliusmarminge Oct 26, 2026
576a40f
fix(server): telemetry keeps a failed batch for 30 minutes, not 1.5 (…
t3dotgg Oct 26, 2026
78e93fd
fix(mobile): a caret move in one composer no longer moves another dra…
juliusmarminge Oct 26, 2026
d73fd12
test(web): attachment preview Word and large-text tests no longer fla…
juliusmarminge Oct 26, 2026
82dc892
feat(evals): shared evals package and an eval for T3 tool-use mistake…
t3dotgg Oct 26, 2026
0b0417a
refactor(web): Cmd+O presses the Open in editor button instead of a p…
juliusmarminge Oct 26, 2026
923a59f
fix(fork): merge upstream 0b0417a into patch/cache-expiry-compaction
fireboltdude1357 Oct 26, 2026
577e645
docs(fork): Compact before cache expires now lives in Settings > Threads
fireboltdude1357 Oct 27, 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
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .agents/skills/test-t3-mobile/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ names). The device id may be omitted when exactly one device is booted. It
prints one JSON line; on failure it names the next command. Authorized mobile
verification includes that step unless the user prohibits it.

For a device you opened in an agent-device session elsewhere (a device pool,
cloud, or remote host), run `vp run mobile:install <ios|android> --session <s>`
from the directory you opened the session in. It installs the cached build into
that session (`--abi arm64-v8a` for an ARM Android device; `--metro <url>` also
opens it against Metro) and never builds.

Start `vp run dev:client` from `apps/mobile`, or reuse a healthy Metro belonging
to this checkout. Open its printed development-client URL with AgentDevice
`open com.t3tools.t3code.dev <url>` and all returned target arguments.
Expand Down
2 changes: 2 additions & 0 deletions .github/scripts/ci-changes.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//;

// These tests read other packages without declaring dependencies.
const TEST_ONLY_DEPS = {
// Evals import the server's source to test its real model-facing text.
"@t3tools/evals": ["t3"],
"@t3tools/provider-pi": ["@t3tools/client-runtime"],
"@t3tools/scripts": [
"@t3tools/desktop",
Expand Down
10 changes: 7 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,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. `--no-seed` or `T3CODE_DEV_SEED=0` skips it.
- 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.
- 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.
Expand Down Expand Up @@ -136,10 +136,14 @@ An empty database is a bad test. Seed your worktree's `.t3` with a copy of real

For authorized mobile verification, a missing or outdated native client is an install step, not a blocker. Run `vp run mobile:install <ios|android> [device-id]` on the device host before starting Metro; it targets the only booted simulator or connected Android device when the id is omitted. It installs the dev client for this checkout's native fingerprint from the shared cache in seconds and builds only when no machine has built it yet, so prefer it over `expo run:*`, `xcodebuild`, or `gradlew`, which rebuild from scratch. Its stdout is one JSON line; build output goes to the log it names. See `test-t3-mobile` for the full workflow.

## Evals

`evals/` checks how real models behave with T3 Code's model-facing text: agent instructions, MCP tool descriptions and schemas, and generated titles. Before you change that text, read [evals/README.md](evals/README.md) and run the evals it names. When you fix a model behavior bug, add a case that fails before the fix.

## Pull requests

- Never make a PR unless the developer explicitly asks you to do so.
- If `origin` is git.cafe, follow [GitCafe mirror](docs/operations/development.md#gitcafe-mirror-optional): use `cafe` for existing PRs, issues, comments and labels, but push and open PRs on GitHub, where CI runs.
- If `origin` is git.cafe, follow [GitCafe mirror](docs/operations/development.md#gitcafe-mirror-optional): push to `origin`, open and track PRs with `cafe` and their git.cafe URLs, and keep `gh` for merging.
- Conventional commit titles, plain language: `fix(web): new threads no longer spike CPU`.
- Body: the problem in a sentence or two, then how you fixed it. End with the model and harness that did the work.
- UI changes need before/after images. Motion or timing needs a short video.
Expand Down Expand Up @@ -194,7 +198,7 @@ Architecture and its constraints: `docs/internals/overview.md`. Glossary: `docs/
options, and pass combinators as additional function-builder arguments.
- Inferred types over annotations. `any` is the enemy.
- Default to no comment. Write one only for a constraint, an external quirk, or an invariant the code can't show, in one line where you can and four at most. Don't restate names or signatures, narrate steps, record history, or explain the fix you just made; the PR carries that. `t3code/no-low-value-comments` enforces the length cap and the mechanical cases.
- React effects are a last resort: derive in render, act in handlers, reset with `key`, or use a ref callback or store. The `t3code/react-effect-budget` lint rule holds each file to its entry in `scripts/effect-budget.json`; raise an entry only in the PR that needs the effect, and lower it when you remove one.
- React effects are for synchronizing with something outside React. Derive in render and act in handlers instead of syncing state, but don't hide real lifecycle work in refs or stores to dodge one. The `t3code/react-effect-budget` lint rule holds each file to its count in `scripts/effect-budget.json`; a new effect states its reason with `// oxlint-disable-next-line t3code/react-effect-budget -- <why>`.
- Our users drive agents all day and notice a dropped frame, a lying spinner, and a stale label. No continuously repainting animations; they peg the GPU on high-refresh displays.
- If a rule here fights the task in front of you, say so loudly and get a human sign-off before breaking it.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ We are (mostly) not accepting contributions yet. Small fixes may be considered.

Full docs live in [docs/](./docs). There's no docs site yet.

- [FAQ](./docs/user/faq.md)
- [Install and first run](./docs/user/install.md)
- [Permission modes](./docs/user/permission-modes.md)
- [Keyboard shortcuts](./docs/user/keybindings.md)
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/app/DesktopAppActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,10 @@ export const make = Effect.gen(function* () {
webContents.send(DESKTOP_APP_ACTIVATION_REQUEST_CHANNEL, request);
});
}),
complete: (response) => Effect.sync(() => broker.complete(response)),
// The IPC handler owns the activation-completion span.
complete: Effect.fnUntraced(function* (response) {
return yield* Effect.sync(() => broker.complete(response));
}),
});
});

Expand Down
4 changes: 1 addition & 3 deletions apps/desktop/src/app/DesktopClerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ vi.mock("@clerk/electron", () => ({
vi.mock("@clerk/electron/storage", () => ({
storage: storageMock,
}));

import * as Option from "effect/Option";
import * as Exit from "effect/Exit";
import * as FileSystem from "effect/FileSystem";
import * as ElectronApp from "../electron/ElectronApp.ts";
Expand Down Expand Up @@ -297,7 +295,7 @@ it.effect(
}),
} as unknown as ElectronApp.ElectronApp["Service"];
const electronWindow = {
currentMainOrFirst: Effect.succeed(Option.some(window)),
currentMainOrFirst: Effect.succeedSome(window),
reveal: () => Effect.sync(() => revealed.resolve()),
} as unknown as ElectronWindow.ElectronWindow["Service"];
return Effect.gen(function* () {
Expand Down
8 changes: 7 additions & 1 deletion apps/desktop/src/app/DesktopCliCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,15 @@ it.layer(NodeServices.layer)("DesktopCliCommand", (it) => {
const command = yield* commandIn({ home });
const link = path.join(home, ".local", "bin", "t3");

expect(yield* command.state).toEqual({ supported: true, installedPath: null, onPath: false });
expect(yield* command.state).toEqual({
supported: true,
installedPath: null,
onPath: false,
launcherPath: null,
});
const installed = yield* command.install;
expect(installed.installedPath).toBe(link);
expect(installed.launcherPath).toBe(path.join(home, ".t3", "bin", "t3"));
expect(yield* fs.readLink(link)).toBe(path.join(home, ".t3", "bin", "t3"));
expect((yield* command.install).installedPath).toBe(link);

Expand Down
16 changes: 12 additions & 4 deletions apps/desktop/src/app/DesktopCliCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,26 @@ export const make = Effect.gen(function* () {

const state: DesktopCliCommand["Service"]["state"] = Effect.gen(function* () {
if (!environment.isPackaged) {
return { supported: false, installedPath: null, onPath: false } as const;
return { supported: false, installedPath: null, onPath: false, launcherPath: null } as const;
}
const launcherPath = (yield* exists(launcher)) ? launcher : null;
const installed = yield* installedAt;
const shadowedBy = yield* foreignFirstOnPath;
const shadow = Option.isSome(shadowedBy) ? { shadowedBy: shadowedBy.value } : {};
if (Option.isNone(installed)) {
return { supported: true, installedPath: null, onPath: false, ...shadow };
return { supported: true, installedPath: null, onPath: false, launcherPath, ...shadow };
}
const first = yield* firstOnPath;
const onPath = windows || (Option.isSome(first) && (yield* isOurLink(first.value)));
return { supported: true, installedPath: installed.value, onPath, ...shadow };
}).pipe(Effect.orElseSucceed(() => ({ supported: false, installedPath: null, onPath: false })));
return { supported: true, installedPath: installed.value, onPath, launcherPath, ...shadow };
}).pipe(
Effect.orElseSucceed(() => ({
supported: false,
installedPath: null,
onPath: false,
launcherPath: null,
})),
);

const ensureLauncher = DesktopCliShim.install.pipe(
Effect.provideService(DesktopEnvironment.DesktopEnvironment, environment),
Expand Down
169 changes: 86 additions & 83 deletions apps/desktop/src/app/DesktopObservability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,90 +459,91 @@ const makeBackendOutputLogShape = (
): Effect.Effect<DesktopBackendOutputLogShape> =>
Option.match(sink, {
onNone: () => Effect.succeed(DesktopBackendOutputLogNoop),
onSome: (logFile) =>
Effect.gen(function* () {
const sessionRef = yield* Ref.make(Option.none<BackendOutputSession>());
const writeFailure = Effect.fn("desktop.observability.backendOutput.writeFailure")(
function* (session: BackendOutputSession, details: string) {
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process failure output start",
level: "ERROR",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
phase: "START",
details: session.startDetails,
},
});
for (const output of session.chunks) {
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process output",
level: output.streamName === "stderr" ? "ERROR" : "INFO",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
stream: output.streamName,
text: textDecoder.decode(output.chunk.subarray(output.offset)),
},
});
}
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process failure output end",
level: "ERROR",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
phase: "END",
details: sanitizeLogValue(details),
},
});
// The enclosing operation owns the span for this inline handler.
onSome: Effect.fnUntraced(function* (logFile) {
const sessionRef = yield* Ref.make(Option.none<BackendOutputSession>());
const writeFailure = Effect.fn("desktop.observability.backendOutput.writeFailure")(function* (
session: BackendOutputSession,
details: string,
) {
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process failure output start",
level: "ERROR",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
phase: "START",
details: session.startDetails,
},
);
return {
beginSession: Effect.fn("desktop.observability.backendOutput.beginSession")(function* ({
details,
}) {
const runId = yield* currentDesktopRunId;
yield* Ref.set(
sessionRef,
Option.some({
runId,
startDetails: sanitizeLogValue(details),
chunks: [],
byteLength: 0,
}),
);
}),
writeOutputChunk: Effect.fnUntraced(function* (streamName, chunk) {
if (environment.isDevelopment) {
yield* writeDevelopmentConsoleOutput(streamName, chunk);
}
yield* Ref.update(
sessionRef,
Option.map((session) => appendBoundedOutputChunk(session, streamName, chunk)),
);
}),
persistFailureSnapshot: Effect.fn(
"desktop.observability.backendOutput.persistFailureSnapshot",
)(function* ({ details }) {
const session = yield* Ref.get(sessionRef);
if (Option.isSome(session)) {
yield* writeFailure(session.value, details);
}
}),
persistFailure: Effect.fn("desktop.observability.backendOutput.persistFailure")(
function* ({ details }) {
const session = yield* Ref.modify(sessionRef, (current) => [current, Option.none()]);
if (Option.isNone(session)) return;
yield* writeFailure(session.value, details);
});
for (const output of session.chunks) {
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process output",
level: output.streamName === "stderr" ? "ERROR" : "INFO",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
stream: output.streamName,
text: textDecoder.decode(output.chunk.subarray(output.offset)),
},
),
discardSession: Ref.set(sessionRef, Option.none()),
} satisfies DesktopBackendOutputLogShape;
}),
});
}
yield* writeBackendChildLogRecord(logFile, {
message: "backend child process failure output end",
level: "ERROR",
annotations: {
component: "desktop-backend-child",
runId: session.runId,
instanceId: id,
phase: "END",
details: sanitizeLogValue(details),
},
});
});
return {
beginSession: Effect.fn("desktop.observability.backendOutput.beginSession")(function* ({
details,
}) {
const runId = yield* currentDesktopRunId;
yield* Ref.set(
sessionRef,
Option.some({
runId,
startDetails: sanitizeLogValue(details),
chunks: [],
byteLength: 0,
}),
);
}),
writeOutputChunk: Effect.fnUntraced(function* (streamName, chunk) {
if (environment.isDevelopment) {
yield* writeDevelopmentConsoleOutput(streamName, chunk);
}
yield* Ref.update(
sessionRef,
Option.map((session) => appendBoundedOutputChunk(session, streamName, chunk)),
);
}),
persistFailureSnapshot: Effect.fn(
"desktop.observability.backendOutput.persistFailureSnapshot",
)(function* ({ details }) {
const session = yield* Ref.get(sessionRef);
if (Option.isSome(session)) {
yield* writeFailure(session.value, details);
}
}),
persistFailure: Effect.fn("desktop.observability.backendOutput.persistFailure")(function* ({
details,
}) {
const session = yield* Ref.modify(sessionRef, (current) => [current, Option.none()]);
if (Option.isNone(session)) return;
yield* writeFailure(session.value, details);
}),
discardSession: Ref.set(sessionRef, Option.none()),
} satisfies DesktopBackendOutputLogShape;
}),
});

const layerBackendOutputLogFactory = Layer.effect(
Expand Down Expand Up @@ -589,7 +590,9 @@ const layerBackendOutputLogFactory = Layer.effect(
});

return DesktopBackendOutputLogFactory.of({
forInstance: (id) => makeForId(id),
forInstance: Effect.fn("DesktopBackendOutputLogFactory.forInstance")(function (id) {
return makeForId(id);
}),
});
}),
);
Expand Down
3 changes: 1 addition & 2 deletions apps/desktop/src/app/DesktopWebLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { assert, describe, it } from "@effect/vitest";
import * as NodeEvents from "node:events";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";

import * as ElectronWindow from "../electron/ElectronWindow.ts";
import { WEB_LINK_OPEN_CHANNEL } from "../ipc/channels.ts";
Expand All @@ -26,7 +25,7 @@ const makeWindow = () => {
Layer.succeed(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
currentMainOrFirst: Effect.succeed(Option.some(window)),
currentMainOrFirst: Effect.succeedSome(window),
reveal: (target: Electron.BrowserWindow) => Effect.sync(() => void revealed.push(target)),
} as unknown as ElectronWindow.ElectronWindow["Service"]),
),
Expand Down
15 changes: 7 additions & 8 deletions apps/desktop/src/app/DesktopWebLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,15 @@ const make = Effect.gen(function* () {
});

return DesktopWebLinks.of({
receive: (url) =>
Effect.suspend(() => {
receive: Effect.fn("DesktopWebLinks.receive")(function (url) {
return Effect.suspend(() => {
pending.push(url);
return flush;
}).pipe(Effect.withSpan("DesktopWebLinks.receive")),
setRendererReady: (ready) =>
(ready ? startListening : Effect.sync(stopListening)).pipe(
Effect.andThen(flush),
Effect.withSpan("DesktopWebLinks.setRendererReady"),
),
});
}),
setRendererReady: Effect.fn("DesktopWebLinks.setRendererReady")(function (ready) {
return ready ? startListening : Effect.sync(stopListening);
}, Effect.andThen(flush)),
});
});

Expand Down
Loading