You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(cli): t3 thread list and show sign in read-only with a short-lived session - #36055
Cause: withThreadTools in apps/server/src/cli/thread.ts issued every t3 thread session with access "full-access" and the 30-day MCP_CLIENT_SESSION_TTL, and relied only on acquireRelease to revoke it, which does not run when the CLI is killed. Fix: withThreadTools now takes the access level and whether the command waits. list and show request "read-only" (orchestration:read only). start and send keep "full-access" because they launch and message threads. EnvironmentAuth.issueMcpClientSession gained an optional ttl (default unchanged, so McpOAuth is unaffected), and the CLI passes 10 minutes for every command that does not --wait. A killed list, show, start or send now leaves at most a 10-minute session, which is read-only for reads. Commands with --wait keep the default lifetime because a wait has no upper bound and sessions cannot be renewed. Reproduced first: the repro branch's test failed with both commands requesting full-access. I extended that test with an assertion that list and show get a 10-minute session. No new regexes or parsers.
On origin/main a2be070, I ran node apps/server/src/bin.ts serve --base-dir <fresh dir> --port 37991 with a fresh database, then t3 project add <dir> and t3 thread list --project <dir> --base-dir <same>. Read-only queries of auth_sessions showed a 't3 thread cli' row for each thread list call (including one that failed with 'No T3 Code project matches'). Each row had scopes [orchestration:read, orchestration:operate, settings:write, source-control:write] and expires_at 30 days after issued_at, and was revoked about 100ms later. I then started t3 thread list in the background, waited for its session row, and sent SIGKILL to the captured PID (exit 137). t3 auth session list --json still listed that session as active, with the same four scopes and expiresAt 2026-12-13 (issued 2026-11-13). A unit test on branch factory/repro-36050 shows that both list and show request access 'full-access' instead of 'read-only'.
Evidence
Run identical commands against fresh real servers: register a synthetic project; list its threads; list a missing project; show a missing thread; SIGKILL another list command immediately after session issuance; inspect active sessions immediately and after 10 real minutes. Linux, Node CLI, isolated databases, loopback servers. Captures render recorded CLI output; video pauses are compressed and elapsed timestamps retained.
Base's killed session expires December 13 and remained active after the comparison interval. Head's killed session expired November 13 at 07:24:14 UTC and was absent from the active list at 07:24:20 UTC. Neither session was revoked.
Successful show of a populated thread was not exercised; show was verified through a real missing-thread request that issued a session.
Mutating commands and --wait session lifetimes were outside this reproduction and were not exercised.
Base's full 30-day expiry was read from its persisted timestamp; only head's 10-minute expiry was observed in real time.
Tests
vp test run src/cli/thread.test.ts (repro branch, before the fix): 1 failed, 13 passed, failing on full-access vs read-only. After the fix: vp test run src/cli/thread.test.ts src/auth/EnvironmentAuth.test.ts: 39 passed. vp run check:changed: format, lint, knip, graphql, typecheck (3 packages) and tests (3 files, 41 passed) all pass.
Not in this change
t3 thread start --wait and send --wait still issue a session with the 30-day default lifetime. Waits are unbounded and sessions cannot be extended, so bounding them needs a renewal/heartbeat mechanism.
…d session
`t3 thread` issued a full-access MCP session with the 30-day MCP TTL for every
subcommand, reads included, and revoked it only on a clean exit. A killed CLI
left a write-capable credential active for a month.
list and show now ask for read-only access. Sessions for commands that do not
--wait expire after 10 minutes, so one the CLI never revokes lapses soon after.
Fixes#36050
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macroscope's review found this PR requires human approval — The change narrows list/show sessions to read-only and limits non-waiting CLI sessions to 10 minutes, while preserving cleanup and the existing lifetime for waiting commands. These alter existing product defaults and require confirmation of the intended permission and expiration behavior.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
Thread CLI commands now select MCP session access and expiry options. list and show request read-only sessions with a 10-minute TTL. start and send request full-access sessions and pass their --wait setting. The 30-day default remains when no TTL is supplied.
issueMcpClientSession accepts an optional TTL. Session creation uses that TTL when supplied and retains the 30-day default otherwise.
Thread command session policy and tests apps/server/src/cli/thread.ts, apps/server/src/cli/thread.test.ts
withThreadTools now takes an access level and wait setting. list and show request read-only sessions and a 10-minute TTL. start and send request full access and pass their --wait setting; waiting commands omit the TTL. Tests verify the access and TTL for list and show.
pingdotgg/t3code#27117 — This change shortens the lifetime of sessions for commands that do not wait and makes list and show read-only; the issue’s session-row pruning concern is not addressed here.
Possibly related PRs
pingdotgg/t3code#34249 — Both PRs use the access and TTL options of EnvironmentAuth.issueMcpClientSession.
Suggested labels:bugfix
Suggested reviewers:t3dotgg, jo2234, maria-rcks
Merge Risk: Minimal · up to b27d6
The change makes thread list and show use read-only, short-lived sessions, while start and send keep full access. No merge-blocking risk was found.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: t3 thread list and show now sign in with read-only access and a short-lived session. It follows the conventional commit format and matches the changeset. It is spe…
Linked Issues check
Scope: #36050 is the only active directly linked issue. Its coding requirements are (1) list and show should use a read-only session, and (2) a session left active by a killed CLI should expire within…
Out of Scope Changes check
The whole-PR diff (a2be070 to b27d610) touches three source files, and each change serves #36050: - apps/server/src/auth/EnvironmentAuth.ts: adds optional ttl to issueMcpClientSession, defaulting to t…
Description check
The description follows the repository template closely and covers all required sections. Problem: withThreadTools issued every t3 thread session with full-access and the 30-day TTL, and revocation re…
Full details: Title check
Explanation
The title names the main change: t3 thread list and show now sign in with read-only access and a short-lived session. It follows the conventional commit format and matches the changeset. It is specific and accurate, so it passes. The title is 78 characters, which is long but acceptable.
Full details: Linked Issues check
Explanation
Scope: #36050 is the only active directly linked issue. Its coding requirements are (1) list and show should use a read-only session, and (2) a session left active by a killed CLI should expire within minutes, not 30 days.
Evidence at head b27d610 (diff against merge base a2be070):
Requirement 1 is met. threadListCommand and threadShowCommand call withThreadTools with { access: "read-only", waits: false }. The issued session therefore uses mcpClientScopes("read-only"), which the issue identifies as [orchestration:read].
Requirement 2 is met for list and show. withThreadTools passes ttl: CLI_SESSION_TTL (Duration.minutes(10)) whenever waits is false. EnvironmentAuth.issueMcpClientSession now accepts an optional ttl and uses input.ttl ?? MCP_CLIENT_SESSION_TTL, so the default 30-day lifetime is unchanged for other callers.
Start and send keep full-access, since they launch and message threads. With --wait, they keep the 30-day default. The PR states that a wait has no upper bound and sessions cannot be renewed, so this is a disclosed residual gap, not an omission. The issue's expected-behavior wording ('a CLI session that is never revoked does not outlive the command by much') is not fully met for start --wait and send --wait.
The author reports a test assertion for the 10-minute read-only session for list and show, and a failing repro-branch test before the fix. I did not inspect the test file; this relies on the author's report.
Full details: Out of Scope Changes check
Explanation
The whole-PR diff (a2be070 to b27d610) touches three source files, and each change serves #36050:
apps/server/src/auth/EnvironmentAuth.ts: adds optional ttl to issueMcpClientSession, defaulting to the existing MCP_CLIENT_SESSION_TTL. This supplies the 10-minute lifetime and keeps other callers unchanged.
apps/server/src/cli/thread.ts: adds the access and waits parameters to withThreadTools, sets CLI_SESSION_TTL, and passes access per command. Changes are limited to the thread CLI session path.
apps/server/src/cli/thread.test.ts: test coverage for the new access and TTL behavior, per the author's report.
The PR does not change pruning of session rows (#27117). The PR description lists this as excluded, and the diff does not touch it. The Evidence section in the PR description is a placeholder and carries no code. No unrelated code changes were found.
Full details: Description check
Explanation
The description follows the repository template closely and covers all required sections. Problem: withThreadTools issued every t3 thread session with full-access and the 30-day TTL, and revocation relied on acquireRelease, which does not run when the CLI is killed. Change: list and show now request read-only access, and the CLI passes a 10-minute TTL for commands without --wait; EnvironmentAuth.issueMcpClientSession gained an optional ttl with the default unchanged. Scope: the issue #36050 is linked and the change is a bug fix. Verification: a reproduction on main with before/after captures, a failing-then-passing unit test, and listed vp test and check runs. Limitations are stated (unexercised show of populated threads, mutating commands, --wait lifetimes). The author also names follow-up items that are out of scope (--wait sessions, #27117 row pruning). The model and harness are identified as the template requests.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Reviewing files that changed from the base of the PR and between 5f85768 and b27d610.
Files selected for processing: 3
apps/server/src/auth/EnvironmentAuth.ts
apps/server/src/cli/thread.test.ts
apps/server/src/cli/thread.ts
Limit details: You’ve used all 10 included reviews currently available.
This branch has not been deployed
No deployments
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
size:S10-29 changed lines (additions + deletions).vouch:trustedAuthor is trusted by repo permissions or the VOUCHED list.
1 participant
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.
Cause: withThreadTools in apps/server/src/cli/thread.ts issued every
t3 threadsession with access "full-access" and the 30-day MCP_CLIENT_SESSION_TTL, and relied only on acquireRelease to revoke it, which does not run when the CLI is killed. Fix: withThreadTools now takes the access level and whether the command waits. list and show request "read-only" (orchestration:read only). start and send keep "full-access" because they launch and message threads. EnvironmentAuth.issueMcpClientSession gained an optionalttl(default unchanged, so McpOAuth is unaffected), and the CLI passes 10 minutes for every command that does not --wait. A killed list, show, start or send now leaves at most a 10-minute session, which is read-only for reads. Commands with --wait keep the default lifetime because a wait has no upper bound and sessions cannot be renewed. Reproduced first: the repro branch's test failed with both commands requesting full-access. I extended that test with an assertion that list and show get a 10-minute session. No new regexes or parsers.Fixes #36050
Reproduction on main
On origin/main a2be070, I ran
node apps/server/src/bin.ts serve --base-dir <fresh dir> --port 37991with a fresh database, thent3 project add <dir>andt3 thread list --project <dir> --base-dir <same>. Read-only queries of auth_sessions showed a 't3 thread cli' row for eachthread listcall (including one that failed with 'No T3 Code project matches'). Each row had scopes [orchestration:read, orchestration:operate, settings:write, source-control:write] and expires_at 30 days after issued_at, and was revoked about 100ms later. I then startedt3 thread listin the background, waited for its session row, and sent SIGKILL to the captured PID (exit 137).t3 auth session list --jsonstill listed that session as active, with the same four scopes and expiresAt 2026-12-13 (issued 2026-11-13). A unit test on branch factory/repro-36050 shows that bothlistandshowrequest access 'full-access' instead of 'read-only'.Evidence
Run identical commands against fresh real servers: register a synthetic project; list its threads; list a missing project; show a missing thread; SIGKILL another list command immediately after session issuance; inspect active sessions immediately and after 10 real minutes. Linux, Node CLI, isolated databases, loopback servers. Captures render recorded CLI output; video pauses are compressed and elapsed timestamps retained.
Base's killed session expires December 13 and remained active after the comparison interval. Head's killed session expired November 13 at 07:24:14 UTC and was absent from the active list at 07:24:20 UTC. Neither session was revoked.
Reproduction driver · Capture renderer
Not shown:
Tests
vp test run src/cli/thread.test.ts (repro branch, before the fix): 1 failed, 13 passed, failing on full-access vs read-only. After the fix: vp test run src/cli/thread.test.ts src/auth/EnvironmentAuth.test.ts: 39 passed. vp run check:changed: format, lint, knip, graphql, typecheck (3 packages) and tests (3 files, 41 passed) all pass.
Not in this change
t3 thread start --waitandsend --waitstill issue a session with the 30-day default lifetime. Waits are unbounded and sessions cannot be extended, so bounding them needs a renewal/heartbeat mechanism.Who did the work
Run by the t3code factory in T3 Code.