Skip to content

Commit b98197e

Browse files
committed
merge: integrate main into queued message edit fix
2 parents fdcd21b + 7195b69 commit b98197e

452 files changed

Lines changed: 13754 additions & 2196 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/scripts/ci-changes.cjs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ const EXTRA_OWNERS = [
2727
[/^native\/browser-secret\//, ["@t3tools/desktop"]],
2828
[/^native\/kde-snap-shot\//, ["@t3tools/desktop"]],
2929
[/^native\/libghostty-vt\//, ["@t3tools/web"]],
30+
// apps/server/src/cloud/ConnectReachability.test.ts checks the guide's headings.
31+
[/^docs\/user\/t3-connect-troubleshooting\.md$/, ["t3"]],
3032
];
3133
const RUST = /^native\/(resource-monitor|kde-snap-shot|hyprland-snap-shot)\//;
3234

@@ -92,11 +94,11 @@ function readWorkspace() {
9294

9395
// [] means inert; null means unknown and requires every job.
9496
function ownersOf(file, packages) {
95-
if (INERT.some((pattern) => pattern.test(file))) return [];
97+
const extra = EXTRA_OWNERS.flatMap(([pattern, names]) => (pattern.test(file) ? names : []));
98+
if (INERT.some((pattern) => pattern.test(file))) return extra;
9699
// A manifest can change dependencies or versions other packages read.
97100
if (path.posix.basename(file) === "package.json") return null;
98101
if (RUNS_EVERYTHING.some((pattern) => pattern.test(file))) return null;
99-
const extra = EXTRA_OWNERS.flatMap(([pattern, names]) => (pattern.test(file) ? names : []));
100102
// scripts/build-desktop-artifact.test.ts reads the crates' sources.
101103
if (RUST.test(file)) return ["@t3tools/scripts", ...extra];
102104

‎.macroscope/check-run-agents/ui-consistency.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ The reference for how this codebase wants UI built is the composer banner system
4444

4545
- `ComposerBanner.tsx` exports one object of small slot components (`Root`, `Row`, `Icon`, `Content`, `Actions`, `Dismiss`, `Children`, and so on). Each slot owns its own Tailwind classes, exposes a `data-slot` attribute, takes `className` and spreads the rest of its props, and uses `cn` so callers can adjust without overriding. Variants and density are props on `Root`, not ad hoc class strings at call sites. Where a slot needs a real control it renders `Button` from `ui/` rather than a styled `<button>`.
4646
- `ComposerBannerStack.tsx` composes those slots into the ordered stack and owns only stack behavior (priority, expand and collapse, dismiss transitions).
47-
- Consumers such as `ComposerActivityStatus.tsx`, `ComposerStashBadge.tsx`, and `ComposerPlanFollowUpBanner.tsx` are a few lines of `<ComposerBanner.Row><ComposerBanner.Icon/><ComposerBanner.Content>…` with no styling of their own.
47+
- Consumers such as `ComposerPlanFollowUpBanner.tsx` are a few lines of `<ComposerBanner.Row><ComposerBanner.Icon/><ComposerBanner.Content>…` and style only their own content.
4848

4949
Hold new and changed UI to that shape:
5050

‎README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
T3 Code is an "agent harness control surface". It enables control of the agents on your machine with a best-in-class mobile app ([iOS](https://apps.apple.com/us/app/t3-code-remote-claude-more/id6787819824), [Android](https://play.google.com/store/apps/details?id=com.t3tools.t3code)), [web app](https://app.t3.codes) and [Electron-based desktop app](https://t3.codes).
44

5-
Works with your subscriptions on Claude Code, Codex, Cursor, Grok Build, OpenCode, and Google Antigravity. If they're set up on your computer, T3 Code can control them.
5+
Works with your subscriptions on Claude Code, Codex, Cursor, Grok Build, OpenCode, Google Antigravity, and fx. If they're set up on your computer, T3 Code can control them.
66

77
## "Wait, what are you selling me?"
88

@@ -13,14 +13,15 @@ We wanted something performant, remote-ready, and truly open. If we ever go the
1313
## Installation
1414

1515
> [!WARNING]
16-
> T3 Code currently supports Codex, Claude, Cursor, Grok Build, OpenCode, and Antigravity. Install and authenticate at least one provider before use:
16+
> T3 Code currently supports Codex, Claude, Cursor, Grok Build, OpenCode, Antigravity, and fx. Install and authenticate at least one provider before use:
1717
>
1818
> - Codex: install [Codex CLI](https://developers.openai.com/codex/cli) and run `codex login`
1919
> - Claude: install [Claude Code](https://claude.com/product/claude-code) and run `claude auth login`
2020
> - Cursor: install [Cursor CLI](https://cursor.com/cli) and run `agent login`
2121
> - Grok Build: install [Grok Build CLI](https://x.ai/cli) and run `grok login`
2222
> - OpenCode: install [OpenCode](https://opencode.ai) and run `opencode auth login`
2323
> - Antigravity: enable it in Settings, then use **Install Antigravity** and **Sign in with Google**. No CLI is required.
24+
> - fx: install [fx](https://fx.sh) and run `fx login` on the machine running the T3 Code server ([setup guide](./docs/user/providers-fx.md))
2425
2526
### Command line
2627

‎apps/desktop/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/desktop",
3-
"version": "0.1.5",
3+
"version": "0.1.6",
44
"private": true,
55
"type": "module",
66
"main": "dist-electron/boot.cjs",

‎apps/desktop/src/app/DesktopEarlyElectronStartup.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ type EarlyLinuxElectronOptionsInput = EarlyDesktopSettingsInput;
2626

2727
export interface EarlyLinuxElectronOptions {
2828
readonly isDevelopment: boolean;
29-
readonly linuxWmClass: string;
3029
readonly linuxDesktopEntryName: string;
3130
readonly passwordStore: LinuxPasswordStoreSwitch | null;
3231
}
@@ -146,7 +145,6 @@ export function resolveEarlyLinuxElectronOptions(
146145
const isDevelopment = isDevelopmentEnvironment(input.env);
147146
return {
148147
isDevelopment,
149-
linuxWmClass: isDevelopment ? "t3code-dev" : "t3code",
150148
linuxDesktopEntryName: resolveLinuxDesktopEntryName(isDevelopment),
151149
passwordStore: resolveLinuxPasswordStoreSwitch({
152150
preference,

‎apps/desktop/src/app/DesktopEnvironment.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export class DesktopEnvironment extends Context.Service<
7575
readonly displayName: string;
7676
readonly appUserModelId: string;
7777
readonly linuxDesktopEntryName: string;
78-
readonly linuxWmClass: string;
7978
readonly linuxApplicationsDir: string;
8079
readonly desktopLauncherPath: Option.Option<string>;
8180
readonly appImagePath: Option.Option<string>;
@@ -232,7 +231,6 @@ const make = Effect.fn("desktop.environment.make")(function* (
232231
isDevelopment ? "com.t3tools.t3code.dev" : "com.t3tools.t3code",
233232
),
234233
linuxDesktopEntryName: resolveLinuxDesktopEntryName(isDevelopment),
235-
linuxWmClass: isDevelopment ? "t3code-dev" : "t3code",
236234
linuxApplicationsDir,
237235
desktopLauncherPath: config.desktopLauncherPath,
238236
appImagePath: config.appImagePath,

‎apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ const makeEnvironment = (path: Path.Path, overrides: Record<string, unknown> = {
3535
isDevelopment: false,
3636
displayName: "T3 Code (Alpha)",
3737
linuxDesktopEntryName: "com.t3tools.T3Code.desktop",
38-
linuxWmClass: "t3code",
3938
linuxApplicationsDir: "/home/alice/.local/share/applications",
4039
desktopLauncherPath: Option.none(),
4140
appImagePath: Option.some("/home/alice/Applications/T3-Code.AppImage"),

‎apps/desktop/src/app/DesktopPreReadyPlatform.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ export const make = Effect.gen(function* () {
121121
} catch {}
122122
// Chromium caches portal identity; set it before any asynchronous initialization.
123123
Electron.app.setDesktopName(linux.linuxDesktopEntryName);
124-
Electron.app.commandLine.appendSwitch("class", linux.linuxWmClass);
125124
if (linux.passwordStore !== null && linuxPasswordStoreCommandLine === null) {
126125
Electron.app.commandLine.appendSwitch("password-store", linux.passwordStore);
127126
}

‎apps/desktop/src/backend/DesktopBackendManager.test.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,4 +631,42 @@ describe("DesktopBackendManager", () => {
631631
}).pipe(Effect.provide(TestClock.layer())),
632632
),
633633
);
634+
635+
it.effect("waits out a slow startup and reports one that never becomes ready", () =>
636+
Effect.scoped(
637+
Effect.gen(function* () {
638+
const reports = yield* Queue.unbounded<DesktopBackendManager.BackendStartupFailure>();
639+
const startInstance = (status: Ref.Ref<number>, onReady?: Effect.Effect<void>) =>
640+
makeTestInstance({
641+
spawnerLayer: Layer.succeed(
642+
ChildProcessSpawner.ChildProcessSpawner,
643+
ChildProcessSpawner.make(() =>
644+
Effect.succeed(makeProcess({ exitCode: Effect.never })),
645+
),
646+
),
647+
httpClientLayer: layerHttpClient((request) =>
648+
Ref.get(status).pipe(Effect.map((code) => responseForRequest(request, code))),
649+
),
650+
onStartupFailure: (failure) => Queue.offer(reports, failure).pipe(Effect.asVoid),
651+
...(onReady ? { onReady } : {}),
652+
}).pipe(Effect.tap((instance) => instance.start));
653+
654+
const slowStatus = yield* Ref.make(503);
655+
const ready = yield* Deferred.make<void>();
656+
yield* startInstance(slowStatus, Deferred.succeed(ready, undefined).pipe(Effect.asVoid));
657+
yield* TestClock.adjust(Duration.seconds(66));
658+
yield* Ref.set(slowStatus, 200);
659+
yield* TestClock.adjust(Duration.millis(100));
660+
yield* Deferred.await(ready);
661+
assert.isTrue(Option.isNone(yield* Queue.poll(reports)));
662+
663+
yield* startInstance(yield* Ref.make(503));
664+
yield* TestClock.adjust(Duration.seconds(179));
665+
assert.isTrue(Option.isNone(yield* Queue.poll(reports)));
666+
yield* TestClock.adjust(Duration.seconds(1));
667+
const failure = yield* Queue.take(reports);
668+
assert.equal(failure.reason, "The server started but did not become ready in time.");
669+
}).pipe(Effect.provide(TestClock.layer())),
670+
),
671+
);
634672
});

‎apps/desktop/src/backend/DesktopBackendManager.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import * as DesktopWslEnvironment from "../wsl/DesktopWslEnvironment.ts";
3434
const INITIAL_RESTART_DELAY = Duration.millis(500);
3535
const MAX_RESTART_DELAY = Duration.seconds(10);
3636
const MAX_PREFLIGHT_FAILURE_ATTEMPTS = 5;
37-
const DEFAULT_BACKEND_READINESS_TIMEOUT = Duration.minutes(1);
37+
// Large state databases can take over a minute to become ready.
38+
const DEFAULT_BACKEND_READINESS_TIMEOUT = Duration.minutes(3);
3839
const DEFAULT_BACKEND_READINESS_INTERVAL = Duration.millis(100);
3940
const DEFAULT_BACKEND_READINESS_REQUEST_TIMEOUT = Duration.seconds(1);
4041
// Match the service launcher's grace period so backends can stop provider sessions.

0 commit comments

Comments
 (0)