Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
02076a6
feat(cli): open projects in the running desktop app (#8824)
t3dotgg Sep 1, 2026
847ba89
chore: vouch six repeat contributors (#9131)
t3dotgg Sep 1, 2026
b91dd14
chore: delete dead code, unused deps, and duplicate helpers (#9129)
t3dotgg Sep 2, 2026
ce34c73
fix(web): mute routine notices and update actions (#9063)
maria-rcks Sep 2, 2026
bb62ea0
fix(server): keep Pylon-live helpers through the upstream dead-code s…
rynfar Sep 3, 2026
1cd7291
perf(provider): bound persisted session lookups (#8909)
nateEc Sep 2, 2026
434ba6e
fix(claude): skills picked from the composer now run (#9128)
t3dotgg Sep 2, 2026
2e349a9
perf: make streaming projection and activity appends incremental (#9152)
t3dotgg Sep 2, 2026
7217a80
fix(web): stop highlighter freezes and worker spin by using the Onigu…
LetZico Sep 2, 2026
2a474e4
perf(client-runtime): keep turn and checkpoint refs stable while stre…
t3dotgg Sep 2, 2026
3846feb
perf(clients): lease sidebar status by visibility (#9052)
StiensWout Sep 2, 2026
c3cee83
fix(desktop): show newest changes in nightly previews (#9138)
t3dotgg Sep 2, 2026
3ae8a72
fix(web): keep generated muted foreground dimmer than entered text (#…
flamboh Sep 2, 2026
a761ee5
perf(web): halve the cold-start bundle by splitting Clerk and cold ro…
StiensWout Sep 2, 2026
df8999c
test(server): measure shell, second client, and reconnect transfer (#…
t3dotgg Sep 2, 2026
dcb7542
fix(web): darken neutral control surfaces (#9064)
maria-rcks Sep 2, 2026
760d228
fix(web): preserve panel state across workspace refreshes (#8968)
maria-rcks Sep 2, 2026
e8a4b9d
fix(web): compact project settings actions (#9160)
maria-rcks Sep 2, 2026
c955947
feat(pull-requests): copy provider checkout commands (#9086)
maria-rcks Sep 2, 2026
62c4336
fix(web): hide build pill in narrow sidebars (#9159)
flamboh Sep 2, 2026
1592795
fix(provider): drop removed custom models from the model picker (#9075)
StiensWout Sep 2, 2026
b5104c0
fix(web): label keybinding condition removal actions (#8664)
RakshithBhat03 Sep 2, 2026
cefa8e9
fix(contracts): accept legacy pull request checkout results (#8238)
jakeleventhal Sep 2, 2026
1599a17
feat(web): copy the full error report from the error page (#9166)
StiensWout Sep 2, 2026
b6441e6
fix(chat): replace failed tools with thinking (#9165)
maria-rcks Sep 2, 2026
1c41173
fix(web): open PR toast actions in app (#9006)
eimexdev Sep 2, 2026
f1397ef
fix(pull-requests): expand code tab diffs by default (#9174)
maria-rcks Sep 2, 2026
5ba1ab6
chore: bump vendored GhosttyKit and update terminal integration (#9155)
Yash-Singh1 Sep 2, 2026
a2a85a6
fix(web): copying a code block no longer copies triple backticks (#8448)
ipanasenko Sep 2, 2026
a8d11c4
fix(web): model info button opens its details on click (#9177)
StiensWout Sep 2, 2026
18c32f5
fix(web): avoid stale file writes on close (#8630)
yashranaway Sep 2, 2026
8eff40e
fix(server): bound OpenCode version probes (#8750)
yashranaway Sep 2, 2026
45400e9
fix(server): allow large Azure DevOps PR lists (#8572)
Lucenx9 Sep 2, 2026
e3bb6ee
fix(server): allow local-only worktree bases (#8751)
yashranaway Sep 2, 2026
80befb9
fix(web): remove projects with archived threads (#8798)
none23 Sep 2, 2026
5077da7
fix(connect): refresh relay credentials before expiry (#9178)
maria-rcks Sep 2, 2026
da77de8
fix(pull-requests): reuse github api reads (#9176)
maria-rcks Sep 2, 2026
7e2a1bc
fix(server): stop titling linked PR threads from local git history (#…
t3dotgg Sep 2, 2026
ce4af84
fix(mobile): prevent message and composer overlap (#9195)
juliusmarminge Sep 2, 2026
c0a355e
fix(web): preserve composer draft during worktree setup (#9197)
maria-rcks Sep 2, 2026
ae4e91a
fix(web): prevent two-digit list markers from being clipped (#9101)
G-R3 Sep 2, 2026
92e0ae6
fix(server): discover project skills for Claude (#9210)
anirudhsama Sep 2, 2026
002c226
fix(web): prevent connection rows from wrapping during removal (#8706)
MatthewFeroz Sep 2, 2026
3c85e32
fix(pull-requests): align checkout control with author (#9196)
maria-rcks Sep 2, 2026
1d55596
feat(mobile): long-press file references for path and open actions (#…
juliusmarminge Sep 2, 2026
4d81377
fix(desktop): exclude opposite macOS pty prebuilds (#9240)
extoci Sep 2, 2026
5d34054
feat(web): add copy path button to diff headers (#2403)
ipanasenko Sep 2, 2026
b37cdf6
fix(web): confirm closing agent-controlled browsers (#9272)
maria-rcks Sep 2, 2026
272de2d
refactor(shared): move the node:sqlite Effect SQL client into shared …
juliusmarminge Sep 2, 2026
2ced692
fix(web): offer browser profiles from the empty-panel launcher (#9279)
juliusmarminge Sep 2, 2026
0911991
fix(cursor): honor auto and full access modes (#9283)
maria-rcks Sep 2, 2026
1c806bd
fix(opencode): show Reasoning selector for OpenCode models (#9287)
maria-rcks Sep 3, 2026
d2d174c
fix(web): stop remounting markdown on every activity delta (#9306)
juliusmarminge Sep 3, 2026
e643d9d
fix(sidebar): collapse settled and snoozed shelves by default (#9314)
maria-rcks Sep 3, 2026
16e4acb
fix(dev): share dev servers on the loopback Vite actually binds (#9324)
juliusmarminge Sep 3, 2026
79c452b
fix(web): line up the titlebar wordmark label and version pill (#9255)
tristanmanchester Sep 3, 2026
8b8ab28
fix(web): resolve Vite sourcemap and supports warnings (#9343)
juliusmarminge Sep 3, 2026
c83c4c4
fix(web): add press feedback to buttons (#9349)
maria-rcks Sep 3, 2026
3b53020
fix(mobile): stop indented code overflowing Android chat bubbles (#9347)
Adamulek123 Sep 3, 2026
aaa57b6
fix(web): collapse oldest pull request comments (#9323)
maria-rcks Sep 3, 2026
95c55a2
fix(web): stop usage summary requests reporting slow RPCs (#9358)
juliusmarminge Sep 3, 2026
eb2a043
feat(web): suggest ssh hosts in a dropdown under the host field (#9171)
flamboh Sep 3, 2026
d77e58c
test(web): remove static markup-only component tests (#9364)
t3-code[bot] Sep 3, 2026
a2b93c4
fix(claude): expand slash commands when a message has attachments (#9…
t3dotgg Sep 3, 2026
0835324
fix(web): preserve explicit preview navigation URLs (#8902)
nateEc Sep 3, 2026
01da6f5
fix(web): prevent loading ssh environments from overriding navigation…
flamboh Sep 3, 2026
5868dd8
fix(server): reconnect Pylon-only call sites after the upstream sweep
rynfar Sep 3, 2026
e3510a5
revert: park #9306 and #9279 until their parents land
rynfar Sep 3, 2026
55a57d4
fix(mobile): reconnect Pylon-only call sites after the upstream sweep
rynfar Sep 3, 2026
4caab7e
fix(scripts): restore the yaml dependency #9129 dropped
rynfar Sep 3, 2026
5d6fae2
fix(server): point t3 app tests at Pylon's runtime home
rynfar Sep 3, 2026
19069af
fix(server): drain Claude usage events before asserting on them
rynfar Sep 3, 2026
7f1b416
fix(mobile): restore #9128's slash-command position gate
rynfar Sep 3, 2026
95a169e
fix(contracts): drop the dead preview recording frame schema
rynfar Sep 3, 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 .github/VOUCHED.td
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,29 @@
# -github:username reason for denouncement
#
# Keep entries sorted alphabetically.
github:0x4bs3nt
github:Adamulek123
github:adityavardhansharma
github:binbandit
github:chuks-qua
github:cursoragent
github:gbarros-dev
github:github-actions[bot]
github:hwanseoc
github:ipanasenko
github:jamesx0416
github:jasonLaster
github:JoeEverest
github:maria-rcks
github:maxwellyoung
github:nateEc
github:nmggithub
github:Noojuno
github:notkainoa
github:PatrickBauer
github:realAhmedRoach
github:shiroyasha9
github:tsouth89
github:Yash-Singh1
github:eggfriedrice24
github:Ymit24
Expand Down
6 changes: 6 additions & 0 deletions apps/desktop/src/app/DesktopApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as ElectronDialog from "../electron/ElectronDialog.ts";
import * as ElectronProtocol from "../electron/ElectronProtocol.ts";
import * as ElectronSafeStorage from "../electron/ElectronSafeStorage.ts";
import { installDesktopIpcHandlers } from "../ipc/DesktopIpcHandlers.ts";
import * as DesktopAppActivation from "./DesktopAppActivation.ts";
import * as DesktopAppIdentity from "./DesktopAppIdentity.ts";
import * as DesktopClerk from "./DesktopClerk.ts";
import * as DesktopApplicationMenu from "../window/DesktopApplicationMenu.ts";
Expand Down Expand Up @@ -148,6 +149,7 @@ const bootstrap = Effect.gen(function* () {
const serverExposure = yield* DesktopServerExposure.DesktopServerExposure;
const wslBackend = yield* DesktopWslBackend.DesktopWslBackend;
const desktopWindow = yield* DesktopWindow.DesktopWindow;
const appActivation = yield* DesktopAppActivation.DesktopAppActivation;
yield* logBootstrapInfo("bootstrap start");

if (environment.isDevelopment && Option.isNone(environment.configuredBackendPort)) {
Expand Down Expand Up @@ -210,6 +212,10 @@ const bootstrap = Effect.gen(function* () {
}
yield* primaryBackend.start;
yield* logBootstrapInfo("bootstrap backend start requested");
yield* appActivation.start.pipe(
Effect.tap(() => logBootstrapInfo("desktop app control socket ready")),
Effect.catch((error) => logStartupError("desktop app control socket unavailable", { error })),
);
// Bring up the WSL backend if the user previously enabled it. The
// primary is already starting; reconcile fires off the WSL register
// in parallel rather than blocking primary readiness on a possibly
Expand Down
140 changes: 140 additions & 0 deletions apps/desktop/src/app/DesktopAppActivation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// @effect-diagnostics nodeBuiltinImport:off -- This adapter test binds a real local socket or Windows named pipe and verifies its cleanup.
import * as NodeFSP from "node:fs/promises";
import * as NodeNet from "node:net";
import * as NodeOS from "node:os";
import * as NodePath from "node:path";

import {
ProjectId,
ThreadId,
type DesktopAppActivationRequest,
type DesktopAppActivationResponse,
} from "@t3tools/contracts";
import { resolveDesktopAppControlAddress } from "@t3tools/shared/desktopAppControl";
import { HostProcessPlatform, HostProcessUserId } from "@t3tools/shared/hostProcess";
import { it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import { afterEach, describe, expect } from "vite-plus/test";

import { startDesktopAppControlServer } from "./DesktopAppActivation.ts";

const openServers: Array<{ close: () => Promise<void> }> = [];

afterEach(async () => {
await Promise.all(openServers.splice(0).map((server) => server.close()));
});

function makeTarget(stateDir: string, platform: NodeJS.Platform, userId: number | undefined) {
return resolveDesktopAppControlAddress({
stateDir,
platform,
tempDir: NodeOS.tmpdir(),
userId,
joinPath: NodePath.join,
});
}

function request(requestId: string, platform: NodeJS.Platform): DesktopAppActivationRequest {
return {
version: 1,
requestId,
type: "open-workspace",
workspaceRoot: NodePath.join(NodeOS.tmpdir(), "project"),
platform: platform === "win32" ? "win32" : platform === "darwin" ? "darwin" : "linux",
};
}

function exchange(address: string, payload: DesktopAppActivationRequest) {
return new Promise<DesktopAppActivationResponse>((resolve, reject) => {
const socket = NodeNet.createConnection(address);
socket.setEncoding("utf8");
let buffer = "";
socket.once("error", reject);
socket.once("connect", () => socket.write(`${JSON.stringify(payload)}\n`));
socket.on("data", (chunk) => {
buffer += chunk;
const newline = buffer.indexOf("\n");
if (newline === -1) return;
socket.destroy();
resolve(JSON.parse(buffer.slice(0, newline)) as DesktopAppActivationResponse);
});
});
}

describe("desktop app control server", () => {
it.effect("roundtrips a request and removes its socket on shutdown", () =>
Effect.gen(function* () {
const platform = yield* HostProcessPlatform;
const userId = yield* HostProcessUserId;
yield* Effect.promise(async () => {
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-control-test-"));
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
const received: DesktopAppActivationRequest[] = [];
const server = await startDesktopAppControlServer({
...target,
userId,
handle: async (input) => {
received.push(input);
return {
version: 1,
requestId: input.requestId,
ok: true,
projectId: ProjectId.make("project-1"),
threadId: ThreadId.make("thread-1"),
};
},
cancel: () => undefined,
});
openServers.push(server);

const response = await exchange(target.address, request("request-1", platform));

expect(received).toHaveLength(1);
expect(response).toMatchObject({ ok: true, requestId: "request-1" });
await server.close();
openServers.splice(openServers.indexOf(server), 1);
if (target.directory !== null) {
await expect(NodeFSP.stat(target.address)).rejects.toMatchObject({ code: "ENOENT" });
}
await NodeFSP.rm(root, { recursive: true, force: true });
});
}),
);

it.effect("cancels a queued request when the client disconnects", () =>
Effect.gen(function* () {
const platform = yield* HostProcessPlatform;
const userId = yield* HostProcessUserId;
yield* Effect.promise(async () => {
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-app-cancel-test-"));
const target = makeTarget(NodePath.join(root, "userdata"), platform, userId);
let resolveCanceled: (requestId: string) => void = () => undefined;
const canceled = new Promise<string>((resolve) => {
resolveCanceled = resolve;
});
const server = await startDesktopAppControlServer({
...target,
userId,
handle: () => new Promise(() => undefined),
cancel: resolveCanceled,
});
openServers.push(server);
const socket = NodeNet.createConnection(target.address);
await new Promise<void>((resolve, reject) => {
socket.once("error", reject);
socket.once("connect", () => {
socket.write(`${JSON.stringify(request("request-canceled", platform))}\n`, () => {
socket.destroy();
resolve();
});
});
});

await expect(canceled).resolves.toBe("request-canceled");
await server.close();
openServers.splice(openServers.indexOf(server), 1);
await NodeFSP.rm(root, { recursive: true, force: true });
});
}),
);
});
Loading
Loading