Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/triage/PLAYBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ different code depending on it:

- How is Ada running on this machine: `ada serve` in a terminal, the
background service, or the desktop app?
- Which surface is the user connecting from: the website (app.t3.codes), the
- Which surface is the user connecting from: a web browser, the
desktop app against a local server, the desktop app against a remote server,
or the mobile app?

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mobile-eas-preview-fork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
else
link="$(eas build:list --platform android --build-profile preview:dev --runtime-version "$hash" --status finished --limit 5 --json --non-interactive \
| sed -n '/^\[/,$p' \
| jq -r --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '[.[] | select((.expirationDate // "9999") > $now)][0] | select(. != null) | "https://expo.dev/accounts/pingdotgg/projects/t3-code/builds/\(.id)"')"
| jq -r --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '[.[] | select((.expirationDate // "9999") > $now)][0] | select(. != null) | "https://expo.dev/accounts/jayleaton/projects/t3-code-personal/builds/\(.id)"')"
fi
echo "link=$link" >> "$GITHUB_OUTPUT"
if [ -z "$link" ]; then echo "build=true" >> "$GITHUB_OUTPUT"; fi
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mobile-eas-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ jobs:
# A client EAS built before this job existed; expired ones are gone.
link="$(eas build:list --platform android --build-profile preview:dev --runtime-version "$hash" --status finished --limit 5 --json --non-interactive \
| sed -n '/^\[/,$p' \
| jq -r --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '[.[] | select((.expirationDate // "9999") > $now)][0] | select(. != null) | "https://expo.dev/accounts/pingdotgg/projects/t3-code/builds/\(.id)"')"
| jq -r --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '[.[] | select((.expirationDate // "9999") > $now)][0] | select(. != null) | "https://expo.dev/accounts/jayleaton/projects/t3-code-personal/builds/\(.id)"')"
fi
echo "link=$link" >> "$GITHUB_OUTPUT"
if [ -z "$link" ]; then echo "build=true" >> "$GITHUB_OUTPUT"; fi
Expand Down
5 changes: 3 additions & 2 deletions apps/desktop/src/app/CodexAuthCallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ describe("desktop Codex callback helper", () => {
});
expect(received).toBe(expected);
});
it("returns hosted web to the exact instance and environment without putting the code in its query", async () => {
it("returns web to the exact instance and environment without putting the code in its query", async () => {
const authorizationUrl = request(await freePort());
const expected = callback(authorizationUrl);
const input = {
authorizationUrl,
returnUrl: "https://app.t3.codes/settings/providers?environmentId=remote-one&instanceId=work",
returnUrl:
"http://localhost:5733/settings/providers?environmentId=remote-one&instanceId=work",
environmentId: EnvironmentId.make("remote-one"),
instanceId: ProviderInstanceId.make("work"),
flowId: "flow-one",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/app/DesktopLaunchLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ it.effect.each(["startup", "open-url", "early-second-instance"] as const)(
}).toString();
const request = {
authorizationUrl: authorize.toString(),
returnUrl: "https://app.t3.codes/welcome#agents:remote-one",
returnUrl: "http://localhost:5733/welcome#agents:remote-one",
environmentId: EnvironmentId.make("remote-one"),
instanceId: ProviderInstanceId.make("work"),
flowId: "flow-one",
Expand Down
3 changes: 0 additions & 3 deletions apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,6 @@ describe("DesktopBackendConfiguration", () => {

assert.equal(config.env.OTEL_SDK_DISABLED, "true");
assert.include((config.env.WSLENV ?? "").split(":"), "OTEL_SDK_DISABLED");
assert.equal(config.env.T3CODE_TELEMETRY_ENABLED, "false");
assert.include((config.env.WSLENV ?? "").split(":"), "T3CODE_TELEMETRY_ENABLED");
assert.equal(config.env.OTEL_RESOURCE_ATTRIBUTES, "deployment.environment.name=staging");
assert.include((config.env.WSLENV ?? "").split(":"), "OTEL_RESOURCE_ATTRIBUTES");
}).pipe(
Expand All @@ -714,7 +712,6 @@ describe("DesktopBackendConfiguration", () => {
),
Effect.provideService(HostProcess.Environment, {
OTEL_SDK_DISABLED: "true",
T3CODE_TELEMETRY_ENABLED: "false",
OTEL_RESOURCE_ATTRIBUTES: "deployment.environment.name=staging",
}),
);
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/backend/DesktopBackendConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ const DESKTOP_BACKEND_ENV_NAMES = [
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",
Expand Down
2 changes: 0 additions & 2 deletions apps/desktop/src/shell/DesktopShellEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ const LOGIN_SHELL_ENV_NAMES = [
"SSL_CERT_FILE",
"SSL_CERT_DIR",
"NODE_EXTRA_CA_CERTS",
"T3CODE_TELEMETRY_ENABLED",
"T3CODE_GITHUB_APP_CLIENT_ID",
"__MISE_DIFF",
"JAVA_HOME",
Expand Down Expand Up @@ -484,7 +483,6 @@ const installPosixEnvironment = Effect.fn("desktop.shellEnvironment.installPosix
"SSL_CERT_DIR",
"NODE_EXTRA_CA_CERTS",
"JAVA_HOME",
"T3CODE_TELEMETRY_ENABLED",
"T3CODE_GITHUB_APP_CLIENT_ID",
] as const) {
if (!config.env[name] && shellEnvironment[name]) {
Expand Down
1 change: 0 additions & 1 deletion apps/marketing/src/pages/llms.txt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ Official builds come from ${absoluteUrl("/download")} and ${GITHUB_REPOSITORY_UR
## Official links

- [Download ${APP_NAME}](${absoluteUrl("/download")}): macOS, Windows, and Linux installers
- [Web app](https://app.t3.codes): connect to an ${APP_NAME} server from a browser
- [Source code](${GITHUB_REPOSITORY_URL}): the MIT-licensed repository
- [Releases](${GITHUB_REPOSITORY_URL}/releases): release builds and changelogs

Expand Down
14 changes: 0 additions & 14 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ const runtimeVersionPolicy =
const personalTeamBundleIdentifier = repoEnv.T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID?.trim();
const IOS_BUNDLE_IDENTIFIER_PATTERN = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/;

// Observe must never send this personal client to Expo ingestion.
const observeEndpointUrl = "https://observe.invalid";

if (
isIosPersonalTeamBuild &&
(!personalTeamBundleIdentifier ||
Expand Down Expand Up @@ -99,8 +96,6 @@ function resolveAppVariant(value: string | undefined): AppVariant {
}

const variant = VARIANT_CONFIG[APP_VARIANT];
// Copied thread links open the app. app.t3.codes/.well-known lists every variant's identifiers.
const HOSTED_APP_HOST = "app.t3.codes";
const iosBundleIdentifier = isIosPersonalTeamBuild
? personalTeamBundleIdentifier!
: variant.iosBundleIdentifier;
Expand Down Expand Up @@ -276,14 +271,6 @@ const config: ExpoConfig = {
predictiveBackGestureEnabled: true,
// expo-sensors declares this for its pedometer, which the app does not use.
blockedPermissions: ["android.permission.ACTIVITY_RECOGNITION"],
intentFilters: [
{
action: "VIEW",
autoVerify: true,
data: [{ scheme: "https", host: HOSTED_APP_HOST, pathPrefix: "/threads/" }],
category: ["BROWSABLE", "DEFAULT"],
},
],
},
web: {
favicon: variant.assets.appIcon,
Expand Down Expand Up @@ -421,7 +408,6 @@ const config: ExpoConfig = {
extra: {
eas: {
projectId: "263247b1-0330-46c5-ac6a-7efcba44c359",
observe: { endpointUrl: observeEndpointUrl },
},
appVariant: APP_VARIANT,
personalClient: isPersonalClient,
Expand Down
2 changes: 0 additions & 2 deletions apps/mobile/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Must stay first: the render counter and behavior probe wrap the DevTools hook before any React commit.
import "./src/features/development/render-counter-entry";
import "./src/features/development/behavior-probe-entry";
// Next, so errors thrown while the rest of the app loads are recorded too.
import "./src/features/observability/observe";
import { registerRootComponent } from "expo";
import "react-native-gesture-handler";
import { LogBox } from "react-native";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.json.JSONArray
internal object T3ComposerClipboard {
// Pasted thread links become chips in JS; packages/shared/src/threadLinks.ts parses them.
fun hasThreadLink(text: String): Boolean =
text.contains("t3-thread://v2/") || text.contains("https://app.t3.codes/threads/")
text.contains("t3-thread://v2/")

fun write(context: Context, text: String, fragment: String) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ enum T3ComposerClipboard {

// Pasted thread links become chips in JS; packages/shared/src/threadLinks.ts parses them.
static func hasThreadLink(_ text: String) -> Bool {
text.contains("t3-thread://v2/") || text.contains("https://app.t3.codes/threads/")
text.contains("t3-thread://v2/")
}

static func write(text: String, fragment: String) {
Expand Down
1 change: 0 additions & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
"expo-linking": "~58.0.16",
"expo-network": "58.0.4",
"expo-notifications": "~58.1.5",
"expo-observe": "~58.0.18",
"expo-paste-input": "^0.2.2",
"expo-quick-actions": "^6.0.2",
"expo-secure-store": "~58.0.2",
Expand Down
2 changes: 0 additions & 2 deletions apps/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
type NavigationState,
} from "@react-navigation/native";
import { pipe } from "effect/Function";
import { DEFAULT_HOSTED_APP_URL } from "@t3tools/shared/hostedApp";

import { RegistryContext } from "@effect/atom-react";
import { ProjectChipIconRasterizer } from "./native/ProjectChipIconImages";
Expand Down Expand Up @@ -131,7 +130,6 @@ const appLinking = {
"t3code-personal://",
"t3code-personal-dev://",
"t3code-personal-preview://",
DEFAULT_HOSTED_APP_URL,
],
config: { initialRouteName: "Home" },
filter: shouldHandleAppLink,
Expand Down
5 changes: 1 addition & 4 deletions apps/mobile/src/components/RenderErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Observe } from "expo-observe";
import { Component, type ReactNode } from "react";
import { ScrollView, View } from "react-native";

Expand Down Expand Up @@ -61,9 +60,7 @@ export class RenderErrorBoundary extends Component<
return { failed: true, error };
}

override componentDidCatch(error: unknown, info: { componentStack?: string }) {
// A caught render error never reaches Observe's global handler.
Observe.reportError(error);
override componentDidCatch(_error: unknown, info: { componentStack?: string }) {
this.setState({ componentStack: info.componentStack });
}

Expand Down
19 changes: 6 additions & 13 deletions apps/mobile/src/connection/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { AppState } from "react-native";

import { authClientMetadata } from "../lib/authClientMetadata";
import * as Runtime from "../lib/runtime";
import * as MobileStorage from "../persistence/mobile-storage";
import { clearThreadOutboxEnvironment } from "../state/thread-outbox-removal";
import { clearComposerDraftsEnvironment } from "../state/use-composer-drafts";
import { clearThreadComposerErrorsForEnvironment } from "../state/thread-composer-error";
Expand Down Expand Up @@ -138,18 +137,13 @@ const layerWakeups = Wakeups.layer({
});

const layerCapabilities = Layer.effectContext(
Effect.gen(function* () {
const storage = yield* MobileStorage.MobileStorage;
return Context.make(
Effect.sync(() =>
Context.make(
ClientCapabilities.PrimaryEnvironmentAuth,
ClientCapabilities.PrimaryEnvironmentAuth.of({
bearerToken: Effect.succeedNone,
}),
).pipe(
// The device id persists in secure storage; analytics only sends its hash.
Context.add(ClientCapabilities.AnalyticsDevice, {
id: storage.loadOrCreateAgentAwarenessDeviceId.pipe(Effect.option),
}),
Context.add(
ClientCapabilities.ClientPresentation,
ClientCapabilities.ClientPresentation.of({
Expand Down Expand Up @@ -180,8 +174,8 @@ const layerCapabilities = Layer.effectContext(
),
}),
),
);
}),
),
),
);

const layerPlatformConnectionSource = Layer.succeed(
Expand All @@ -192,7 +186,6 @@ const layerPlatformConnectionSource = Layer.succeed(
);

const layerProvidedConnectionStorage = ConnectionStorage.layer.pipe(Layer.provide(Runtime.layer));
const layerProvidedCapabilities = layerCapabilities.pipe(Layer.provide(Runtime.layer));

const layerEnvironmentOwnedDataCleanup = Layer.succeed(
Persistence.EnvironmentOwnedDataCleanup,
Expand All @@ -215,7 +208,7 @@ type ConnectionPlatformLayerSource =
| typeof Runtime.layer
| typeof layerConnectivity
| typeof layerWakeups
| typeof layerProvidedCapabilities
| typeof layerCapabilities
| typeof layerPlatformConnectionSource
| typeof layerEnvironmentOwnedDataCleanup;

Expand All @@ -228,7 +221,7 @@ export const layer: Layer.Layer<
Runtime.layer,
layerConnectivity,
layerWakeups,
layerProvidedCapabilities,
layerCapabilities,
layerPlatformConnectionSource,
layerEnvironmentOwnedDataCleanup,
);
4 changes: 2 additions & 2 deletions apps/mobile/src/features/connection/pairing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("parsePairingUrl", () => {
it("reads hosted pairing links into backend host fields", () => {
expect(
parsePairingUrl(
"https://app.t3.codes/pair?host=https%3A%2F%2Fdesktop.tailnet.ts.net%2F#token=pairing-token",
"https://hosted.example/pair?host=https%3A%2F%2Fdesktop.tailnet.ts.net%2F#token=pairing-token",
),
).toEqual({
host: "https://desktop.tailnet.ts.net",
Expand Down Expand Up @@ -72,7 +72,7 @@ describe("mobile pairing address validation", () => {
it("validates the backend inside a hosted pairing link", () => {
expect(() =>
validateMobilePairingUrl(
"https://app.t3.codes/pair?host=http%3A%2F%2Flocalhost%3A3773#token=secret",
"https://hosted.example/pair?host=http%3A%2F%2Flocalhost%3A3773#token=secret",
),
).toThrow("Localhost points to this phone");
});
Expand Down
5 changes: 0 additions & 5 deletions apps/mobile/src/features/observability/observe.ts

This file was deleted.

9 changes: 1 addition & 8 deletions apps/mobile/src/features/prompt-links/prompt-link-actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Dialog from "../../components/ui/Dialog";
import { buildNewThreadLink, type LinkedPrompt } from "@t3tools/shared/promptLinks";
import type { LinkedPrompt } from "@t3tools/shared/promptLinks";

import { tryCopyTextWithHaptic } from "../../lib/copyTextWithHaptic";
import {
getComposerDraftSnapshot,
setComposerDraftText,
Expand Down Expand Up @@ -35,9 +34,3 @@ export async function applyLinkedPromptToThreadDraft(
{ text: "Replace", onPress: () => setComposerDraftText(draftKey, prompt.text) },
]);
}

export async function copyPromptLink(prompt: string): Promise<void> {
if (!(await tryCopyTextWithHaptic(buildNewThreadLink({ prompt }), { target: "prompt link" }))) {
Dialog.alert("Could not copy", "Try again.");
}
}
26 changes: 3 additions & 23 deletions apps/mobile/src/features/threads/ThreadFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ import {
import type { ToolCallVisibility } from "@t3tools/contracts/settings";
import { renderAssistantCitationsAsText } from "@t3tools/shared/assistantCitations";
import { encodeComposerContextFragment } from "@t3tools/shared/composerContextClipboard";
import { hostedThreadLink, parseThreadLinkHref } from "@t3tools/shared/threadLinks";
import { PROMPT_LINK_MAX_PROMPT_LENGTH } from "@t3tools/shared/promptLinks";
import { formatThreadLink, parseThreadLinkHref } from "@t3tools/shared/threadLinks";
import {
parseComposerContextHref,
collectComposerContextReferences,
Expand Down Expand Up @@ -169,10 +168,8 @@ import { attachmentVideoPreviewSource } from "../../lib/videoPreviewSource";
import { CopyTextButton } from "./CopyTextButton";
import { getReviewSectionIdForCheckpoint } from "../review/reviewModel";
import { setReviewSelectedSectionId } from "../review/reviewState";
import { Menu } from "../../components/ui/Menu";
import { useAndroidControlSizing } from "../../components/ui/useAndroidControlSizing";
import { useBarInset, useContentTopInset } from "../../components/ui/useBarInset";
import { copyPromptLink } from "../prompt-links/prompt-link-actions";
import { parseReviewCommentMessageSegments } from "../review/reviewCommentSelection";
import {
ReviewCommentCard,
Expand Down Expand Up @@ -348,16 +345,12 @@ export interface ThreadFeedProps {
readonly onUseArtifactTemplate?: (template: CodexArtifactTemplate) => void;
}

const USER_MESSAGE_COPY_MENU_ACTIONS = [
{ id: "copy-prompt-link", title: "Copy prompt link", image: "link" },
];

function UserMessageCopyButton(props: {
readonly text: string;
readonly onCopy: (() => Promise<void>) | undefined;
readonly tintColor: ColorValue;
}) {
const button = (
return (
<CopyTextButton
accessibilityLabel="Copy message"
text={props.text}
Expand All @@ -367,19 +360,6 @@ function UserMessageCopyButton(props: {
iconSize={13}
/>
);
const linkText = replaceComposerContextReferences(props.text, (reference) => reference.label);
if (linkText.length > PROMPT_LINK_MAX_PROMPT_LENGTH) return button;
return (
<Menu
actions={USER_MESSAGE_COPY_MENU_ACTIONS}
onPressAction={({ nativeEvent }) => {
if (nativeEvent.event === "copy-prompt-link") void copyPromptLink(linkText);
}}
shouldOpenOnLongPress
>
{button}
</Menu>
);
}

function AssistantForkButton(props: {
Expand Down Expand Up @@ -3034,7 +3014,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
const thread = resolveLinkedThread(href);
if (thread) {
if (actionId === "open-thread") onMarkdownLinkPress(href);
else if (actionId === "copy-thread-link") copyTextWithHaptic(hostedThreadLink(thread));
else if (actionId === "copy-thread-link") copyTextWithHaptic(formatThreadLink(thread));
return;
}
const target = resolveFileChipTarget(href, props.workspaceRoot);
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/lib/appLinking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("shouldHandleAppLink", () => {
"t3code-personal://threads/env-1/thread-1",
"t3code-personal://pair?pairingUrl=x",
"t3code-personal-dev://settings/usage?tab=limits",
"https://app.t3.codes/threads/env/thread?prompt=Fix%20expo-development-client%20and%20t3code-personal://expo-sharing",
"t3code-personal://threads/env/thread?prompt=Fix%20expo-development-client%20and%20t3code-personal://expo-sharing",
])("handles path-bearing URL %s", (url) => {
expect(shouldHandleAppLink(url)).toBe(true);
});
Expand Down
Loading
Loading