Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
fe0e8d4
fix(mobile): dragging a microphone or environment route now saves its…
juliusmarminge Oct 27, 2026
bbd9593
feat(web): channel artwork choice on Latest, plus nightly or dev art …
maria-rcks Oct 27, 2026
5b70d81
feat(mobile): browse ZIP attachments with the shared archive reader (…
Bil0000 Oct 27, 2026
8d94203
fix(server): Claude --settings launch args merge with T3's settings i…
t3dotgg Oct 27, 2026
a27ec88
feat(server,web): see and stop the dev servers a thread started (#22542)
t3dotgg Oct 27, 2026
f08136d
fix(web): composer no longer jumps when a second line is added (#26155)
maria-rcks Oct 27, 2026
b31de68
feat(web,mobile): queued messages show who sent them (#26159)
juliusmarminge Oct 27, 2026
8f0b729
fix(preview): agent and browser panel show the same page after a late…
t3dotgg Oct 27, 2026
1cf14a4
fix(web): pairing, connect, error, and Clerk sign-in surfaces match t…
maria-rcks Oct 27, 2026
3b22620
fix(server): parallel worktree launches no longer fail on .git/config…
juliusmarminge Oct 27, 2026
4cb5f10
fix(task-cache): the hook test waits for every flusher it starts (#26…
juliusmarminge Oct 27, 2026
33d0c96
chore(lint): every React effect states its reason at the call site (#…
juliusmarminge Oct 27, 2026
0637f87
test(server): the link-card retry test names a new MCP credential (#2…
juliusmarminge Oct 27, 2026
e66ffcc
perf(web): stop measuring table cells while replies stream (#26152)
t3dotgg Oct 27, 2026
dde3fdb
feat(desktop): add a remote server by clicking its t3code://pair link…
t3dotgg Oct 27, 2026
cb7e381
chore(mobile): e2e runner retries pool failures and reports them apar…
juliusmarminge Oct 27, 2026
867112c
fix(server): agent questions stay answerable after a server restart (…
SunkenInTime Oct 27, 2026
34ca613
chore(lint): the effect lint error walks the alternatives before the …
juliusmarminge Oct 27, 2026
4fc9e5f
fix(web): settings avatar no longer hugs the modal corner (#26180)
t3dotgg Oct 27, 2026
622bc06
fix(mobile): shared file names can no longer write outside the Androi…
juliusmarminge Oct 27, 2026
80b7b0f
fix(server): repository lookups no longer send GitHub tokens to arbit…
juliusmarminge Oct 27, 2026
f0c1da3
fix(github): long period runs no longer freeze reference autolinking …
juliusmarminge Oct 27, 2026
0b7caf2
fix(mobile): Android diff ellipsis no longer measures text quadratica…
juliusmarminge Oct 27, 2026
b88da91
fix(server): Codex works again after its app-server is killed (#26188)
t3dotgg Oct 27, 2026
3ae672f
fix(desktop): ask once per site before a preview page reads the clipb…
juliusmarminge Oct 27, 2026
294981f
fix(mobile): Windows paths in messages keep their backslashes (#21454)
juliusmarminge Oct 27, 2026
7e884b2
feat: triage an issue from the command palette, no terminal needed (#…
t3dotgg Oct 27, 2026
a846084
fix(server): Forgejo required-check globs no longer hang the event lo…
juliusmarminge Oct 27, 2026
735cc3c
fix(server): fx's version policy reaches servers with a cached model …
juliusmarminge Oct 27, 2026
c570c2c
fix(mobile): keep iPad tab bar within narrow sidebars (#26198)
juliusmarminge Oct 28, 2026
c38b265
feat(web): filter the sidebar by several projects at once (#25284)
maria-rcks Oct 28, 2026
0894156
fix(mobile): provider accounts fit each provider on one row (#25861)
saphid Oct 28, 2026
531cee6
fix(server): Claude background-task wakes start when the queue is hel…
fireboltdude1357 Oct 28, 2026
68495b6
docs(testing): new tests extend existing cases and stay at one layer …
t3dotgg Oct 28, 2026
06d4b57
fix(web): composer strip drops the folder icon next to the machine ic…
maria-rcks Oct 28, 2026
7e71392
feat(web): background work line shows the newest activity, no dots (#…
maria-rcks Oct 28, 2026
7f2dcd8
fix(mobile): dictation buttons line up with the send button on every …
juliusmarminge Oct 28, 2026
df4132b
chore(mobile): e2e runs keep Metro's output, the app's console logs, …
juliusmarminge Oct 28, 2026
2e2d86c
chore(mobile): e2e Metro serves no lazy chunks
juliusmarminge Oct 28, 2026
4ab7084
feat(web,mobile): the @ picker lists threads on linked machines (#26201)
juliusmarminge Oct 28, 2026
0affd28
feat(web,mobile): Copy thread copies a chip that works on linked mach…
juliusmarminge Oct 28, 2026
5cf49d2
fix(mobile): unclosed code fences in HTML blocks no longer freeze PR …
juliusmarminge Oct 28, 2026
b5af460
fix(opencode): apply runtime mode changes to existing OpenCode sessio…
juliusmarminge Oct 28, 2026
8251578
fix(server): Antigravity file writes cannot escape the workspace thro…
juliusmarminge Oct 28, 2026
7a059b9
chore(mobile): e2e treats a failed daemon read as a pool error
juliusmarminge Oct 28, 2026
2525252
feat(web): F6 moves between workspace areas, and the sidebar is keybo…
t3dotgg Oct 28, 2026
575a03f
chore(review): Macroscope check agents move to GPT 6.1 Sol (#26187)
t3dotgg Oct 28, 2026
e2a8ef0
feat(dev): test turns in the UI for free with a replay provider (#26207)
t3dotgg Oct 28, 2026
93a9e07
feat(threads): choose your own snooze options in settings (#26208)
t3dotgg Oct 28, 2026
c3b40b9
fix(relay): stop delivery attempts from growing the database forever …
juliusmarminge Oct 28, 2026
9638e17
fix(relay): tunnel cleanup keeps up with new idle tunnels (#26135)
juliusmarminge Oct 28, 2026
4b00290
fix(server): delegate cgroups to agent scopes (#23913)
juliusmarminge Oct 28, 2026
3b2f958
fix(server): ACP Registry searches reuse a recently fetched index (#2…
juliusmarminge Oct 28, 2026
67661cf
chore: delete 18k lines of code that nothing reaches (#26238)
t3dotgg Oct 28, 2026
e0b3cfe
fix(web): image and file chips underline their name on hover again (#…
flamboh Oct 28, 2026
2de4762
fix(mobile): tab bar hides with the push, logo fits the iPad sidebar,…
juliusmarminge Oct 28, 2026
91992bd
fix(web): update dialog shows one row per machine (#26210)
t3dotgg Oct 28, 2026
7c6446b
chore(mobile): e2e review skips behaviour confirmed as intended
juliusmarminge Oct 28, 2026
36aea58
fix(mobile): trust user-installed Android CA certificates (#25730)
juliusmarminge Oct 28, 2026
8947f3e
fix(web): server rows keep their source visible, cmd-click opens your…
t3dotgg Oct 28, 2026
f120c51
fix(mobile): list bullets keep a gap before their text at every text …
juliusmarminge Oct 28, 2026
949929d
fix(mobile): Android split content keeps a gap at the screen edge (#2…
juliusmarminge Oct 28, 2026
c2fbf0a
feat(server,web,mobile): thread links name their environment and neve…
juliusmarminge Oct 28, 2026
fe55faf
fix(clients): # suggestions put linked pull requests first for text q…
juliusmarminge Oct 28, 2026
49a9e80
fix(terminal): keep shared terminal grids in sync (#24352)
juliusmarminge Oct 28, 2026
0c80751
feat: sign in to GitHub through the T3 Code for GitHub app [internal …
juliusmarminge Oct 28, 2026
ff71186
test(mobile): vp run mobile:behavior grades app behavior on pool devices
juliusmarminge Oct 28, 2026
9910c0e
perf: reduce remote reconnect and command delays (#25754)
Bil0000 Oct 28, 2026
822fc15
fix(relay): deploys plan again after the Worker lost its default expo…
juliusmarminge Oct 28, 2026
3ef1610
fix(web): compaction row says "Compacting for 12s" instead of "after …
t3dotgg Oct 28, 2026
9a8bc04
test(task-cache): remove flaky native runner integration test
juliusmarminge Oct 28, 2026
c4606e5
fix(server): Codex shadow homes share thread writer locks with the ma…
t3dotgg Oct 28, 2026
28da8c8
fix(mobile): sidebar connection status uses the header's free width (…
juliusmarminge Oct 28, 2026
9c6429d
fix(web): threads without a project start on the primary environment …
juliusmarminge Oct 28, 2026
d01684a
Merge commit '9c6429d74b59c7a06fb7357526457d192a744a9f' into fix/cach…
fireboltdude1357 Oct 28, 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
  •  
  •  
  •  
13 changes: 13 additions & 0 deletions .agents/skills/test-t3-app/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ Test with meaningful project and thread data. Read
[references/sqlite-fixtures.md](references/sqlite-fixtures.md) only when
inspecting or seeding SQLite. Stop the test server before direct fixture writes.

## Drive agent turns for free

Do not spend a paid model to see a turn render. `vp run dev` registers the
`Replay (dev)` provider, which replays recorded turns with simulated streaming
through the real adapter and orchestration. Its models are scenarios:
`showcase` covers thinking, a plan, tool calls, an approval, and markdown.
Press `Ctrl+Alt+Shift+R` for a picker (GNOME takes that key for screen
recording), call `window.t3dev.replay("showcase")`
with `preview_evaluate`, or run
`node apps/server/src/bin.ts thread start --base-dir .t3 --provider devReplay --model showcase <project> "go"`.
When the test needs real provider behavior, use that provider's cheapest
model, such as Haiku for Claude or Luna for Codex.

## Use the Browser panel

Call `preview_status`, then `preview_open` if the Browser panel is
Expand Down
2 changes: 1 addition & 1 deletion .coderabbit.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default defineConfig((ctx) => ({
instructions: "Hold changed code to the rules in docs/internals/web-ui.md.",
},
{
path: "**/*.test.{ts,tsx}",
path: "**/*.test.*",
instructions:
"Hold changed tests to the rules in docs/internals/testing.md. Request tests only as those rules allow.",
},
Expand Down
4 changes: 2 additions & 2 deletions .github/triage/PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ provided alongside this playbook. Everything machine-specific lives there, not h

## 1. Ask what went wrong

Your first message to the user: ask them to describe what went wrong, in their own
words. Ask them to paste screenshots directly into this session if they have any.
Unless the user already described it, your first message asks what went wrong, in
their own words. Ask them to paste screenshots directly into this session if they have any.
Ask follow-up questions when the description is vague. Good repro steps are the most
valuable thing you can extract from this conversation.

Expand Down
10 changes: 0 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,16 +225,6 @@ jobs:
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

# Blacksmith boots GitHub's Ubuntu runner image (gcc is usually present),
# but ACP process-tree live tests compile a small pthread fixture with `cc`
# and soft-skip when it is missing. Install build-essential so that path
# always runs in CI instead of silently no-oping.
- name: Install C toolchain for process-tree fixtures
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends build-essential
command -v cc
- uses: ./.github/actions/setup-apt-mirrors

# The desktop tests compile the browser secret helper. The install runs in
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ apps/mobile/.android-cache/
/.generated/
artifacts/app-store/screenshots/
artifacts/mobile-e2e/
artifacts/mobile-behavior/
.github/pr-assets/
native/**/target/
node_modules/
Expand Down
4 changes: 2 additions & 2 deletions .macroscope/check-run-agents/effect-service-conventions.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Effect Service Conventions
model: gpt-6-sol
effort: max
model: gpt-6-1-sol
reasoning: low
input: incremental
tools:
- browse_code
Expand Down
27 changes: 27 additions & 0 deletions .macroscope/check-run-agents/test-signal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: Test Signal
model: gpt-6-1-terra
reasoning: low
input: incremental
tools:
- browse_code
- modify_pr
# An explicit include overrides ignore.md, which skips test files for every other check.
include:
- "**/*.test.*"
exclude:
- ".repos/**"
requires:
- Check
maxBudgetPerRun: 1
maxBudgetPerPR: 5
conclusion: neutral
---

# Test signal review

Review only the tests this pull request adds or grows. Tests it moves, renames, or deletes are not findings. The rules are in `docs/internals/testing.md` under `## What to test`; read that section first and hold new tests to it.

Post each finding as a brief inline comment on the new test with one fix. Ask only for fewer or smaller tests, never for more coverage.

When there are no findings, make the entire final response exactly `All clear` on one line with nothing else.
4 changes: 2 additions & 2 deletions .macroscope/check-run-agents/ui-consistency.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: UI Consistency
model: gpt-6-sol
effort: max
model: gpt-6-1-sol
reasoning: low
input: incremental
tools:
- browse_code
Expand Down
10 changes: 7 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ An empty database is a bad test. Seed your worktree's `.t3` with a copy of real
- While you iterate, run `vp test run <files>`. For big files, add `-t "<name>"`.
- Use `vp run typecheck`, not `npx tsc`; it caches per package. Both run tsc-rs (`typescript` is the `@tsc-rs/typescript` drop-in).
- Run `node -e` snippets from `apps/server` or the owning package. `effect` does not resolve from the repo root.
- Test meaningful logic or observable behavior. Do not render components to static markup to assert props or attributes, or add tests that merely assert callback wiring or mirror the implementation.
- Test behavior that can break, such as orchestration and event-sourcing, provider protocol translation, auth and permission guards, persistence and migrations, external-format parsing, race fixes, and the logic a bug fix changed. Leave out tests for what types already guarantee, trivial logic, copy text, smoke runs, mock-call mirrors, callback wiring, and props rendered to static markup.
- Extend before you add. When an existing test or table covers the behavior, add a row or an assertion there. Write a new `it` only when no case fits.
- Test at one layer: the lowest one that fails on `main`. Do not repeat the case in a wrapper test, at each call site, or in web and mobile copies of the same logic.
- [Testing](docs/internals/testing.md) carries these same test rules, plus review guidance, for review bots. Change both together.
- **Do not run repo-wide checks.** No `vp check`, no `vp run -r test`, no `vp run -r typecheck` unless I ask. CI owns the full suite.
- Backend behavior changes ship with focused tests for that behavior.
- Proof for a bug fix depends on the bug. Logic, data, state, and protocol bugs get a failing test first (the fewest that fail on `main` for that reason, in the module's existing test file), then the fix. Purely visual bugs (spacing, colour, alignment, a layout shift) get before/after captures as their proof and no test: do not add UI tests for nitpicks, and do not ask for one in review when a capture already shows it. A server-only fix with no visible surface ships the test plus a short runtime transcript.
- Proof for a bug fix depends on the bug. Logic, data, state, and protocol bugs get a failing test first (the fewest that fail on `main` for that reason, in an existing case of the module's test file where one fits), then the fix. Purely visual bugs (spacing, colour, alignment, a layout shift) get before/after captures as their proof and no test: do not add UI tests for nitpicks, and do not ask for one in review when a capture already shows it. A server-only fix with no visible surface ships the test plus a short runtime transcript.
- The server is event-sourced, and side effects run after the command commits. In tests, drain the effect worker (`OrchestrationEffectWorkerV2.drain`) or await the specific persisted event or `Deferred` that marks the milestone. Never wait on sleeps or polling. A test that needs a timeout to pass is wrong. Concretely:
- `it.effect` runs on a `TestClock`; `Effect.sleep` only advances through `TestClock.adjust`. `it.live` is wall-clock and only for subprocess or network fixtures.
- No `yieldNow` or `sleep` polling loops. Await the `Deferred`, take from the `Queue`, or drain the worker.
Expand Down Expand Up @@ -135,6 +137,8 @@ An empty database is a bad test. Seed your worktree's `.t3` with a copy of real

- Upon request, user-visible frontend changes should get one integrated pass in a real client: `test-t3-app` for web, `test-t3-mobile` for mobile. The primary agent does this once after integrating. Subagents do not launch their own dev servers. Ask permission before doing computer use or spinning up browsers.

- Real agent turns cost money. To watch a turn stream through the pipeline and UI, use the free `Replay (dev)` provider that `vp run dev` registers; [test-t3-app](.agents/skills/test-t3-app/SKILL.md) shows how to start one. Use a real provider only when the change depends on that provider's behavior, and then pick its cheapest model, such as Haiku for Claude or Luna for Codex.

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
Expand Down Expand Up @@ -199,7 +203,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 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>`.
- 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/no-unreasoned-state-sync` lint rule reports every effect; one that stays states its reason with `// oxlint-disable-next-line t3code/no-unreasoned-state-sync -- <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
11 changes: 9 additions & 2 deletions apps/desktop/src/app/DesktopClerk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ describe("DesktopClerk", () => {
`setPath:userData:${userData}`,
"requestSingleInstanceLock",
"on:second-instance",
"on:open-url",
"createClerkBridge",
]);
},
Expand Down Expand Up @@ -200,14 +201,15 @@ describe("DesktopClerk", () => {
},
);

it.effect("delivers queued startup links and later links once in arrival order", () => {
it.effect("delivers each queued startup link once, before later links", () => {
storageMock.mockReturnValue(storageAdapter);
createClerkBridgeMock.mockReturnValue({ cleanup: vi.fn(), isPrimaryInstance: true });
const listeners = new Map<string, (...args: unknown[]) => void>();
const received: string[] = [];
const first = "t3code://new?prompt=first";
const second = "t3code://threads/env-1/thread-1";
const later = "t3code://new?prompt=later";
const launchPair = "t3code://pair?pairingUrl=https%3A%2F%2Fvps.example.com%2F%23token%3Dabc";
const delivered = Promise.withResolvers<void>();
const electronApp = {
on: (name: string, listener: (...args: unknown[]) => void) =>
Expand All @@ -220,13 +222,14 @@ describe("DesktopClerk", () => {
const clerk = yield* DesktopClerk.DesktopClerk;
listeners.get("second-instance")?.({}, ["t3", first]);
listeners.get("second-instance")?.({}, ["t3", second]);
listeners.get("open-url")?.({ preventDefault: () => undefined }, launchPair);
assert.deepEqual(received, []);

yield* clerk.configure;
listeners.get("second-instance")!({}, ["t3", later]);
yield* Effect.promise(() => delivered.promise);

assert.deepEqual(received, [first, second, later]);
assert.deepEqual(received, [launchPair, first, second, later]);
}).pipe(
Effect.scoped,
Effect.provide(layerDesktopClerk(true, [], "darwin", undefined, undefined, listeners)),
Expand Down Expand Up @@ -425,6 +428,7 @@ it.effect("hands web and app links to the renderer and leaves other links alone"
}),
} as unknown as ElectronApp.ElectronApp["Service"];
const received: Array<string> = [];
const pairLink = "t3code://pair?pairingUrl=https%3A%2F%2Fvps.example.com%2F%23token%3Dabc";
yield* Effect.gen(function* () {
const clerk = yield* DesktopClerk.DesktopClerk;
yield* clerk.configure;
Expand All @@ -437,6 +441,8 @@ it.effect("hands web and app links to the renderer and leaves other links alone"
assert.strictEqual(open("http://localhost:3000/"), 1);
assert.strictEqual(open("t3code://new?prompt=hi"), 1);
assert.strictEqual(open("t3code://threads/env-1/thread-1"), 1);
assert.strictEqual(open(pairLink), 1);
assert.strictEqual(open("t3code://pair"), 0);
assert.strictEqual(open("mailto:hello@example.com"), 0);
assert.strictEqual(open("t3code://somewhere-else"), 0);
yield* Effect.yieldNow;
Expand All @@ -445,6 +451,7 @@ it.effect("hands web and app links to the renderer and leaves other links alone"
"http://localhost:3000/",
"t3code://new?prompt=hi",
"t3code://threads/env-1/thread-1",
pairLink,
]);
}).pipe(
Effect.provide(layerDesktopClerk(true, [], "darwin", undefined, undefined, listeners)),
Expand Down
28 changes: 18 additions & 10 deletions apps/desktop/src/app/DesktopClerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { receiveCodexAuthCallback, CodexAuthCallbackError } from "./CodexAuthCal
import * as ElectronShell from "../electron/ElectronShell.ts";
import { providerAuthReturnUrl } from "@t3tools/shared/providerAuthReturnUrl";
import { promptLinkAppPath } from "@t3tools/shared/promptLinks";
import { readPairingAppLink } from "@t3tools/shared/remote";
import * as HostProcess from "@t3tools/shared/HostProcess";
import { clerkFrontendApiHostnameFromPublishableKey } from "@t3tools/shared/relayAuth";
import * as ElectronApp from "../electron/ElectronApp.ts";
Expand Down Expand Up @@ -118,6 +119,15 @@ export const make = Effect.gen(function* () {
yield* electronApp.on("second-instance", (_event: unknown, argv: readonly string[]) => {
handleSecondInstance(argv);
});
// macOS hands a launch link to open-url, which can fire before configure runs.
const pendingOpenUrls: Array<string> = [];
let handleOpenUrl = (url: string) => {
pendingOpenUrls.push(url);
return true;
};
yield* electronApp.on("open-url", (event: { preventDefault: () => void }, url: string) => {
if (handleOpenUrl(url)) event.preventDefault();
});

yield* Effect.acquireRelease(
Effect.try({
Expand Down Expand Up @@ -194,7 +204,8 @@ export const make = Effect.gen(function* () {
return true;
};
const openAppLink = (value: string | undefined) => {
if (!value || promptLinkAppPath(value) === null) return false;
if (!value || (promptLinkAppPath(value) === null && readPairingAppLink(value) === null))
return false;
void runPromise(webLinks.receive(value));
return true;
};
Expand All @@ -205,15 +216,12 @@ export const make = Effect.gen(function* () {
void runPromise(webLinks.receive(url));
return true;
};
yield* electronApp.on("open-url", (event: { preventDefault: () => void }, url: string) => {
if (
startProviderAuthHandoff(url) ||
resumeProviderAuth(url) ||
openWebLink(url) ||
openAppLink(url)
)
event.preventDefault();
});
handleOpenUrl = (url) =>
startProviderAuthHandoff(url) ||
resumeProviderAuth(url) ||
openWebLink(url) ||
openAppLink(url);
for (const url of pendingOpenUrls.splice(0)) handleOpenUrl(url);
yield* electronApp.on("open-file", (event: { preventDefault: () => void }, path: string) => {
if (!DesktopWebLinks.isWebPageFile(path)) return;
event.preventDefault();
Expand Down
5 changes: 1 addition & 4 deletions apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,14 @@ const withStore = <A, E, R>(
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped);

describe("DesktopConnectionCatalogStore", () => {
it.effect("persists, reads, and clears an encrypted connection catalog", () =>
it.effect("persists and reads an encrypted connection catalog", () =>
withStore(
Effect.gen(function* () {
const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;
const catalog = '{"schemaVersion":1,"targets":[]}';

assert.isTrue(yield* store.set(catalog));
assert.deepStrictEqual(yield* store.get, Option.some(catalog));

yield* store.clear;
assert.deepStrictEqual(yield* store.get, Option.none());
}),
),
);
Expand Down
10 changes: 0 additions & 10 deletions apps/desktop/src/app/DesktopConnectionCatalogStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ export class DesktopConnectionCatalogStore extends Context.Service<
boolean,
DesktopConnectionCatalogStoreWriteError | DesktopConnectionCatalogStoreProtectionError
>;
readonly clear: Effect.Effect<void>;
}
>()("@t3tools/desktop/app/DesktopConnectionCatalogStore") {}

Expand Down Expand Up @@ -503,15 +502,6 @@ export const make = Effect.gen(function* () {
yield* writeCatalog(catalog);
return true;
}),
clear: fileSystem.remove(catalogPath, { force: true }).pipe(
Effect.catch((error) =>
Effect.logWarning("Could not clear the desktop connection catalog.", {
catalogPath,
error,
}),
),
Effect.withSpan("desktop.connectionCatalogStore.clear"),
),
});
});

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/backend/DesktopBackendConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ const DESKTOP_BACKEND_ENV_NAMES = [
"T3CODE_NO_BROWSER",
"T3CODE_HOST",
"T3CODE_DESKTOP_WS_URL",
"T3CODE_DESKTOP_LAN_ACCESS",
"T3CODE_DESKTOP_LAN_HOST",
"T3CODE_DESKTOP_HTTPS_ENDPOINTS",
"T3CODE_TAILSCALE_SERVE",
Expand All @@ -86,6 +85,7 @@ const WSL_FORWARDED_ENV_NAMES = [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"T3CODE_TELEMETRY_ENABLED",
"T3CODE_GITHUB_APP_CLIENT_ID",
// Otherwise the WSL server keeps exporting to endpoints from the bootstrap.
"T3CODE_OTEL_SDK_DISABLED",
"OTEL_SDK_DISABLED",
Expand Down
9 changes: 1 addition & 8 deletions apps/desktop/src/ipc/DesktopIpcHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import { receiveProviderAuthCallback, cancelProviderAuthCallback } from "./metho
import * as DesktopIpc from "./DesktopIpc.ts";
import { installNotificationBadge } from "./methods/notificationBadge.ts";
import { getClientSettings, setClientSettings } from "./methods/clientSettings.ts";
import {
clearConnectionCatalog,
getConnectionCatalog,
setConnectionCatalog,
} from "./methods/connectionCatalog.ts";
import { getConnectionCatalog, setConnectionCatalog } from "./methods/connectionCatalog.ts";
import {
getLocalEnvironmentEnabled,
setLocalEnvironmentEnabled,
Expand All @@ -26,7 +22,6 @@ import {
ensureSshEnvironment,
fetchSshEnvironmentDescriptor,
fetchSshSessionState,
issueSshWebSocketTicket,
resolveSshHost,
resolveSshPasswordPrompt,
} from "./methods/sshEnvironment.ts";
Expand Down Expand Up @@ -120,7 +115,6 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers"
yield* ipc.handle(dismissSnapShotAnimation);
yield* ipc.handle(acknowledgeSnapShot);
yield* ipc.handle(setConnectionCatalog);
yield* ipc.handle(clearConnectionCatalog);

yield* ipc.handle(discoverSshHosts);
yield* ipc.handle(resolveSshHost);
Expand All @@ -129,7 +123,6 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers"
yield* ipc.handle(fetchSshEnvironmentDescriptor);
yield* ipc.handle(bootstrapSshBearerSession);
yield* ipc.handle(fetchSshSessionState);
yield* ipc.handle(issueSshWebSocketTicket);
yield* ipc.handle(resolveSshPasswordPrompt);

yield* ipc.handle(getServerExposureState);
Expand Down
Loading
Loading