authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-01 21:42:58-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-01 21:42:58-07:00
logdb4bf9497b524f35e665af9e941aa8faeb05ea9e
tree7ea41f309200216d073db044e37e6b1826f6bcb2
parentb8262b41228e03b2e1e14b701be449034349c8ba
signature Signed by PGP key B5690EEEBB952194

chore: remove unused code and brittle tests (#9150)


41 files changed, 17 insertions(+), 744 deletions(-)

.github/workflows/release.yml-29
......@@ -851,35 +851,6 @@ jobs:
851851 fi
852852 done
853853
854 # - name: Merge Windows updater manifests
855 # run: |
856 # shopt -s nullglob
857 # found_windows_manifest=false
858 # for x64_manifest in release-assets/*-win-x64.yml; do
859 # if [[ "$(basename "$x64_manifest")" == builder-debug-* ]]; then
860 # continue
861 # fi
862
863 # arm64_manifest="${x64_manifest/-x64.yml/-arm64.yml}"
864 # output_manifest="${x64_manifest/-win-x64.yml/.yml}"
865 # if [[ ! -f "$arm64_manifest" ]]; then
866 # echo "Missing matching arm64 Windows manifest for $x64_manifest" >&2
867 # exit 1
868 # fi
869
870 # found_windows_manifest=true
871 # node scripts/merge-update-manifests.ts --platform win \
872 # "$arm64_manifest" \
873 # "$x64_manifest" \
874 # "$output_manifest"
875 # rm -f "$arm64_manifest" "$x64_manifest"
876 # done
877
878 # if [[ "$found_windows_manifest" != true ]]; then
879 # echo "No Windows updater manifests found to merge." >&2
880 # exit 1
881 # fi
882
883854 - name: Publish release
884855 if: needs.preflight.outputs.previous_tag != ''
885856 uses: softprops/action-gh-release@v3
apps/desktop/src/wsl/DesktopWslEnvironment.test.ts-3
......@@ -198,13 +198,10 @@ describe("WSL runtime cache", () => {
198198 expect(script).toContain(' [ -f "$runtime_root/apps/server/dist/bin.mjs" ] &&');
199199 expect(script).toContain(' [ -f "$runtime_root/node_modules/node-pty/package.json" ] &&');
200200 expect(script).toContain(' node_pty_payload_present "$runtime_root"');
201 expect(script).not.toContain("node_modules/effect/package.json");
202201 expect(script).toContain("if runtime_is_ready; then");
203202 expect(script).toContain("trap 'exit 1' HUP INT TERM");
204203 expect(script).toContain('exec 9> "$runtime_lock"');
205204 expect(script).toContain("flock -x 9");
206 expect(script).not.toContain("runtime_lock_pid");
207 expect(script).not.toContain("sleep 0.1");
208205 expect(script).not.toContain('rm -rf "$runtime_lock"');
209206 expect(script).toContain('mv -T "$runtime_root" "$runtime_stale"');
210207 expect(script).toContain('mktemp -d "$runtime_parent/.1.2.3-x64.tmp.XXXXXX"');
apps/marketing/public/apple-touch-icon.webp deleted
Binary files a/apps/marketing/public/apple-touch-icon.webp and /dev/null differ
apps/marketing/public/favicon-16x16.webp deleted
Binary files a/apps/marketing/public/favicon-16x16.webp and /dev/null differ
apps/marketing/public/favicon-32x32.webp deleted
Binary files a/apps/marketing/public/favicon-32x32.webp and /dev/null differ
apps/marketing/public/icon.png deleted
Binary files a/apps/marketing/public/icon.png and /dev/null differ
apps/marketing/public/screenshot.webp deleted
Binary files a/apps/marketing/public/screenshot.webp and /dev/null differ
apps/marketing/tweets.md deleted-19
......@@ -1,19 +0,0 @@
1https://x.com/Shay_Benshabtay/status/2054668503857156326
2https://x.com/teja2495/status/2052420254991581623
3https://x.com/developedbyed/status/2030627970532921605
4https://x.com/tannerlinsley/status/2031102771529920966
5https://x.com/aronprins/status/2045102518196183109
6https://x.com/BennettBuhner/status/2054667115697754387
7https://x.com/ex0t1clol/status/2054666870008021197
8https://x.com/Josikinz/status/2030367951694745870
9https://x.com/jetpackjoe_/status/2054666792933404959
10https://x.com/mil000/status/2030120041451246071
11https://x.com/_winter_wonders/status/2052350198764970434
12https://x.com/kostyniuk00/status/2052041388179468521
13https://x.com/gnukeith/status/2054670073579630730
14https://x.com/peculiarnewbie/status/2054671685027233827
15https://x.com/leodev/status/2054679746353537042
16https://x.com/pocarles/status/2054673964274758046
17https://x.com/DavidKPiano/status/2054682983504719930
18https://x.com/iamkaffe/status/2054675539311411280
19https://x.com/uwunetes/status/2054683356022120640
apps/mobile/package.json-1
......@@ -52,7 +52,6 @@
5252 "@expo/ui": "~57.0.14",
5353 "@legendapp/list": "catalog:",
5454 "@noble/curves": "catalog:",
55 "@noble/hashes": "catalog:",
5655 "@pierre/diffs": "catalog:",
5756 "@react-native-ai/apple": "0.12.0",
5857 "@react-native-menu/menu": "^2.0.0",
apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.test.ts deleted-108
......@@ -1,108 +0,0 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { resolveTerminalRouteBootstrap } from "./terminalRouteBootstrap";
4
5describe("resolveTerminalRouteBootstrap", () => {
6 it("redirects bare terminal routes to another already-running terminal for the thread", () => {
7 expect(
8 resolveTerminalRouteBootstrap({
9 hasThread: true,
10 hasWorkspaceRoot: true,
11 hasOpened: false,
12 requestedTerminalId: null,
13 currentTerminalId: "default",
14 runningTerminalId: "term-2",
15 currentTerminalStatus: "closed",
16 hasCurrentTerminalHydration: false,
17 }),
18 ).toEqual({
19 kind: "redirect",
20 terminalId: "term-2",
21 });
22 });
23
24 it("hydrates the current running terminal when client state is not hydrated yet", () => {
25 expect(
26 resolveTerminalRouteBootstrap({
27 hasThread: true,
28 hasWorkspaceRoot: true,
29 hasOpened: false,
30 requestedTerminalId: null,
31 currentTerminalId: "default",
32 runningTerminalId: "default",
33 currentTerminalStatus: "running",
34 hasCurrentTerminalHydration: false,
35 }),
36 ).toEqual({
37 kind: "open",
38 });
39 });
40
41 it("opens explicit terminal routes when the session still needs hydration", () => {
42 expect(
43 resolveTerminalRouteBootstrap({
44 hasThread: true,
45 hasWorkspaceRoot: true,
46 hasOpened: false,
47 requestedTerminalId: "term-2",
48 currentTerminalId: "term-2",
49 runningTerminalId: "term-2",
50 currentTerminalStatus: "running",
51 hasCurrentTerminalHydration: false,
52 }),
53 ).toEqual({
54 kind: "open",
55 });
56 });
57
58 it("stays idle after the route already bootstrapped once", () => {
59 expect(
60 resolveTerminalRouteBootstrap({
61 hasThread: true,
62 hasWorkspaceRoot: true,
63 hasOpened: true,
64 requestedTerminalId: null,
65 currentTerminalId: "default",
66 runningTerminalId: "default",
67 currentTerminalStatus: "running",
68 hasCurrentTerminalHydration: true,
69 }),
70 ).toEqual({
71 kind: "idle",
72 });
73 });
74
75 it("stays idle when the current running terminal is already hydrated in client state", () => {
76 expect(
77 resolveTerminalRouteBootstrap({
78 hasThread: true,
79 hasWorkspaceRoot: true,
80 hasOpened: false,
81 requestedTerminalId: null,
82 currentTerminalId: "default",
83 runningTerminalId: "default",
84 currentTerminalStatus: "running",
85 hasCurrentTerminalHydration: true,
86 }),
87 ).toEqual({
88 kind: "idle",
89 });
90 });
91
92 it("stays idle for explicit running terminal routes that already have hydrated output", () => {
93 expect(
94 resolveTerminalRouteBootstrap({
95 hasThread: true,
96 hasWorkspaceRoot: true,
97 hasOpened: false,
98 requestedTerminalId: "term-2",
99 currentTerminalId: "term-2",
100 runningTerminalId: "term-2",
101 currentTerminalStatus: "running",
102 hasCurrentTerminalHydration: true,
103 }),
104 ).toEqual({
105 kind: "idle",
106 });
107 });
108});
apps/mobile/src/features/terminal/terminalRouteBootstrap.ts deleted-35
......@@ -1,35 +0,0 @@
1export function resolveTerminalRouteBootstrap(input: {
2 readonly hasThread: boolean;
3 readonly hasWorkspaceRoot: boolean;
4 readonly hasOpened: boolean;
5 readonly requestedTerminalId: string | null;
6 readonly currentTerminalId: string;
7 readonly runningTerminalId: string | null;
8 readonly currentTerminalStatus: "starting" | "running" | "exited" | "error" | "closed";
9 /** True once the attach stream has populated scrollback (`buffer` non-empty), not merely metadata. */
10 readonly hasCurrentTerminalHydration: boolean;
11}):
12 | { readonly kind: "idle" }
13 | { readonly kind: "redirect"; readonly terminalId: string }
14 | { readonly kind: "open" } {
15 if (!input.hasThread || !input.hasWorkspaceRoot || input.hasOpened) {
16 return { kind: "idle" };
17 }
18
19 if (
20 input.requestedTerminalId === null &&
21 input.runningTerminalId !== null &&
22 input.runningTerminalId !== input.currentTerminalId
23 ) {
24 return { kind: "redirect", terminalId: input.runningTerminalId };
25 }
26
27 if (
28 (input.currentTerminalStatus === "running" || input.currentTerminalStatus === "starting") &&
29 input.hasCurrentTerminalHydration
30 ) {
31 return { kind: "idle" };
32 }
33
34 return { kind: "open" };
35}
apps/server/integration/OrchestrationEngineHarness.integration.ts+2-2
......@@ -25,7 +25,7 @@ import * as Stream from "effect/Stream";
2525import * as Tracer from "effect/Tracer";
2626
2727import * as CheckpointStore from "../src/checkpointing/CheckpointStore.ts";
28import { TextGeneration, type TextGenerationShape } from "../src/textGeneration/TextGeneration.ts";
28import { TextGeneration } from "../src/textGeneration/TextGeneration.ts";
2929import { OrchestrationCommandReceiptRepositoryLive } from "../src/persistence/Layers/OrchestrationCommandReceipts.ts";
3030import { OrchestrationEventStoreLive } from "../src/persistence/Layers/OrchestrationEventStore.ts";
3131import { ProjectionCheckpointRepositoryLive } from "../src/persistence/Layers/ProjectionCheckpoints.ts";
......@@ -335,7 +335,7 @@ export const makeOrchestrationIntegrationHarness = (
335335 const textGenerationLayer = Layer.succeed(TextGeneration, {
336336 generateBranchName: () => Effect.succeed({ branch: "update" }),
337337 generateThreadTitle: () => Effect.succeed({ title: "New thread" }),
338 } as unknown as TextGenerationShape);
338 } as unknown as TextGeneration["Service"]);
339339 const providerCommandReactorLayer = ProviderCommandReactorLive.pipe(
340340 Layer.provideMerge(runtimeServicesLayer),
341341 Layer.provideMerge(gitWorkflowLayer),
apps/server/package.json-1
......@@ -25,7 +25,6 @@
2525 "@anthropic-ai/claude-agent-sdk": "^0.3.170",
2626 "@effect/platform-bun": "catalog:",
2727 "@effect/platform-node": "catalog:",
28 "@effect/platform-node-shared": "catalog:",
2928 "@effect/sql-sqlite-bun": "catalog:",
3029 "@ff-labs/fff-node": "0.9.4",
3130 "@opencode-ai/sdk": "^1.3.15",
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+3-3
......@@ -45,7 +45,7 @@ import {
4545 type ProviderServiceShape,
4646} from "../../provider/Services/ProviderService.ts";
4747import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegistryMock.ts";
48import { TextGeneration, type TextGenerationShape } from "../../textGeneration/TextGeneration.ts";
48import { TextGeneration } from "../../textGeneration/TextGeneration.ts";
4949import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
5050import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
5151import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";
......@@ -310,7 +310,7 @@ describe("ProviderCommandReactor", () => {
310310 pr: null,
311311 }),
312312 );
313 const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>((_) =>
313 const generateBranchName = vi.fn<TextGeneration["Service"]["generateBranchName"]>((_) =>
314314 Effect.fail(
315315 new TextGenerationError({
316316 operation: "generateBranchName",
......@@ -318,7 +318,7 @@ describe("ProviderCommandReactor", () => {
318318 }),
319319 ),
320320 );
321 const generateThreadTitle = vi.fn<TextGenerationShape["generateThreadTitle"]>((_) =>
321 const generateThreadTitle = vi.fn<TextGeneration["Service"]["generateThreadTitle"]>((_) =>
322322 Effect.fail(
323323 new TextGenerationError({
324324 operation: "generateThreadTitle",
apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts-13
......@@ -96,11 +96,6 @@ const fakeCursorAdapter: CursorAdapter.CursorAdapterShape = {
9696 streamEvents: Stream.empty,
9797};
9898
99// ProviderAdapterRegistryLive is now a facade over ProviderInstanceRegistry —
100// it walks `listInstances` once at boot and surfaces the default-instance
101// adapter keyed by its driver kind. To test the facade we supply four fake
102// instances whose `instanceId === defaultInstanceIdForDriver(driverKind)` so
103// they pass the default-instance filter.
10499const makeFakeInstance = (
105100 driverKindString: "codex" | "claudeAgent" | "cursor" | "opencode",
106101 adapter: ProviderInstance["adapter"],
......@@ -184,13 +179,5 @@ it.layer(layer)("ProviderAdapterRegistryLive", (it) => {
184179 defaultInstanceIdForDriver(OPENCODE_DRIVER),
185180 defaultInstanceIdForDriver(CURSOR_DRIVER),
186181 ]);
187
188 const providers = yield* registry.listProviders();
189 assert.deepStrictEqual(providers, [
190 CODEX_DRIVER,
191 CLAUDE_AGENT_DRIVER,
192 OPENCODE_DRIVER,
193 CURSOR_DRIVER,
194 ]);
195182 }));
196183});
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts+1-26
......@@ -15,11 +15,7 @@
1515 *
1616 * @module ProviderAdapterRegistryLive
1717 */
18import {
19 defaultInstanceIdForDriver,
20 ProviderInstanceId,
21 type ProviderDriverKind,
22} from "@t3tools/contracts";
18import { ProviderInstanceId } from "@t3tools/contracts";
2319import * as Effect from "effect/Effect";
2420import * as Layer from "effect/Layer";
2521
......@@ -71,31 +67,10 @@ const makeProviderAdapterRegistry = Effect.fn("makeProviderAdapterRegistry")(fun
7167 Effect.map((instances) => instances.map((instance) => instance.instanceId)),
7268 );
7369
74 const listProviders: ProviderAdapterRegistryShape["listProviders"] = () =>
75 registry.listInstances.pipe(
76 Effect.map((instances) => {
77 const kinds = new Set<ProviderDriverKind>();
78 for (const instance of instances) {
79 const defaultId = defaultInstanceIdForDriver(instance.driverKind);
80 if (instance.instanceId === defaultId) {
81 // Only the default-instance rows show up through the legacy
82 // shim — custom instances like `codex_personal` have no
83 // `ProviderDriverKind` equivalent.
84 kinds.add(instance.driverKind);
85 }
86 }
87 return Array.from(kinds);
88 }),
89 );
90
9170 return {
9271 getByInstance,
9372 getInstanceInfo,
9473 listInstances,
95 listProviders,
96 // Proxy directly — the facade has no state of its own; the instance
97 // registry already coalesces adds/removes/rebuilds into one emission.
98 streamChanges: registry.streamChanges,
9974 subscribeChanges: registry.subscribeChanges,
10075 } satisfies ProviderAdapterRegistryShape;
10176});
apps/server/src/provider/Layers/ProviderService.test.ts-4
......@@ -514,8 +514,6 @@ it.effect(
514514 })
515515 : Effect.fail(unsupported()),
516516 listInstances: () => Effect.succeed([instanceId]),
517 listProviders: () => Effect.succeed([driverKind] as const),
518 streamChanges: Stream.empty,
519517 subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) =>
520518 PubSub.subscribe(pubsub),
521519 ),
......@@ -594,8 +592,6 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance
594592 })
595593 : Effect.fail(unsupported()),
596594 listInstances: () => Effect.succeed([instanceId]),
597 listProviders: () => Effect.succeed([CODEX_DRIVER] as const),
598 streamChanges: Stream.empty,
599595 subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) =>
600596 PubSub.subscribe(pubsub),
601597 ),
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+3-35
......@@ -1,21 +1,9 @@
11/**
22 * ProviderAdapterRegistry - Lookup boundary for provider adapter implementations.
33 *
4 * Maps a `ProviderInstanceId` (the new per-instance routing key) or a
5 * `ProviderDriverKind` (legacy single-instance-per-driver key) to the concrete
6 * adapter service (Codex, Claude, etc). It does not own session lifecycle
7 * or routing rules; `ProviderService` uses this registry together with
8 * `ProviderSessionDirectory`.
9 *
10 * During the driver/instance migration this tag exposes both flavours:
11 *
12 * - `getByInstance` / `listInstances` — new per-instance routing. Callers
13 * that already know an `instanceId` (threads, sessions, events)
14 * should prefer these.
15 * (`defaultInstanceIdForDriver(kind) === kind`), matching the pre-Slice-D
16 * behaviour. New code should not grow additional callers of the kind-keyed
17 * methods; they exist so the settings UI, WS refresh RPC, and a handful
18 * of legacy persisted rows can still be routed during the rollout.
4 * Maps a `ProviderInstanceId` to its provider adapter. `ProviderService` uses
5 * this registry with `ProviderSessionDirectory`. The registry does not own
6 * session lifecycle or routing rules.
197 *
208 * @module ProviderAdapterRegistry
219 */
......@@ -24,7 +12,6 @@ import * as Context from "effect/Context";
2412import type * as Effect from "effect/Effect";
2513import type * as PubSub from "effect/PubSub";
2614import type * as Scope from "effect/Scope";
27import type * as Stream from "effect/Stream";
2815
2916import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";
3017import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
......@@ -64,25 +51,6 @@ export interface ProviderAdapterRegistryShape {
6451 */
6552 readonly listInstances: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>;
6653
67 /**
68 * Legacy: list provider kinds whose default instance is currently
69 * registered.
70 *
71 * @deprecated Prefer `listInstances`. Retained for migration-era call
72 * sites that iterate providers to build UI/metrics.
73 */
74 readonly listProviders: () => Effect.Effect<ReadonlyArray<ProviderDriverKind>>;
75
76 /**
77 * Change notification stream mirroring `ProviderInstanceRegistry.streamChanges`.
78 * Emits one `void` tick whenever the set of live instances changes
79 * (instance added, removed, or rebuilt after a settings edit). Consumers
80 * that fan out `adapter.streamEvents` per instance — e.g. `ProviderService`'s
81 * runtime event bus — re-pull `listInstances` on each tick and fork new
82 * subscriptions for instances they haven't seen yet.
83 */
84 readonly streamChanges: Stream.Stream<void>;
85
8654 /**
8755 * Acquire a change subscription synchronously in the caller's current fiber.
8856 * Consumers that must avoid missing a publish between initial reconciliation
apps/server/src/provider/testUtils/providerAdapterRegistryMock.ts+2-27
......@@ -2,15 +2,6 @@
22 * Test helpers for constructing a `ProviderAdapterRegistryShape` mock from a
33 * kind-keyed adapter map.
44 *
5 * Tests historically assembled a `registry` object with only `getByProvider`
6 * + `listProviders` populated. Slice D grew the shape with `getByInstance`
7 * and `listInstances`; this helper fills both in from a single kind-keyed
8 * input so individual fixtures can stay concise.
9 *
10 * Non-default instance ids (e.g. `codex_personal`) are not addressable via
11 * the shim returned here — the legacy test fixtures only ever had
12 * single-instance-per-driver data anyway.
13 *
145 * @module provider/testUtils/providerAdapterRegistryMock
156 */
167import {
......@@ -20,9 +11,6 @@ import {
2011} from "@t3tools/contracts";
2112import * as Effect from "effect/Effect";
2213import * as PubSub from "effect/PubSub";
23import * as Record from "effect/Record";
24import * as Result from "effect/Result";
25import * as Stream from "effect/Stream";
2614
2715import { ProviderUnsupportedError, type ProviderAdapterError } from "../Errors.ts";
2816import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts";
......@@ -34,9 +22,8 @@ export type KindAdapterMap = Partial<
3422
3523/**
3624 * Build a `ProviderAdapterRegistryShape` from a kind-keyed adapter map.
37 * Every adapter present in the map is addressable via both the legacy
38 * `getByProvider(kind)` path and the new `getByInstance(id)` path (where
39 * `id = defaultInstanceIdForDriver(kind)`).
25 * Every adapter present in the map is addressable through its default
26 * provider instance id.
4027 */
4128export const makeAdapterRegistryMock = (adapters: KindAdapterMap): ProviderAdapterRegistryShape => {
4229 const byInstanceId = new Map<ProviderInstanceId, ProviderAdapterShape<ProviderAdapterError>>();
......@@ -80,18 +67,6 @@ export const makeAdapterRegistryMock = (adapters: KindAdapterMap): ProviderAdapt
8067 });
8168 },
8269 listInstances: () => Effect.succeed(Array.from(byInstanceId.keys())),
83 listProviders: () =>
84 Effect.succeed(
85 Record.keys(
86 Record.filterMap(adapters, (adapter, kind) =>
87 adapter !== undefined ? Result.succeed(kind) : Result.failVoid,
88 ),
89 ),
90 ),
91 // Static test fixtures don't reload; an empty stream is enough to
92 // satisfy the shape. Tests exercising hot-reload build their own
93 // stream via the real `ProviderInstanceRegistry`.
94 streamChanges: Stream.empty,
9570 subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) =>
9671 PubSub.subscribe(pubsub),
9772 ),
apps/server/src/textGeneration/TextGeneration.ts-12
......@@ -73,15 +73,6 @@ export interface ThreadTitleGenerationResult {
7373 title: string;
7474}
7575
76export interface TextGenerationService {
77 generateCommitMessage(
78 input: CommitMessageGenerationInput,
79 ): Promise<CommitMessageGenerationResult>;
80 generatePrContent(input: PrContentGenerationInput): Promise<PrContentGenerationResult>;
81 generateBranchName(input: BranchNameGenerationInput): Promise<BranchNameGenerationResult>;
82 generateThreadTitle(input: ThreadTitleGenerationInput): Promise<ThreadTitleGenerationResult>;
83}
84
8576/**
8677 * TextGeneration - Service tag for commit and change request text generation.
8778 */
......@@ -116,9 +107,6 @@ export class TextGeneration extends Context.Service<
116107 }
117108>()("t3/textGeneration/TextGeneration") {}
118109
119/** @deprecated Use `TextGeneration["Service"]`. */
120export type TextGenerationShape = TextGeneration["Service"];
121
122110type TextGenerationOp =
123111 | "generateCommitMessage"
124112 | "generatePrContent"
apps/web/src/cloud/linkEnvironment.ts-45
......@@ -16,7 +16,6 @@ import {
1616 WS_METHODS,
1717} from "@t3tools/contracts";
1818import {
19 type RelayClientDeviceRecord,
2019 type RelayClientEnvironmentRecord,
2120 type RelayEnvironmentLinkResponse,
2221 type RelayManagedEndpointProviderKind,
......@@ -26,10 +25,6 @@ import { request, runStream } from "@t3tools/client-runtime/rpc";
2625import { makeEnvironmentHttpApiClient } from "@t3tools/client-runtime/rpc";
2726import { ManagedRelay, relayProtectedErrorMessage } from "@t3tools/client-runtime/relay";
2827
29import {
30 readPrimaryEnvironmentDescriptor,
31 readPrimaryEnvironmentTarget,
32} from "../environments/primary";
3328import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer";
3429import { resolveCloudPublicConfig } from "./publicConfig";
3530import {
......@@ -215,20 +210,6 @@ export function collectCloudLinkTargets(input: {
215210 return [...byId.values()];
216211}
217212
218export function readPrimaryCloudLinkTarget(): CloudLinkTarget | null {
219 const descriptor = readPrimaryEnvironmentDescriptor();
220 const target = readPrimaryEnvironmentTarget();
221 if (!descriptor || !target) {
222 return null;
223 }
224 return {
225 environmentId: descriptor.environmentId,
226 label: descriptor.label,
227 httpBaseUrl: target.target.httpBaseUrl,
228 wsBaseUrl: target.target.wsBaseUrl,
229 };
230}
231
232213export function listManagedCloudEnvironments(input: {
233214 readonly clerkToken: string;
234215}): Effect.Effect<
......@@ -260,32 +241,6 @@ export function listManagedCloudEnvironments(input: {
260241 });
261242}
262243
263export function listCloudDevices(input: {
264 readonly clerkToken: string;
265}): Effect.Effect<
266 ReadonlyArray<RelayClientDeviceRecord>,
267 CloudEnvironmentLinkError,
268 ManagedRelay.ManagedRelayClient
269> {
270 return Effect.gen(function* () {
271 if (!relayUrl()) {
272 return yield* new CloudEnvironmentLinkError({
273 message: "T3CODE_RELAY_URL is not configured.",
274 });
275 }
276 const relayClient = yield* ManagedRelay.ManagedRelayClient;
277 return yield* relayClient.listDevices({ clerkToken: input.clerkToken }).pipe(
278 Effect.mapError(
279 (cause) =>
280 new CloudEnvironmentLinkError({
281 message: "Could not list cloud devices.",
282 cause,
283 }),
284 ),
285 );
286 });
287}
288
289244export function readPrimaryCloudLinkState(input: {
290245 readonly target: CloudLinkTarget;
291246}): Effect.Effect<CloudLinkState | null, CloudEnvironmentLinkError, HttpClient.HttpClient> {
apps/web/src/components/DiffPanelShell.tsx-14
......@@ -46,20 +46,6 @@ export function DiffPanelShell(props: {
4646 );
4747}
4848
49export function DiffPanelHeaderSkeleton() {
50 return (
51 <>
52 <div className="min-w-0 flex-1">
53 <Skeleton className="h-8 w-32 rounded-lg" />
54 </div>
55 <div className="flex shrink-0 gap-1">
56 <Skeleton className="size-7 rounded-md" />
57 <Skeleton className="size-7 rounded-md" />
58 </div>
59 </>
60 );
61}
62
6349function DiffFileHeaderSkeleton({ titleClassName }: { titleClassName: string }) {
6450 return (
6551 <div className="flex h-8 items-center gap-2 px-2 pr-3">
apps/web/src/components/ProviderUpdateLaunchNotification.logic.ts-4
......@@ -202,10 +202,6 @@ export function providerUpdateNotificationKey(
202202 return parts.length > 0 ? parts.join("|") : null;
203203}
204204
205export function providerUpdateCandidateKey(provider: ProviderUpdateCandidate): string {
206 return providerUpdateNotificationKey([provider])!;
207}
208
209205export function formatProviderList(providers: ReadonlyArray<Pick<ServerProvider, "driver">>) {
210206 const names = providers.map(
211207 (provider) => PROVIDER_DISPLAY_NAMES[provider.driver] ?? provider.driver,
apps/web/src/components/chat/ChangedFilesTree.test.tsx-11
......@@ -22,17 +22,6 @@ describe("ChangedFilesCard", () => {
2222
2323 expect(markup).toContain('data-changed-files-state="expanded"');
2424 expect(markup).toContain('aria-expanded="true"');
25 expect(markup).toContain("whitespace-nowrap");
26 expect(markup).toContain('class="group flex min-w-0 flex-1 items-center rounded-xl');
27 expect(markup).not.toMatch(/class="group flex min-w-0 flex-1 items-center[^"]*overflow-hidden/);
28 expect(markup).toContain('class="flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden"');
29 expect(markup).toContain('class="flex shrink-0 items-center gap-1 whitespace-nowrap');
30 expect(markup).toContain('class="ml-1 hidden min-w-0 flex-1 truncate');
31 expect(markup).toContain("@[24rem]/changed-files:inline");
32 expect(markup).not.toContain("sm:inline");
33 expect(markup).toContain('class="flex shrink-0 items-center gap-1.5 pr-1"');
34 expect(markup).toContain("!size-[22px]");
35 expect(markup).toContain("size-3");
3625 expect(markup).toContain('aria-label="Collapse all folders"');
3726 expect(markup).toContain('aria-label="Open diff"');
3827 expect(markup).toContain('role="group" aria-label="2 additions, 1 deletions"');
apps/web/src/components/chat/ComposerCommandMenu.test.tsx+3-36
......@@ -5,24 +5,7 @@ import { describe, expect, it } from "vite-plus/test";
55import { ComposerCommandMenu } from "./ComposerCommandMenu";
66
77describe("ComposerCommandMenu", () => {
8 it("renders slash-command results as an attached composer drawer", () => {
9 const markup = renderToStaticMarkup(
10 <ComposerCommandMenu
11 items={[]}
12 resolvedTheme="dark"
13 isLoading={false}
14 triggerKind="slash-command"
15 activeItemId={null}
16 onHighlightedItemChange={() => {}}
17 onSelect={() => {}}
18 />,
19 );
20
21 expect(markup).toContain('data-composer-command-drawer="true"');
22 expect(markup).not.toContain("dropdown-glass");
23 });
24
25 it("renders commands without a category heading or invented icons", () => {
8 it("renders slash commands with their descriptions", () => {
269 const markup = renderToStaticMarkup(
2710 <ComposerCommandMenu
2811 items={[
......@@ -45,16 +28,9 @@ describe("ComposerCommandMenu", () => {
4528
4629 expect(markup).toContain("/model");
4730 expect(markup).toContain("Switch response model for this thread");
48 expect(markup).not.toContain("Built-in");
49 expect(markup).not.toContain("<svg");
50 expect(markup).toContain("font-sans text-xs font-medium");
51 expect(markup).not.toContain("font-mono");
52 expect(markup).not.toContain("grid-cols-");
53 expect(markup).toContain("max-w-[45%]");
54 expect(markup).toContain("text-left");
5531 });
5632
57 it("renders the skill source icon inside its badge", () => {
33 it("shows the app source for an app skill", () => {
5834 const markup = renderToStaticMarkup(
5935 <ComposerCommandMenu
6036 items={[
......@@ -85,18 +61,10 @@ describe("ComposerCommandMenu", () => {
8561 expect(markup).toContain('data-slot="badge"');
8662 expect(markup).toContain(">App Skill</span>");
8763 expect(markup).toContain("Open and control the in-app browser");
88 expect(markup).toContain("max-w-[48ch]");
89 expect(markup).toContain("text-secondary-label text-xs");
90 expect(markup).toContain("ms-auto");
91 expect(markup).toContain("text-current");
92 expect(markup.indexOf("Open and control the in-app browser")).toBeLessThan(
93 markup.indexOf(">App Skill</span>"),
94 );
9564 expect(markup).toContain("<svg");
96 expect(markup.indexOf('data-slot="badge"')).toBeLessThan(markup.indexOf("<svg"));
9765 });
9866
99 it("keeps slash skills aligned with the source icon inside the badge", () => {
67 it("shows the repo source for a slash skill", () => {
10068 const markup = renderToStaticMarkup(
10169 <ComposerCommandMenu
10270 items={[
......@@ -129,6 +97,5 @@ describe("ComposerCommandMenu", () => {
12997 expect(markup).toContain("lucide-folder");
13098 expect(markup).toContain(">Repo</span>");
13199 expect(markup).toContain("Find the right skill or workflow");
132 expect(markup).not.toContain("font-medium text-secondary-label");
133100 });
134101});
apps/web/src/components/chat/ComposerPrimaryActions.test.tsx+1-32
......@@ -44,27 +44,6 @@ function renderPendingActions(isRunning: boolean) {
4444 );
4545}
4646
47function renderStandaloneStop() {
48 return renderToStaticMarkup(
49 createElement(ComposerPrimaryActions, {
50 compact: true,
51 pendingAction: null,
52 isRunning: true,
53 showPlanFollowUpPrompt: false,
54 promptHasText: false,
55 isSendBusy: false,
56 sendDisabledReason: null,
57 isConnecting: false,
58 isEnvironmentUnavailable: false,
59 isPreparingWorktree: false,
60 hasSendableContent: false,
61 onPreviousPendingQuestion: () => {},
62 onInterrupt: () => {},
63 onImplementPlanInNewThread: () => {},
64 }),
65 );
66}
67
6847function renderRunningActions(showSendWhileRunning: boolean, hasSendableContent: boolean) {
6948 return renderToStaticMarkup(
7049 createElement(ComposerPrimaryActions, {
......@@ -219,12 +198,6 @@ describe("ComposerPrimaryActions", () => {
219198 expect(renderPendingActions(false)).not.toContain('aria-label="Stop generation"');
220199 });
221200
222 it("matches the small pending action size without changing the standalone size", () => {
223 expect(renderPendingActions(true)).toContain("size-8 sm:size-7");
224 expect(renderStandaloneStop()).toContain("size-8 sm:h-8 sm:w-8");
225 expect(renderStandaloneStop()).not.toContain("sm:size-7");
226 });
227
228201 it("renders stage artwork inside the send button when artwork identification is active", () => {
229202 stageArtworkState.mode = "artwork";
230203 stageArtworkState.variant = "nightly";
......@@ -232,17 +205,14 @@ describe("ComposerPrimaryActions", () => {
232205 const markup = renderSendButton();
233206
234207 expect(markup).toContain("stage-nightly");
235 expect(markup).toContain("bg-transparent text-white");
236 expect(markup).not.toContain("bg-message-action text-message-action-foreground");
237208 });
238209
239 it("keeps the normal send-button fill when artwork identification is inactive", () => {
210 it("hides stage artwork when artwork identification is inactive", () => {
240211 stageArtworkState.variant = "nightly";
241212
242213 const markup = renderSendButton();
243214
244215 expect(markup).not.toContain("stage-nightly");
245 expect(markup).toContain("bg-message-action text-message-action-foreground");
246216 });
247217
248218 it("only renders stop while running when Enter-to-send is available", () => {
......@@ -258,7 +228,6 @@ describe("ComposerPrimaryActions", () => {
258228 expect(markup).toContain('aria-label="Stop generation"');
259229 expect(markup).toContain('aria-label="Send message"');
260230 expect(markup).toContain('type="submit"');
261 expect(markup).toContain("size-9 sm:size-8");
262231 });
263232
264233 it("keeps stop as the only action while running with an empty composer", () => {
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-126
......@@ -16,12 +16,7 @@ vi.mock("@legendapp/list/react", async () => {
1616 ListFooterComponent?: ReactNode;
1717 anchoredEndSpace?: {
1818 anchorIndex: number;
19 anchorMaxSize?: number;
20 anchorOffset?: number;
21 onReady?: (info: { anchorIndex: number }) => void;
2219 };
23 contentInsetEndAdjustment?: number;
24 className?: string;
2520 maintainScrollAtEnd?:
2621 | boolean
2722 | {
......@@ -32,27 +27,12 @@ vi.mock("@legendapp/list/react", async () => {
3227 layout?: boolean;
3328 };
3429 };
35 maintainVisibleContentPosition?:
36 | boolean
37 | {
38 data?: boolean;
39 size?: boolean;
40 shouldRestorePosition?: (item: { id: string }) => boolean;
41 };
4230 ref?: Ref<LegendListRef>;
4331 }) => {
44 if (props.anchoredEndSpace) {
45 props.anchoredEndSpace.onReady?.({ anchorIndex: props.anchoredEndSpace.anchorIndex });
46 }
4732 return (
4833 <div
4934 data-testid={legendListTestId}
5035 data-anchor-index={props.anchoredEndSpace?.anchorIndex}
51 data-anchor-max-size={props.anchoredEndSpace?.anchorMaxSize}
52 data-anchor-offset={props.anchoredEndSpace?.anchorOffset}
53 data-anchor-on-ready={Boolean(props.anchoredEndSpace?.onReady)}
54 data-content-inset-end={props.contentInsetEndAdjustment}
55 data-class-name={props.className}
5636 data-maintain-scroll-at-end={props.maintainScrollAtEnd ? "enabled" : undefined}
5737 data-maintain-scroll-at-end-animated={
5838 typeof props.maintainScrollAtEnd === "object"
......@@ -74,26 +54,6 @@ vi.mock("@legendapp/list/react", async () => {
7454 ? props.maintainScrollAtEnd.on?.layout
7555 : undefined
7656 }
77 data-maintain-visible-content-position={
78 typeof props.maintainVisibleContentPosition === "object"
79 ? "object"
80 : props.maintainVisibleContentPosition
81 }
82 data-maintain-visible-content-position-data={
83 typeof props.maintainVisibleContentPosition === "object"
84 ? props.maintainVisibleContentPosition.data
85 : undefined
86 }
87 data-maintain-visible-content-position-size={
88 typeof props.maintainVisibleContentPosition === "object"
89 ? props.maintainVisibleContentPosition.size
90 : undefined
91 }
92 data-maintain-visible-content-position-restore={
93 typeof props.maintainVisibleContentPosition === "object"
94 ? Boolean(props.maintainVisibleContentPosition.shouldRestorePosition)
95 : undefined
96 }
9757 >
9858 {props.ListHeaderComponent}
9959 {props.data.map((item) => (
......@@ -293,7 +253,7 @@ describe("MessagesTimeline", () => {
293253 expect(markup).toContain("codex-thread-1");
294254 });
295255
296 it("renders the worked-for row at assistant response text size", () => {
256 it("renders elapsed time for a completed turn", () => {
297257 const turnId = TurnId.make("turn-with-fold");
298258 const assistantEntry = buildAssistantTimelineEntry("Done.");
299259 const markup = renderToStaticMarkup(
......@@ -328,23 +288,6 @@ describe("MessagesTimeline", () => {
328288 );
329289
330290 expect(markup).toContain("Worked for 8.0s");
331 expect(markup).toContain("px-1 text-sm leading-relaxed text-muted-foreground");
332 });
333
334 it("uses the larger leading inset only when the top fade is enabled", () => {
335 const timelineEntries = [buildUserTimelineEntry("Hello")];
336
337 const compactMarkup = renderToStaticMarkup(
338 <MessagesTimeline {...buildProps()} timelineEntries={timelineEntries} />,
339 );
340 const fadedMarkup = renderToStaticMarkup(
341 <MessagesTimeline {...buildProps()} timelineEntries={timelineEntries} topFadeEnabled />,
342 );
343
344 expect(compactMarkup).toContain('class="h-3 sm:h-4"');
345 expect(compactMarkup).not.toContain("topbar-scroll-fade");
346 expect(fadedMarkup).toContain('class="h-[var(--workspace-titlebar-scroll-fade-height)]"');
347 expect(fadedMarkup).toContain("topbar-scroll-fade");
348291 });
349292
350293 it("keeps assistant changed-files headers sticky below the thread header", () => {
......@@ -491,74 +434,6 @@ describe("MessagesTimeline", () => {
491434 expect(resolveTimelineMinimapInteractiveWidth(40, true)).toBe("22rem");
492435 });
493436
494 it("anchors the first user message using its measured height", () => {
495 const onAnchorReady = vi.fn();
496 const firstEntry = {
497 ...buildUserTimelineEntry("First prompt."),
498 message: {
499 ...buildUserTimelineEntry("First prompt.").message,
500 attachments: [
501 {
502 type: "image" as const,
503 id: "attachment-1",
504 name: "screenshot.png",
505 mimeType: "image/png",
506 sizeBytes: 1,
507 previewUrl: "data:image/png;base64,iVBORw0KGgo=",
508 },
509 ],
510 },
511 };
512 const markup = renderToStaticMarkup(
513 <MessagesTimeline
514 {...buildProps()}
515 anchorMessageId={firstEntry.message.id}
516 onAnchorReady={onAnchorReady}
517 contentInsetEndAdjustment={144}
518 timelineEntries={[firstEntry]}
519 />,
520 );
521
522 expect(markup).toContain('data-anchor-index="0"');
523 expect(markup).toContain('data-anchor-offset="24"');
524 expect(markup).toContain('data-anchor-on-ready="true"');
525 expect(markup).not.toContain("data-anchor-max-size=");
526 expect(markup).toContain('data-content-inset-end="144"');
527 expect(markup).toContain("[overflow-anchor:none]");
528 expect(markup).not.toContain('data-maintain-scroll-at-end="enabled"');
529 expect(markup).toContain('data-maintain-visible-content-position="object"');
530 expect(markup).toContain('data-maintain-visible-content-position-data="true"');
531 expect(markup).toContain('data-maintain-visible-content-position-size="true"');
532 expect(markup).toContain('data-maintain-visible-content-position-restore="true"');
533 expect(onAnchorReady).toHaveBeenCalledOnce();
534 expect(onAnchorReady).toHaveBeenCalledWith(firstEntry.message.id, 0);
535 });
536
537 it("does not reserve end space for a follow-up user message", () => {
538 const onAnchorReady = vi.fn();
539 const firstEntry = buildUserTimelineEntry("First prompt.");
540 const secondEntry = {
541 ...buildUserTimelineEntry("Newest prompt."),
542 id: "entry-2",
543 message: {
544 ...buildUserTimelineEntry("Newest prompt.").message,
545 id: MessageId.make("message-2"),
546 },
547 };
548 const markup = renderToStaticMarkup(
549 <MessagesTimeline
550 {...buildProps()}
551 anchorMessageId={secondEntry.message.id}
552 onAnchorReady={onAnchorReady}
553 timelineEntries={[firstEntry, secondEntry]}
554 />,
555 );
556
557 expect(markup).not.toContain("data-anchor-index=");
558 expect(markup).toContain('data-maintain-scroll-at-end="enabled"');
559 expect(onAnchorReady).not.toHaveBeenCalled();
560 });
561
562437 it("renders generic attachments as download links instead of image previews", () => {
563438 const entry = {
564439 ...buildUserTimelineEntry("Read the report."),
apps/web/src/components/chat/providerIconUtils.ts-12
......@@ -1,6 +1,5 @@
11import { ProviderDriverKind } from "@t3tools/contracts";
22import { ClaudeAI, CursorIcon, GrokIcon, Icon, OpenAI, OpenCodeIcon } from "../Icons";
3import { PROVIDER_OPTIONS } from "../../session-logic";
43
54export const PROVIDER_ICON_BY_PROVIDER: Partial<Record<ProviderDriverKind, Icon>> = {
65 [ProviderDriverKind.make("codex")]: OpenAI,
......@@ -10,17 +9,6 @@ export const PROVIDER_ICON_BY_PROVIDER: Partial<Record<ProviderDriverKind, Icon>
109 [ProviderDriverKind.make("grok")]: GrokIcon,
1110};
1211
13function isAvailableProviderOption(option: (typeof PROVIDER_OPTIONS)[number]): option is {
14 value: ProviderDriverKind;
15 label: string;
16 available: true;
17 pickerSidebarBadge?: "new" | "soon";
18} {
19 return option.available;
20}
21
22export const AVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter(isAvailableProviderOption);
23
2412export type ModelEsque = {
2513 slug: string;
2614 name: string;
apps/web/src/components/desktopUpdate.logic.ts-5
......@@ -125,11 +125,6 @@ export function shouldToastDesktopUpdateActionResult(result: DesktopUpdateAction
125125 return getDesktopUpdateActionError(result) !== null;
126126}
127127
128export function shouldHighlightDesktopUpdateError(state: DesktopUpdateState | null): boolean {
129 if (!state || state.status !== "error") return false;
130 return state.errorContext === "download" || state.errorContext === "install";
131}
132
133128export function canCheckForUpdate(state: DesktopUpdateState | null): boolean {
134129 if (!state || !state.enabled) return false;
135130 return (
apps/web/src/composer-logic.ts-2
......@@ -221,8 +221,6 @@ export function isCollapsedCursorAdjacentToInlineToken(
221221 return false;
222222}
223223
224export const isCollapsedCursorAdjacentToMention = isCollapsedCursorAdjacentToInlineToken;
225
226224export function detectComposerTrigger(text: string, cursorInput: number): ComposerTrigger | null {
227225 const cursor = clampCursor(text, cursorInput);
228226 const lineStart = text.lastIndexOf("\n", Math.max(0, cursor - 1)) + 1;
apps/web/src/composerDraftStore.ts-8
......@@ -4117,11 +4117,3 @@ export function finalizePromotedDraftThreadByRef(threadRef: ScopedThreadRef): vo
41174117 }
41184118 clearBackgroundDraftSubmissionByRef(threadRef);
41194119}
4120
4121export function finalizePromotedDraftThreadsByRef(
4122 serverThreadRefs: Iterable<ScopedThreadRef>,
4123): void {
4124 for (const threadRef of serverThreadRefs) {
4125 finalizePromotedDraftThreadByRef(threadRef);
4126 }
4127}
apps/web/src/keybindings.ts-16
......@@ -411,14 +411,6 @@ export function isDiffToggleShortcut(
411411 return matchesCommandShortcut(event, keybindings, "diff.toggle", options);
412412}
413413
414export function isPreviewToggleShortcut(
415 event: ShortcutEventLike,
416 keybindings: ResolvedKeybindingsConfig,
417 options?: ShortcutMatchOptions,
418): boolean {
419 return matchesCommandShortcut(event, keybindings, "preview.toggle", options);
420}
421
422414export function isPreviewRefreshShortcut(
423415 event: ShortcutEventLike,
424416 keybindings: ResolvedKeybindingsConfig,
......@@ -427,14 +419,6 @@ export function isPreviewRefreshShortcut(
427419 return matchesCommandShortcut(event, keybindings, "preview.refresh", options);
428420}
429421
430export function isPreviewFocusUrlShortcut(
431 event: ShortcutEventLike,
432 keybindings: ResolvedKeybindingsConfig,
433 options?: ShortcutMatchOptions,
434): boolean {
435 return matchesCommandShortcut(event, keybindings, "preview.focusUrl", options);
436}
437
438422export function isChatNewShortcut(
439423 event: ShortcutEventLike,
440424 keybindings: ResolvedKeybindingsConfig,
apps/web/src/lib/contextWindow.ts-22
......@@ -25,28 +25,6 @@ export type ContextWindowSnapshot = NullableContextWindowUsage & {
2525 readonly updatedAt: string;
2626};
2727
28/** Map a provider driver kind to a user-facing display name. */
29export function formatProviderDisplayName(provider: string | null | undefined): string {
30 if (!provider) return "This agent";
31 switch (provider) {
32 case "claudeAgent":
33 case "claude":
34 return "Claude";
35 case "codex":
36 return "Codex";
37 case "cursor":
38 return "Cursor";
39 case "opencode":
40 return "OpenCode";
41 default: {
42 // Title-case unknown driver kinds so they read reasonably.
43 const trimmed = provider.replace(/Agent$/i, "").trim();
44 if (trimmed.length === 0) return provider;
45 return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
46 }
47 }
48}
49
5028export function deriveLatestContextWindowSnapshot(
5129 activities: ReadonlyArray<OrchestrationThreadActivity>,
5230): ContextWindowSnapshot | null {
apps/web/src/lib/utils.ts+1-7
......@@ -1,4 +1,4 @@
1import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts";
1import { MessageId, ProjectId, ThreadId } from "@t3tools/contracts";
22import { type CxOptions, cx } from "class-variance-authority";
33import * as Encoding from "effect/Encoding";
44import { twMerge } from "tailwind-merge";
......@@ -16,10 +16,6 @@ export function isWindowsPlatform(platform: string): boolean {
1616 return /^win(dows)?/i.test(platform);
1717}
1818
19export function isLinuxPlatform(platform: string): boolean {
20 return /linux/i.test(platform);
21}
22
2319export function normalizeSearchText(value: string): string {
2420 return value.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/\s+/g, " ").trim();
2521}
......@@ -46,8 +42,6 @@ export function randomUUID(): string {
4642 return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
4743}
4844
49export const newCommandId = (): CommandId => CommandId.make(randomUUID());
50
5145export const newProjectId = (): ProjectId => ProjectId.make(randomUUID());
5246
5347export const newThreadId = (): ThreadId => ThreadId.make(randomUUID());
apps/web/src/portDiscoveryState.ts-19
......@@ -85,22 +85,3 @@ export function useThreadDiscoveredPorts(input: {
8585 [input.threadId, ports],
8686 );
8787}
88
89export function useTerminalDiscoveredPorts(input: {
90 readonly environmentId: EnvironmentId | null;
91 readonly threadId: ThreadId | null;
92 readonly terminalId: string | null;
93}): ReadonlyArray<DiscoveredLocalServer> {
94 const ports = useDiscoveredPorts(input.environmentId);
95 return useMemo(
96 () =>
97 input.threadId && input.terminalId
98 ? ports.filter(
99 (port) =>
100 port.terminal?.threadId === input.threadId &&
101 port.terminal.terminalId === input.terminalId,
102 )
103 : EMPTY_PORTS,
104 [input.terminalId, input.threadId, ports],
105 );
106}
apps/web/src/providerInstances.ts-11
......@@ -299,17 +299,6 @@ export function getProviderInstanceEntry(
299299 return deriveProviderInstanceEntries(providers).find((entry) => entry.instanceId === instanceId);
300300}
301301
302/**
303 * Model list for a specific instance. Returns `[]` when the instance isn't
304 * present so callers don't have to thread optionality through render code.
305 */
306export function getProviderInstanceModels(
307 providers: ReadonlyArray<ServerProvider>,
308 instanceId: ProviderInstanceId,
309): ReadonlyArray<ServerProviderModel> {
310 return getProviderInstanceEntry(providers, instanceId)?.models ?? [];
311}
312
313302/**
314303 * Default model slug for a specific instance: its declared built-in default,
315304 * then its first built-in model, then any model it reports, then the driver-level default. Custom
apps/web/src/providerModels.ts-18
......@@ -38,14 +38,6 @@ export function getProviderSnapshot(
3838 return providers.find((candidate) => candidate.instanceId === defaultInstanceId);
3939}
4040
41export function getProviderDisplayName(
42 providers: ReadonlyArray<ServerProvider>,
43 provider: ProviderDriverKind,
44): string {
45 const snapshot = getProviderSnapshot(providers, provider);
46 return snapshot?.displayName?.trim() || formatProviderDriverKindLabel(provider);
47}
48
4941export function getProviderInteractionModeToggle(
5042 providers: ReadonlyArray<ServerProvider>,
5143 provider: ProviderDriverKind,
......@@ -53,16 +45,6 @@ export function getProviderInteractionModeToggle(
5345 return getProviderSnapshot(providers, provider)?.showInteractionModeToggle ?? true;
5446}
5547
56export function isProviderEnabled(
57 providers: ReadonlyArray<ServerProvider>,
58 provider: ProviderDriverKind,
59): boolean {
60 if (providers.length === 0) {
61 return true;
62 }
63 return getProviderSnapshot(providers, provider)?.enabled ?? false;
64}
65
6648// Resolve an instance selection to the correlated live driver. If the
6749// instance is absent, fall back to a live enabled provider instead of
6850// inferring a driver from the missing instance id.
apps/web/src/routes/-chatIndexTitlebar.test.ts deleted-29
......@@ -1,29 +0,0 @@
1// @effect-diagnostics nodeBuiltinImport:off
2// Regression coverage compares the onboarding header with the shared titlebar contract.
3import * as NodeFS from "node:fs";
4
5import { describe, expect, it } from "vite-plus/test";
6
7describe("hosted static onboarding header", () => {
8 it("uses the shared workspace topbar geometry", () => {
9 const routeSource = NodeFS.readFileSync(new URL("./_chat.index.tsx", import.meta.url), "utf8");
10 const onboardingStart = routeSource.indexOf("function HostedStaticOnboardingState()");
11 const onboardingEnd = routeSource.indexOf('<Empty className="flex-1">', onboardingStart);
12
13 expect(onboardingStart).toBeGreaterThanOrEqual(0);
14 expect(onboardingEnd).toBeGreaterThan(onboardingStart);
15
16 const onboardingHeader = routeSource.slice(onboardingStart, onboardingEnd);
17
18 expect(onboardingHeader).toContain("<WorkspacePageHeader");
19 expect(onboardingHeader).not.toMatch(/(?:^|\s)(?:[\w-]+:)*py-/);
20
21 const headerSource = NodeFS.readFileSync(
22 new URL("../components/WorkspacePageHeader.tsx", import.meta.url),
23 "utf8",
24 );
25 expect(headerSource).toContain("h-[var(--workspace-topbar-height)]");
26 expect(headerSource).toContain("min-h-[var(--workspace-topbar-height)]");
27 expect(headerSource).toContain("COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS");
28 });
29});
apps/web/src/themePalette.ts-2
......@@ -1521,8 +1521,6 @@ export class ThemeLibraryStorageError extends Schema.TaggedErrorClass<ThemeLibra
15211521 }
15221522}
15231523
1524export const isThemeLibraryStorageError = Schema.is(ThemeLibraryStorageError);
1525
15261524function saveCustomThemes(
15271525 storedThemes: ReadonlyArray<unknown>,
15281526 themes: ReadonlyArray<ThemeDefinition>,
pnpm-lock.yaml-6
......@@ -232,9 +232,6 @@ importers:
232232 '@noble/curves':
233233 specifier: 'catalog:'
234234 version: 1.9.1
235 '@noble/hashes':
236 specifier: 'catalog:'
237 version: 1.8.0
238235 '@pierre/diffs':
239236 specifier: 'catalog:'
240237 version: 1.3.0-beta.10(patch_hash=7ef7cb0cbabb17c15cdb137554068b36f15f6f5265e73fb51452aa3380db91aa)(@shikijs/themes@4.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
......@@ -479,9 +476,6 @@ importers:
479476 '@effect/platform-node':
480477 specifier: 4.0.0-beta.103
481478 version: 4.0.0-beta.103(bufferutil@4.1.0)(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))(ioredis@5.11.0)(utf-8-validate@6.0.6)
482 '@effect/platform-node-shared':
483 specifier: 4.0.0-beta.103
484 version: 4.0.0-beta.103(bufferutil@4.1.0)(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))(utf-8-validate@6.0.6)
485479 '@effect/sql-sqlite-bun':
486480 specifier: 4.0.0-beta.103
487481 version: 4.0.0-beta.103(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))
vite.config.ts-1
......@@ -37,7 +37,6 @@ export default defineConfig({
3737 "**/routeTree.gen.ts",
3838 "apps/mobile/android/**",
3939 "apps/mobile/ios/**",
40 "apps/web/public/mockServiceWorker.js",
4140 "apps/web/src/lib/vendor/qrcodegen.ts",
4241 "apps/mobile/uniwind-types.d.ts",
4342 "*.icon/**",