Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2b4c0c6
feat(providers): add Devin CLI through ACP
Derpedyea Sep 11, 2026
edba53e
fix(devin): handle disconnects and preserve workspace and account bou…
Derpedyea Sep 11, 2026
56f0d97
fix(devin): preserve closed-session startup errors
Derpedyea Sep 11, 2026
dd1ebfe
test(devin): cover authentication environment overrides
Derpedyea Sep 11, 2026
abbbe89
feat(devin): add Fusion model pairing support
Derpedyea Sep 12, 2026
30de69d
chore: remove unrelated anti-slop tooling from Devin PR
Derpedyea Sep 12, 2026
d0766ae
test(devin): remove redundant coverage and reuse existing suites
Derpedyea Sep 12, 2026
d968c4e
test(devin): consolidate provider coverage around driver and adapter …
Derpedyea Sep 12, 2026
9205023
feat(devin): add Fusion model pairing support
Derpedyea Sep 13, 2026
91c662a
chore: merge main into Devin ACP support
Derpedyea Sep 13, 2026
97f0dbe
fix(devin): honor catalog identifiers and bound Fusion animation
Derpedyea Sep 13, 2026
d1525e2
fix(settings): preserve disabled auto-pull during migration
Derpedyea Sep 13, 2026
2b02734
fix(devin): deduplicate aliases and bound auth status matching
Derpedyea Sep 13, 2026
77e7aa6
Merge branch 'main' into t3code/add-devin-acp-support
Derpedyea Sep 13, 2026
763f2ac
Merge branch 'main' into t3code/add-devin-acp-support
Derpedyea Sep 13, 2026
36dc9c7
Merge branch 'main' into t3code/add-devin-acp-support
Derpedyea Sep 13, 2026
19b726d
fix(devin): use the official Cognition mark across clients
Derpedyea Sep 13, 2026
a950eb6
Merge branch 'main' into t3code/add-devin-acp-support
Derpedyea Sep 14, 2026
28e9869
fix(devin): serialize ACP lifecycle operations and refresh workspace ski
Derpedyea Sep 15, 2026
d90daf9
fix(web): preserve model picker focus around fusion selection
Derpedyea Sep 15, 2026
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

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

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

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

> [!WARNING]
> T3 Code currently supports Codex, Claude, Cursor, Grok Build, OpenCode, and Antigravity. Install and authenticate at least one provider before use:
> T3 Code currently supports Codex, Claude, Cursor, Grok Build, OpenCode, Devin CLI, and Antigravity. Install and authenticate at least one provider before use:
>
> - Codex: install [Codex CLI](https://developers.openai.com/codex/cli) and run `codex login`
> - Claude: install [Claude Code](https://claude.com/product/claude-code) and run `claude auth login`
> - Cursor: install [Cursor CLI](https://cursor.com/cli) and run `agent login`
> - Grok Build: install [Grok Build CLI](https://x.ai/cli) and run `grok login`
> - OpenCode: install [OpenCode](https://opencode.ai) and run `opencode auth login`
> - Devin: install [Devin CLI](https://docs.devin.ai/cli), run `devin auth login`, then enable it in Settings → Providers.
> - Antigravity: enable it in Settings, then use **Install Antigravity** and **Sign in with Google**. No CLI is required.

### Try it out (install-free)
Expand Down
11 changes: 11 additions & 0 deletions apps/mobile/src/components/ProviderIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ export function ProviderIcon(props: ProviderIconProps) {
);
}

// Cognition mark from https://cognition.com/icon.svg.
if (props.provider === "devin") {
return (
<Svg width={size} height={size} viewBox="0 0 20 20" fill={mono}>
<Path d="M13.2603 8.85657C13.6689 8.62171 14.174 8.62171 14.5826 8.85657L15.6386 9.46628C15.6723 9.48514 15.7089 9.5 15.746 9.51028C15.7534 9.51314 15.7609 9.51485 15.7694 9.516C15.806 9.52457 15.8431 9.52914 15.8797 9.53085H15.8854C15.89 9.53085 15.8929 9.53085 15.8974 9.52914C15.9311 9.52914 15.9649 9.52343 15.9986 9.516C16.0043 9.516 16.0106 9.51428 16.0163 9.51143C16.0517 9.50114 16.0866 9.48628 16.1191 9.46914C16.122 9.46743 16.1266 9.46628 16.1294 9.46457L18.2403 8.24514C18.3917 8.15828 18.4843 7.99714 18.4843 7.82228V5.384C18.4843 5.20914 18.3917 5.04742 18.2403 4.96114L16.1294 3.74171C15.978 3.65485 15.7917 3.65485 15.6403 3.74171L13.5294 4.96114C13.5294 4.96114 13.5237 4.96571 13.5209 4.96685C13.4889 4.98571 13.4574 5.00971 13.4311 5.036C13.4266 5.04057 13.4237 5.04457 13.4191 5.04914C13.3957 5.07428 13.3751 5.10228 13.3563 5.13142C13.3534 5.136 13.3506 5.14 13.3477 5.14628C13.33 5.17885 13.3151 5.21257 13.3049 5.24914C13.302 5.25657 13.3003 5.264 13.2991 5.27257C13.2906 5.30914 13.2843 5.34742 13.2843 5.38742V6.60685C13.2843 7.07714 13.0317 7.516 12.6231 7.75085C12.2146 7.98571 11.7094 7.98571 11.3009 7.75085L10.2449 7.14114C10.2111 7.12228 10.1746 7.10742 10.1374 7.09714C10.13 7.09428 10.1226 7.09257 10.114 7.09142C10.0774 7.08285 10.0403 7.07828 10.0037 7.07657H9.98771C9.95228 7.07657 9.91857 7.08228 9.88485 7.08971C9.87914 7.08971 9.87285 7.09142 9.86885 7.09257C9.83228 7.10285 9.79857 7.11771 9.76428 7.13542C9.76142 7.13714 9.75685 7.13828 9.75399 7.14L7.64314 8.35942C7.49171 8.44628 7.39914 8.60742 7.39914 8.78228V11.2206C7.39914 11.3954 7.49171 11.5571 7.64314 11.6434L9.75399 12.8629C9.75399 12.8629 9.76142 12.8657 9.76428 12.8674C9.79799 12.8851 9.83171 12.9 9.86885 12.9103C9.87457 12.912 9.88085 12.9131 9.88657 12.9149C9.92028 12.9223 9.95399 12.9269 9.98771 12.928C9.99228 12.928 9.99514 12.9297 9.99971 12.9297H10.0054C10.042 12.9297 10.0791 12.924 10.1157 12.9149C10.1231 12.9131 10.1306 12.9103 10.1391 12.9091C10.1757 12.8989 10.2111 12.884 10.2466 12.8651L11.3026 12.2554C11.7111 12.0206 12.2163 12.0206 12.6243 12.2554C13.0311 12.4903 13.2854 12.9297 13.2854 13.4V14.6194C13.2854 14.6589 13.2911 14.6971 13.3003 14.7337C13.302 14.7411 13.3031 14.7497 13.306 14.7571C13.3163 14.7926 13.3311 14.8274 13.3483 14.86C13.3511 14.8646 13.354 14.8686 13.3574 14.8749C13.3751 14.904 13.3957 14.932 13.4209 14.9571C13.4254 14.9617 13.4283 14.9657 13.4329 14.9703C13.4591 14.9966 13.49 15.0189 13.5226 15.0394C13.5254 15.0411 13.5283 15.044 13.5311 15.0451L15.642 16.2646C15.7169 16.3086 15.802 16.3309 15.886 16.3309C15.97 16.3309 16.0551 16.3086 16.13 16.2646L18.2409 15.0451C18.3923 14.9583 18.4849 14.7971 18.4849 14.6223V12.184C18.4849 12.0091 18.3923 11.8474 18.2409 11.7611L16.13 10.5417C16.13 10.5417 16.1226 10.5389 16.1197 10.5371C16.086 10.5194 16.0523 10.5046 16.0157 10.4943C16.01 10.4931 16.0054 10.4931 15.9997 10.4914C15.966 10.4829 15.9306 10.4783 15.8969 10.4783H15.8809C15.8443 10.4783 15.8077 10.484 15.7706 10.4931C15.7631 10.4949 15.7557 10.4977 15.7489 10.4989C15.7123 10.5091 15.6769 10.524 15.6414 10.5429L14.5854 11.1526C14.1786 11.3874 13.6717 11.3874 13.2637 11.1526C12.8569 10.9177 12.6026 10.4783 12.6026 10.008C12.6026 9.53771 12.8551 9.09885 13.2637 8.86343L13.262 8.85771L13.2603 8.85657Z" />
<Path d="M1.75971 8.24743L3.87057 9.46685C3.94542 9.51085 4.03057 9.53314 4.11457 9.53314C4.19857 9.53314 4.28371 9.51085 4.35857 9.46685L6.46942 8.24743C6.46942 8.24743 6.47514 8.24285 6.47799 8.24171C6.51057 8.22285 6.54085 8.19885 6.56771 8.17257C6.57228 8.168 6.57514 8.164 6.57971 8.15942C6.60314 8.13428 6.62371 8.10628 6.64314 8.07714C6.64599 8.07257 6.64885 8.06857 6.65171 8.06228C6.66942 8.02971 6.68428 7.996 6.69457 7.95942C6.69742 7.952 6.69914 7.94457 6.70028 7.936C6.70885 7.89943 6.71514 7.86114 6.71514 7.82171V6.60228C6.71514 6.132 6.96771 5.69314 7.37628 5.45828C7.78485 5.22342 8.28999 5.22342 8.69857 5.45828L9.75457 6.068C9.78828 6.08685 9.82485 6.10171 9.86199 6.112C9.86942 6.11485 9.87685 6.11657 9.88542 6.11771C9.92199 6.12628 9.95742 6.13085 9.99571 6.13257H10.0014C10.006 6.13257 10.0089 6.13085 10.0134 6.13085C10.0471 6.13085 10.0809 6.12514 10.1146 6.11771C10.1203 6.11771 10.1266 6.116 10.1323 6.11314C10.1689 6.10285 10.2026 6.088 10.2369 6.07028C10.2397 6.06914 10.2443 6.06742 10.2471 6.06571L12.358 4.84628C12.5094 4.75942 12.602 4.59828 12.602 4.42342V1.98514C12.602 1.81028 12.5094 1.64857 12.358 1.56228L10.2471 0.342853C10.0957 0.255996 9.90942 0.255996 9.75799 0.342853L7.64714 1.56228C7.64714 1.56228 7.64142 1.56685 7.63857 1.568C7.60657 1.58685 7.57571 1.61085 7.54885 1.63714C7.54428 1.64171 7.54142 1.64571 7.53685 1.65028C7.51342 1.67542 7.49285 1.70342 7.47342 1.73257C7.47057 1.73714 7.46771 1.74114 7.46485 1.74742C7.44714 1.78 7.43228 1.81371 7.42199 1.85028C7.41914 1.85771 7.41742 1.86514 7.41628 1.87371C7.40714 1.91028 7.40142 1.94857 7.40142 1.98857V3.208C7.40142 3.67828 7.14885 4.11714 6.74028 4.35257C6.33342 4.58742 5.82657 4.58742 5.41799 4.35257L4.36199 3.74285C4.32828 3.724 4.29171 3.70914 4.25457 3.69885C4.24714 3.696 4.23971 3.69428 4.23114 3.69314C4.19457 3.68457 4.15742 3.68 4.12085 3.67828H4.10485C4.06942 3.67828 4.03571 3.684 4.00199 3.69142C3.99628 3.69142 3.98999 3.69314 3.98599 3.69428C3.94942 3.70457 3.91571 3.71942 3.88142 3.73714C3.87857 3.73885 3.87399 3.74 3.87114 3.74171L1.76028 4.96114C1.60885 5.048 1.51628 5.20914 1.51628 5.384V7.82228C1.51628 7.99714 1.60885 8.15885 1.76028 8.24514V8.248L1.75971 8.24743Z" />
<Path d="M12.3551 15.1514L10.2443 13.932C10.2443 13.932 10.2369 13.9291 10.234 13.9274C10.2003 13.9097 10.1666 13.8954 10.1294 13.8851C10.1237 13.8834 10.1174 13.8823 10.1117 13.8806C10.078 13.8731 10.0443 13.8674 10.0089 13.8674H9.99285C9.95628 13.8674 9.91914 13.8731 9.88257 13.8823C9.87514 13.884 9.86771 13.8869 9.86028 13.888C9.82371 13.8983 9.78828 13.9131 9.75285 13.932L8.69685 14.5417C8.28999 14.7766 7.78314 14.7766 7.37628 14.5417C6.96771 14.3069 6.71514 13.8674 6.71514 13.3971V12.1777C6.71514 12.1383 6.70942 12.1 6.70028 12.0629C6.69914 12.0554 6.69742 12.0469 6.69457 12.0394C6.68428 12.004 6.66942 11.9691 6.65171 11.9366C6.64885 11.932 6.64599 11.928 6.64314 11.9217C6.62542 11.8926 6.60485 11.8646 6.57971 11.8394C6.57514 11.8349 6.57228 11.8309 6.56828 11.8263C6.54199 11.8 6.51114 11.7777 6.47857 11.7571C6.47571 11.7554 6.47285 11.7526 6.46999 11.7514L4.35914 10.532C4.20771 10.4451 4.02142 10.4451 3.86999 10.532L1.75914 11.7514C1.60771 11.8383 1.51514 11.9994 1.51514 12.1743V14.6126C1.51514 14.7874 1.60771 14.9491 1.75914 15.0354L3.86999 16.2549C3.86999 16.2549 3.87742 16.2577 3.88028 16.2594C3.91399 16.2771 3.94771 16.2914 3.98314 16.3017C3.98885 16.3034 3.99514 16.3046 4.00085 16.3063C4.03457 16.3137 4.06714 16.3183 4.10199 16.3194C4.10657 16.3194 4.11057 16.3206 4.11399 16.3206H4.11971C4.15628 16.3206 4.19342 16.3149 4.22828 16.3057C4.23571 16.304 4.24428 16.3011 4.25171 16.3C4.28828 16.2897 4.32371 16.2749 4.35914 16.256L5.41514 15.6463C5.82371 15.4114 6.32885 15.4114 6.73742 15.6463C7.14428 15.8811 7.39857 16.3206 7.39857 16.7909V18.0103C7.39857 18.0497 7.40428 18.088 7.41342 18.1251C7.41514 18.1326 7.41628 18.1411 7.41914 18.1486C7.42942 18.184 7.44428 18.2189 7.46199 18.2514C7.46485 18.256 7.46771 18.26 7.47057 18.2663C7.48828 18.2954 7.50885 18.3234 7.53399 18.3486C7.53857 18.3531 7.54142 18.3571 7.54542 18.3617C7.57171 18.388 7.60257 18.4103 7.63514 18.4309C7.63799 18.4326 7.64085 18.4354 7.64428 18.4366L9.75514 19.656C9.82999 19.7 9.91514 19.7223 9.99914 19.7223C10.0831 19.7223 10.1683 19.7 10.2431 19.656L12.354 18.4366C12.5054 18.3497 12.598 18.1886 12.598 18.0137V15.5754C12.598 15.4006 12.5054 15.2389 12.354 15.1526L12.3551 15.1514Z" />
</Svg>
);
}

if (props.provider === "claudeAgent") {
return (
<Svg width={size} height={size} viewBox="0 0 256 257" fill="none">
Expand Down
114 changes: 114 additions & 0 deletions apps/mobile/src/features/threads/FusionModelEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useState } from "react";
import { Pressable, ScrollView, View } from "react-native";
import { AppText as Text } from "../../components/AppText";
import { SymbolView } from "../../components/AppSymbol";
import type { ModelOption } from "../../lib/modelOptions";
import { fusionLeadPairing } from "./fusion-model-options";

export function FusionModelEditor(props: {
readonly models: ReadonlyArray<ModelOption>;
readonly initialKey: string;
readonly onSelect: (model: ModelOption) => void;
}) {
const [selectedKey, setSelectedKey] = useState(props.initialKey);
const [editing, setEditing] = useState<"lead" | "sidekick" | null>(null);
const available = props.models.filter((model) => model.fusion && !model.isUnavailable);
const selected = available.find((model) => model.key === selectedKey);
if (!selected?.fusion) {
return (
<Text className="p-4 text-foreground-muted">
This pairing is no longer available. Go back to choose another model.
</Text>
);
}
const pairing = selected.fusion;
const choices = editing
? [
...new Map(
available
.filter((model) => editing === "lead" || model.fusion?.lead.id === pairing.lead.id)
.map((model) => [model.fusion?.[editing].id, model]),
).values(),
]
: [];

return (
<ScrollView
contentInsetAdjustmentBehavior="automatic"
contentContainerClassName="gap-6 px-4 py-6"
>
<View className="gap-2">
<Text className="text-base text-foreground-muted">{selected.providerLabel}</Text>
<Text className="text-sm text-foreground-muted">
Choose a lead model and a sidekick to work together.
</Text>
</View>
<View className="gap-4">
{(["lead", "sidekick"] as const).map((role) => (
<View key={role} className="gap-2">
<Text className="text-sm font-t3-medium text-foreground-muted">
{role === "lead" ? "Lead" : "Sidekick"}
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel={`${role === "lead" ? "Lead" : "Sidekick"}: ${pairing[role].name}`}
accessibilityState={{ expanded: editing === role }}
onPress={() => setEditing(editing === role ? null : role)}
className="min-h-14 flex-row items-center gap-3 rounded-2xl bg-card px-4 py-3 active:bg-subtle"
>
<View
className={
role === "lead"
? "size-2 rounded-full bg-blue-400"
: "size-2 rounded-full bg-emerald-400"
}
/>
<Text className="flex-1 text-base font-t3-medium text-foreground">
{pairing[role].name}
</Text>
<SymbolView
name={editing === role ? "chevron.up" : "chevron.down"}
size={14}
tintColorClassName="accent-icon"
/>
</Pressable>
{editing === role ? (
<View className="overflow-hidden rounded-2xl bg-card">
{choices.map((model) => (
<Pressable
key={model.key}
accessibilityRole="radio"
accessibilityState={{ checked: model.fusion?.[role].id === pairing[role].id }}
onPress={() => {
const next =
role === "lead"
? fusionLeadPairing(available, selected, model.fusion?.lead.id ?? "")
: model;
if (next) setSelectedKey(next.key);
setEditing(null);
}}
className="min-h-12 flex-row items-center gap-3 px-4 py-3 active:bg-subtle"
>
<Text className="flex-1 text-base text-foreground">
{model.fusion?.[role].name}
</Text>
{model.fusion?.[role].id === pairing[role].id ? (
<SymbolView name="checkmark" size={16} tintColorClassName="accent-icon" />
) : null}
</Pressable>
))}
</View>
) : null}
</View>
))}
</View>
<Pressable
accessibilityRole="button"
onPress={() => props.onSelect(selected)}
className="min-h-12 items-center justify-center rounded-2xl bg-primary px-4 py-3 active:opacity-70"
>
<Text className="text-base font-t3-medium text-primary-foreground">Use Fusion</Text>
</Pressable>
</ScrollView>
);
}
16 changes: 7 additions & 9 deletions apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ import {
} from "../../state/use-composer-drafts";
import { useEnvironmentServerConfig, useProjects } from "../../state/entities";
import {
isModelSelectionUnavailable,
getModelSelectionUnavailableReason,
resolveSelectableModelSelection,
} from "../../lib/modelOptions";
import { deriveThreadTitleFromPrompt } from "../../lib/projectThreadStartTurn";
Expand Down Expand Up @@ -1127,14 +1127,12 @@ export function NewTaskDraftScreen(props: {
) {
return;
}
if (
environmentConnected &&
isModelSelectionUnavailable(selectedEnvironmentServerConfig, modelSelection)
) {
Alert.alert(
"Antigravity model unavailable",
"Set up Antigravity on web or desktop, or choose another model.",
);
const modelUnavailableReason = getModelSelectionUnavailableReason(
selectedEnvironmentServerConfig,
modelSelection,
);
if (environmentConnected && modelUnavailableReason !== null) {
Alert.alert("Model unavailable", modelUnavailableReason);
return;
}
// T3's own limits command is answered by the thread composer; a new task would
Expand Down
7 changes: 6 additions & 1 deletion apps/mobile/src/features/threads/ThreadComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -548,10 +548,15 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
const providerOptionDescriptors = useMemo(
() =>
resolveProviderOptionDescriptors({
modelPolicy: currentModelOption?.modelPolicy,
capabilities: currentModelOption?.capabilities,
selections: currentModelSelection.options,
}),
[currentModelOption?.capabilities, currentModelSelection.options],
[
currentModelOption?.modelPolicy,
currentModelOption?.capabilities,
currentModelSelection.options,
],
);
const settingsOwnerId = composerOwnerKey;
const settingsRouteSession = useMemo<ExistingThreadSettingsRouteSession>(
Expand Down
Loading
Loading