Repository navigation
perf(cli): read long threads from the end instead of paging through them - #6
Draft
fagnersales wants to merge 80 commits into
Draft
fagnersales wants to merge 80 commits into
fagnersales wants to merge 80 commits into
Conversation
Enable prepared server bytecode on native Linux ARM64 desktop builds and install the Electron runtime libraries required to generate it on headless Linux runners. Keep host compatibility checks and extend the existing tests. Document the native ARM64 command and supported build prerequisites. Validated with check:changed, native ARM64 tests, AppImage and .deb builds, and packaged Electron accepting the prepared CLI and server bytecode after extraction. CI and bot reviews passed on 8f89392. Implemented and verified by GPT 6.1 Sol through the Codex harness.
t3_thread_read only paged forward, and every page builds the thread's whole visible index on the server. `t3 thread show` on old CLIs and the result read behind `send/start --wait` (and a linked environment's task result) paged through the entire thread, so a 20k-item thread took about a minute. t3_thread_read now takes `beforePosition` (the newest `limit` matches before it) and returns `previousPosition`. The CLI and RemoteDelegation read backward from the newest item and stop once they have what they need, falling back to the forward read when an older server rejects the field. The CLI now reports a server's invalid-params answer as a tool failure instead of "could not reach the server". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RemoteDelegation.lastReply change overlaps pingdotgg#26664, which already reworks that read, so this PR keeps to the CLI's reads. The newest-position constant moves back into the CLI, its only user. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ons (pingdotgg#32934) Co-authored-by: GPT 6.1 Sol <codex@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: GPT 6.1 Sol <codex@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
…32573) Co-authored-by: GPT 6.1 Sol <codex@openai.com> Co-authored-by: Muse Wonder Full 1.0 <noreply@meta.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A server answering with a previousPosition at or past the requested beforePosition would make readBackward read the same page forever. Also shortens the new t3_thread_read wording (+171 bytes instead of +208). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…21592) Co-authored-by: maria-rcks <maria@kuuro.net>
…g#22295) Co-authored-by: maria-rcks <maria@kuuro.net>
A page past the position requested is a repeat, so its items no longer reach thread show's output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngdotgg#24084) Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>
…ngdotgg#28016) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Lars Nieuwenhuis <35393046+lnieuwenhuis@users.noreply.github.com>
…#28060) Co-authored-by: Nik <64274427+eyeveil@users.noreply.github.com>
Co-authored-by: maria-rcks <maria@kuuro.net>
…tgg#26724) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Yash Singh <saiansh2525@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#33109) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…7733) Co-authored-by: maria-rcks <maria@kuuro.net>
…s reload over gzip HTTP (pingdotgg#32287) Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…kout (pingdotgg#32927) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…32941) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…Code (pingdotgg#33134) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…3177) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ngdotgg#33189) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…cript cleanup (pingdotgg#33147) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…earch primitives (pingdotgg#33193) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… unchecked (pingdotgg#27353) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rash Android (pingdotgg#32812) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#24482) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net>
…nal displays (pingdotgg#19064) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: maria-rcks <maria@kuuro.net>
…ingdotgg#32995) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…5532) Co-authored-by: maria-rcks <maria@kuuro.net>
… blank tile (pingdotgg#21163) Co-authored-by: T3 Code Test <t3code-test@example.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: shivamhwp <shivamhwp@users.noreply.github.com> Co-authored-by: maria-rcks <maria@kuuro.net>
…27576) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Julius Marminge <julius0216@outlook.com>
…gdotgg#21652) Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Julius Marminge <julius0216@outlook.com>
…tgg#24933) Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…pingdotgg#31998) Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gg#25828) Co-authored-by: danddanddand <47586871+danddanddand@users.noreply.github.com> Co-authored-by: whoisaldo <whois.younes@gmail.com> Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: juliusmarminge <julius0216@outlook.com> Co-authored-by: Claudesaul <139932571+Claudesaul@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gg#31207) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net>
…tgg#27753) Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net>
… Android (pingdotgg#28524) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#28125) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… on iPhone Duo (pingdotgg#33213) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…dotgg#24079) Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: GPT-6.1 Sol <noreply@openai.com> Co-authored-by: maria-rcks <maria@kuuro.net>
# Conflicts: # apps/server/src/provider/agentContextBudget.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
t3 thread showon "Auto Delivery" takes ~62 sMeasured with the installed
t3 v0.1.5-nightly.20261029.3877on Fagner's threadba761708…(1,789 runs, 20,944 turn items, a 36 GBstatev2.sqlite).thread show --messages 2takes ~62 s,--messages 50~42 s,--messages 200~86 s. The CLI spends about 2 s of CPU; the rest is waiting on the server.1. The client reads the whole thread, page by page.
thread showcallsreadTimeline, which pagest3_thread_readforward from position 0 in pages of 100 untilhasMoreis false, then keeps the last N messages.main, perf(cli): thread show reads only the end of long threads pingdotgg/t3code#24087 (Oct 30, one day after this nightly) already madethread showstart near the end (cli/thread.ts:344). It still needs 2–7 calls because the server can only page forward.main:runResultText(cli/thread.ts:419), the result read behindthread send/start --wait. It reads themessagesview, or the fullactivityview when a run failed.RemoteDelegation.lastReply(peer/RemoteDelegation.ts:439), the result of a task delegated to a linked environment.2. Every page builds the whole thread's index on the server.
getTimelinePage(ProjectionStore.ts:5981) callsreadTimelineIndex(ProjectionStore.ts:5711) for every page. That function:json_extracton message payloads and noLIMIT;Positions are ranks in that filtered list, so they can't simply become an SQL keyset over
ordinal. A full read is therefore pages × whole thread.Where a page's time goes. One trace of
t3_thread_readon a synthetic 20,680-item thread, 171 ms in total:getThreadRecords(messages)On the live server, the trace file (read only) shows
readTimelineIndexat p50 162 ms, p90 661 ms and max 6.2 s, andreadThreadat p50 338 ms. A cold run of the index SQL statement on the real thread took 0.82 s; warm, it took 0.05 s.So the 62 s is about 50 message pages at ~1.2 s each on a busy server, and the 86 s for
--messages 200comes from the same full read.The fix (additive)
t3_thread_readgainsbeforePosition. It returns the newestlimitmatches before that position, oldest first. Every result now includespreviousPosition: the cursor for the page before this one, ornullwhen nothing older is left.afterPosition,nextPositionandhasMorekeep their meaning.ProjectionTimelinePagegainshasEarlier.hasMoreandhasEarlierboth describe the view's items beyond the page, whatever position bounds the request had.readBackwardreads from the newest item and stops as soon as it has what it needs.thread show --messages Nasks only for the messages still missing, up to 100 per page, so--messages 2downloads 2 items instead of up to 100 items of 20k characters each.runResultText(--wait) stops at the page that holds the run's result, usually the first.RemoteDelegation.lastReplyreads backward the same way. It stops at the run's reply, or at its error for a failed run.t3_thread_readis a strict tool, so an older server rejects the unknown field with JSON-RPC-32602.AcpMcpBridgeErrornow carries the JSON-RPCcode.ThreadToolFailedErrorinstead of "Could not reach the T3 Code server".readBackwardfalls back to the existing forward algorithm. A response withoutpreviousPositionfalls back too.Docs:
docs/orchestration-v2/orchestrator-mcp-server.md(thet3_thread_readparagraph). Agent context budget: +208 bytes in the MCP tool list (agentContextBudget.json).Numbers
Synthetic thread seeded through the real
EventStoreandProjectionStore: 1,880 runs, 20,680 items (user and assistant messages, reasoning, commands with ~2.4 KB of output), a 171 MB DB. It ran on a temporary server with--base-dir /tmp/t3bench, never against~/.t3. Times are server time measured from an MCP client, on a quiet machine:mainthread show --messages 2thread show --messages 200--waitresult (assistant reply)--waitresult (failed run,activityview)End to end, including ~1–2 s of CLI startup:
--messages 2and 16.5 s for--messages 200.A second run on a loaded machine (~650 ms per call) gave the same call counts: full read 38.5 s,
main1.1 s / 4.5 s, this PR 0.86 s / 1.8 s.Estimate for "Auto Delivery" (20,944 items, ~1.2 s per call on the live server):
thread show --messages 2thread show --messages 200--waitresult (reply)--waitresult (failed run)A nightly that only includes pingdotgg#24087 would already bring
thread showdown to 2–7 calls. This PR adds the--waitand peer reads, and saves the remaining extra calls.Compatibility, checked by hand:
beforePosition, the CLI falls back, and the output is identical tomain's for--messages 2and200.previousPositionis an extra output field, and older decoders ignore it.Tests
ProjectionStore.test.ts: the fork-inheritance timeline test now checksbeforePositionpages andhasEarlieracross inherited and synthetic rows.cli/thread.test.ts:thread showcases run against both a current server and a legacy one that rejectsbeforePosition.main.RemoteDelegation.test.ts: "a task's result is its own run's whole last reply, not a later run's" runs against a box that reads backward and against one that only reads forward.vp run check:changed: all pass (format, lint, knip, typecheck of 36 packages, tests beside the changes).Risks
orchestration_v2_projection_turn_items(it would let the index SQL skip the table rows): building it would run over the whole 36 GB database at startup.t3_thread_readdescription, +208 bytes). Themcp-tool-useevals were not run: there is no API key in this checkout and a full run costs ~$9–17. Upstream CI also requires theagent-context-increaselabel for budget growth.Number.MAX_SAFE_INTEGER(THREAD_READ_NEWEST_POSITIONin contracts). Agents are told to use the thread'sitemCount.Benchmark scripts (not committed)
seed.tsbuilds the synthetic thread throughEventStore+ProjectionStoreinto<baseDir>/userdata/statev2.sqlite.read.tssigns in as an MCP client of a running server and times the read patterns: the nightly full read,main'sreadLatestMessages, and this branch's. Both live in/tmp/t3bench-scripts/on Fagner's Mac. Copy them intoapps/server/.bench/to run them, and copymain'scli/thread.tstosrc/cli/thread.mainbench.tsfor themain:mode.Upstream PR text (for
pingdotgg/t3code, if Fagner sends it)Title:
perf(cli): read long threads from the end instead of paging through themBody:
On a thread with ~20k items,
t3 thread send --waitand oldert3 thread showbuilds took about a minute, and a linked environment's task result read the same way. They pagedt3_thread_readforward through the whole thread to keep its last few items, and every page rebuilds the thread's whole visible index on the server (ProjectionStore.readTimelineIndex). The cost is pages × thread length. On a synthetic 20,680-item thread, the--waitresult took 57 calls (11 s), or 207 calls (47 s) for a failed run.t3_thread_readnow takesbeforePosition(the newestlimitmatches before it) and returnspreviousPosition:thread show, its--waitresult, andRemoteDelegation.lastReplyread backward from the newest item and stop once they have what they need: 1–2 calls instead of 2–207.Before and after, on a synthetic thread with 1,880 runs and 20,680 items:
main)thread show --messages 2thread show --messages 200--waitresult--waitresult, failed runNeeds the
agent-context-increaselabel (+208 bytes in the tool list). Themcp-tool-useevals have not been run yet.Claude Opus 5.5 in Claude Code (T3 Code)
🤖 Generated with Claude Code